Skip to content

Latest commit

 

History

History
127 lines (111 loc) · 6.4 KB

File metadata and controls

127 lines (111 loc) · 6.4 KB

GraphQL Create/Update Input Validation Audit

Scope

  • Stack: api/ (NestJS GraphQL)
  • Target: all GraphQL mutations named create* and update*
  • Goal: systematic, coherent, maintainable input format validation

What Was Implemented

  • Central regex base added in api/src/common/REGEX.ts
  • Reusable scalar family added in api/src/graphql/common/scalars/input-value.scalars.ts
  • Existing VideoUrl scalar aligned to shared regex source in api/src/graphql/common/scalars/video-url.scalar.ts
  • Scalars applied across create/update inputs (including nested input objects used by these mutations)
  • Password argument validation applied on non-input mutations in api/src/graphql/user/user.resolver.ts

Inventory Summary

  • Resolvers scanned: all *.resolver.ts under api/src/graphql
  • Create/update mutations found: 57
  • Input classes covered by those mutations: 55
  • Input fields inventoried: 384 total fields (102 unique names)

Top repeated field names in create/update inputs:

  • label (30)
  • locale (28)
  • visibility (26)
  • id (24)
  • description (14)

Scalar Families

  • LocaleCodeScalar: locale values (ex: fr, en, fr-FR)
  • EmailAddressScalar: email format
  • PhoneNumberScalar: phone format
  • PersonNameScalar: person/city/country-like names
  • ShortLabelScalar: short labels/titles/subjects
  • SafeTextScalar: free text blocks (notes, descriptions, comments)
  • DateYMDScalar: strict YYYY-MM-DD string format
  • TokenCodeScalar: technical code/token-like values
  • PasswordTextScalar: password text format gate
  • EntityIdScalar: technical ID strings (ObjectId/UUID/id-like)
  • VideoUrlScalar: absolute HTTP(S) video URL

Mutation Coverage Matrix

Athlete

  • createManualAgendaEvent (CreateManualAgendaEventInput)
    • sourceParentId: EntityIdScalar, sourceId: EntityIdScalar, date: DateYMDScalar
  • athleteInfo_create|athleteInfo_update (Create/UpdateAthleteInfoInput)
    • medicalConditions/allergies/notes: SafeTextScalar
  • coachAthlete_create|coachAthlete_update (Create/UpdateCoachAthleteInput)
    • note: SafeTextScalar
  • wellnessReport_create|wellnessReport_update (Create/UpdateWellnessReportInput)
    • reportDate: DateYMDScalar, notes: SafeTextScalar, painZones: [ShortLabelScalar]

Document

  • document_admin_create|document_admin_update (Create/UpdateDocumentInput)
    • name: ShortLabelScalar, version: TokenCodeScalar, file_name: TokenCodeScalar, locale: LocaleCodeScalar

Note / Task / Support

  • note_create|note_update (Create/UpdateNoteInput)
    • label: ShortLabelScalar, description: SafeTextScalar
  • task_create|task_update (Create/UpdateTaskInput)
    • label: ShortLabelScalar
  • supportMessage_create|supportMessage_update (Create/UpdateSupportMessageInput)
    • subject: ShortLabelScalar, description: SafeTextScalar

Nutrition

  • meal_create|meal_update (Create/UpdateMealInput)
    • locale: LocaleCodeScalar, label: ShortLabelScalar, foods: SafeTextScalar, videoUrl: VideoUrlScalar
  • mealDay_create|mealDay_update (Create/UpdateMealDayInput)
    • locale: LocaleCodeScalar, label: ShortLabelScalar, description: SafeTextScalar
  • mealPlan_create|mealPlan_update (Create/UpdateMealPlanInput + nested)
    • Root: locale: LocaleCodeScalar, label: ShortLabelScalar, description: SafeTextScalar
    • Nested day: locale, label, description, eventDates: [DateYMDScalar]
    • Nested meal: locale, label, description, foods, videoUrl: VideoUrlScalar
  • mealReport_create|mealReport_updateState (Create/UpdateMealReportInput)
    • eventDate: DateYMDScalar, comment: SafeTextScalar
  • mealType_create|mealType_update (Create/UpdateMealTypeInput)
    • locale: LocaleCodeScalar, label: ShortLabelScalar, icon: ShortLabelScalar

Prospect

  • Taxonomy create/update (prospectActivityPreference, prospectLevel, prospectObjective, prospectSource)
    • locale: LocaleCodeScalar, label: ShortLabelScalar
  • prospect_create|prospect_update (Create/UpdateProspectInput)
    • firstName/lastName: PersonNameScalar
    • email: EmailAddressScalar, phone: PhoneNumberScalar
    • levelId/sourceId: EntityIdScalar
    • medicalConditions/allergies/notes/dealDescription: SafeTextScalar

Sport

  • Taxonomy create/update (category, equipment, muscle, tag)
    • locale: LocaleCodeScalar, label: ShortLabelScalar
  • exercise_create|exercise_update (Create/UpdateExerciseInput)
    • locale: LocaleCodeScalar, label/series/repetitions/charge: ShortLabelScalar
    • description/instructions: SafeTextScalar, videoUrl: VideoUrlScalar
  • session_create|session_update (Create/UpdateSessionInput)
    • locale: LocaleCodeScalar, label: ShortLabelScalar, description: SafeTextScalar
  • program_create|program_update (Create/UpdateProgramInput + nested)
    • Root: locale: LocaleCodeScalar, label: ShortLabelScalar, description: SafeTextScalar
    • Nested session/exercise fields aligned to label/text/date/url families
  • sessionReport_create|sessionReport_updateState (Create/UpdateSessionReportInput + nested)
    • sessionId: EntityIdScalar, eventDate: DateYMDScalar, comment/notes: SafeTextScalar

User

  • createIncomeUser (CreateIncomeUserInput)
    • first_name/last_name: PersonNameScalar, email: EmailAddressScalar, phone: PhoneNumberScalar
    • password/confirm_password: PasswordTextScalar, captchaToken: TokenCodeScalar
  • user_create (CreateUserInput)
    • same identity fields + initialSubscriptionPlanCode: TokenCodeScalar
  • user_update / me_update (UpdateUserInput / UpdateMeInput)
    • first_name/last_name/email/phone scalars aligned
  • user_update_password / me_update_password (resolver args)
    • password: PasswordTextScalar

Ambiguities and Decisions

  • ID fields (@Field(() => ID)) were intentionally kept as GraphQL ID for contract compatibility.
  • EntityIdScalar is used for technical string IDs only where field type is string (not GraphQL ID).
  • Regexes are format validators, not business-rule validators.
  • Regex/scalar validation is not an injection defense by itself.

Validation Layers Clarified

  • Format validation: GraphQL scalars (this work)
  • Business validation: use cases/services (existing architecture)
  • Injection/security controls: repository/query hygiene, parameterization, and backend safeguards

Verification Executed

  • npm test passed (204 suites, 903 tests)
  • npm run lint passed
  • npm run build passed