From e145e121af32d469735b88083f08b10b20b79a08 Mon Sep 17 00:00:00 2001 From: PineND Date: Wed, 30 Oct 2024 17:30:47 -0700 Subject: [PATCH] added input security --- apps/backend/src/modules/rating/controller.ts | 17 ++++++++- apps/backend/src/modules/rating/formatter.ts | 35 +++++++++++-------- .../src/modules/rating/typedefs/rating.ts | 14 ++++++-- 3 files changed, 48 insertions(+), 18 deletions(-) diff --git a/apps/backend/src/modules/rating/controller.ts b/apps/backend/src/modules/rating/controller.ts index 8b82660ba..7e5c184d9 100644 --- a/apps/backend/src/modules/rating/controller.ts +++ b/apps/backend/src/modules/rating/controller.ts @@ -53,16 +53,31 @@ export const createRating = async ( ) => { if (!context.user._id) throw new Error("Unauthorized"); + const numberScaleMetrics = ['Usefulness', 'Difficulty', 'Workload'] as const; + const booleanScaleMetrics = ['Attendance', 'Recording'] as const; + + if (numberScaleMetrics.includes(ratingIdentifier.metricName as typeof numberScaleMetrics[number])) { + if (value < 1 || value > 5 || !Number.isInteger(value)) { + throw new Error(`${ratingIdentifier.metricName} rating must be an integer between 1 and 5`); + } + } else if (booleanScaleMetrics.includes(ratingIdentifier.metricName as typeof booleanScaleMetrics[number])) { + if (value !== 0 && value !== 1) { + throw new Error(`${ratingIdentifier.metricName} rating must be either 0 or 1`); + } + } + // potentially enforece value at mongodb level? - models file + const existingRating = await checkRatingExists(context, ratingIdentifier); if (existingRating) { existingRating.value = value; return existingRating.save(); } + // potential vunerability with rating identifier? const newRating = new RatingModel({ ...ratingIdentifier, createdBy: context.user._id, - value + value: value }); return newRating.save(); }; diff --git a/apps/backend/src/modules/rating/formatter.ts b/apps/backend/src/modules/rating/formatter.ts index 9fd1a9a97..fe6ed64a1 100644 --- a/apps/backend/src/modules/rating/formatter.ts +++ b/apps/backend/src/modules/rating/formatter.ts @@ -4,7 +4,8 @@ import { AggregatedRatings, Metric, Category, - Semester + Semester, + MetricName } from '../../generated-types/graphql'; export const formatUserRating = (rating: RatingType): Rating => ({ @@ -16,7 +17,7 @@ export const formatUserRating = (rating: RatingType): Rating => ({ createdBy: rating.google_id, - metricName: rating.metricName, + metricName: rating.metricName as MetricName, value: rating.value }); @@ -37,24 +38,30 @@ const formatCategories = (metric: any): Category[] => { } const formatMetrics = (ratings: any): Metric[] => { - - // TODO: add descriptor logic - const descriptor = 'test'; - const metrics: { [key: string]: any[] } = {}; + const metrics: Record = { + Attendance: [], + Difficulty: [], + Recording: [], + Usefulness: [], + Workload: [] + }; + for (const rating of ratings) { - if (metrics[rating.metricName]) { - metrics[rating.metricName] += rating; + const metricName = rating.metricName as MetricName; + if (metrics[metricName]) { + metrics[metricName].push(rating); } else { - metrics[rating.metricName] = [rating]; + metrics[metricName] = [rating]; } } - return Object.keys(metrics).map(key => ({ - metricName: key, + + return Object.entries(metrics).map(([key, ratings]) => ({ + metricName: key as MetricName, descriptor: descriptor, - count: metrics[key].length, - mean: metrics[key].reduce((acc, curr) => acc + curr.value, 0) / metrics[key].length, - categories: formatCategories(metrics[key]) + count: ratings.length, + mean: ratings.reduce((acc, curr) => acc + curr.value, 0) / ratings.length, + categories: formatCategories(ratings) })); } diff --git a/apps/backend/src/modules/rating/typedefs/rating.ts b/apps/backend/src/modules/rating/typedefs/rating.ts index ec8847e38..e91ce9aaf 100644 --- a/apps/backend/src/modules/rating/typedefs/rating.ts +++ b/apps/backend/src/modules/rating/typedefs/rating.ts @@ -1,6 +1,14 @@ import { gql } from "graphql-tag"; const typedef = gql` +enum MetricName { + Usefulness + Difficulty + Workload + Attendance + Recording +} + type AggregatedRatings { "Class identifer" subject: String! @@ -12,7 +20,7 @@ type AggregatedRatings { metrics: [Metric!]! } type Metric { - metricName: String! + metricName: MetricName! descriptor: String! count: Int! mean: Int! @@ -32,7 +40,7 @@ type Rating { createdBy: String! - metricName: String! + metricName: MetricName! value: Int! } type ClassIdentifier { @@ -53,7 +61,7 @@ type RatingIdentifier { semester: Semester! year: Int! class: String! - metricName: String! + metricName: MetricName! } type Mutation { createRating(rating: RatingIdentifier!, value: Int!): AggregatedRatings! @auth