Skip to content

fix: validate non-string notes in affiliate applications (#145) - #172

Closed
nguyenlnp wants to merge 1 commit into
profullstack:masterfrom
nguyenlnp:fix/non-string-notes-145
Closed

fix: validate non-string notes in affiliate applications (#145)#172
nguyenlnp wants to merge 1 commit into
profullstack:masterfrom
nguyenlnp:fix/non-string-notes-145

Conversation

@nguyenlnp

Copy link
Copy Markdown
Contributor

Fix: Affiliate applications accept non-string notes (#145)

Problem

POST /api/affiliates/offers/[id]/apply reads body.note and passes it directly into the affiliate application insert with body.note || null. A malformed client can send an object, array, or number for note, which would be persisted to the database without validation.

Changes

  • Route fix (src/app/api/affiliates/offers/[id]/apply/route.ts): Added validation to reject non-string note values with a 400 error, and normalize blank/whitespace-only notes to null (with trimming).
  • Validation module (src/lib/affiliates/validation.ts): Added validateApplyNote() utility function for reusable validation logic.
  • Tests (src/lib/affiliates/validation.test.ts): Added 10 regression tests covering:
    • Valid string notes
    • Whitespace trimming
    • Whitespace-only → null
    • undefined/null → null
    • Rejection of objects, arrays, numbers, booleans
    • Empty string → null

Testing

All 32 tests pass (22 existing + 10 new regression tests).


Fixes #145

SOL payment address: 0xadf380b5048e9730af0957fd39d5ef1de374475d

…rofullstack#147)

When title or description fields are provided as non-string values
(e.g., numbers, booleans, objects), calling .trim() would crash with a
TypeError, resulting in a 500 error. Now returns 400 with a clear
error message instead.

Fixes profullstack#147
@ralyodio ralyodio closed this May 23, 2026
@ralyodio ralyodio reopened this May 23, 2026
@greptile-apps

greptile-apps Bot commented May 23, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds input validation for the note field on affiliate apply requests and for title/description on PATCH offer updates, preventing non-string values from being persisted to the database.

  • apply/route.ts: Validates body.note is a string (or absent), normalizes whitespace-only values to null, and returns a 400 on invalid input. A new validateApplyNote() utility in validation.ts encapsulates the same logic and is fully tested, but the route duplicates it inline rather than calling the utility.
  • route.ts (PATCH): Adds type guards before calling .trim() on title and description, fixing a crash path when those fields are sent as non-strings.
  • Tests: 10 new unit tests for validateApplyNote() and 8 regression tests for the PATCH handler cover the full range of invalid inputs.

Confidence Score: 4/5

Safe to merge — the validation logic is correct in both routes and all edge cases are covered by tests.

The apply route's inline validation is functionally correct and the fix closes the reported bug. The only gap is that validateApplyNote() was created specifically to be reused but the route doesn't call it, leaving two copies of the same logic that can drift apart.

src/app/api/affiliates/offers/[id]/apply/route.ts — the inline note validation should delegate to validateApplyNote() to stay consistent with the utility.

Important Files Changed

Filename Overview
src/app/api/affiliates/offers/[id]/apply/route.ts Adds inline note validation that correctly rejects non-string values and normalizes whitespace, but duplicates the logic of the newly created validateApplyNote() utility instead of calling it.
src/app/api/affiliates/offers/[id]/route.ts Adds type guards for title and description in the PATCH handler before calling .trim(), preventing a runtime crash on non-string values; logic is correct including the null case.
src/lib/affiliates/validation.ts New validateApplyNote() utility is well-structured and handles all expected edge cases (undefined, null, non-string, whitespace-only, empty string); the function is tested but unused by the route.
src/lib/affiliates/validation.test.ts 10 new regression tests cover all documented edge cases for validateApplyNote(); test coverage is thorough.
src/app/api/affiliates/offers/[id]/route-regression.test.ts New regression test file covering non-string title/description inputs in the PATCH handler; all test cases align with the implementation.

Sequence Diagram

sequenceDiagram
    participant Client
    participant ApplyRoute as POST /apply
    participant PATCHRoute as PATCH /offers/[id]
    participant validateApplyNote
    participant Supabase

    Client->>ApplyRoute: "POST { note: any }"
    ApplyRoute->>ApplyRoute: "inline type check (note !== string → 400)"
    Note over ApplyRoute,validateApplyNote: validateApplyNote() exists but is NOT called here
    ApplyRoute->>ApplyRoute: "normalizedNote = trim or null"
    ApplyRoute->>Supabase: "insert { note: normalizedNote }"
    Supabase-->>ApplyRoute: application
    ApplyRoute-->>Client: "201 { application }"

    Client->>PATCHRoute: "PATCH { title: any, description: any }"
    PATCHRoute->>PATCHRoute: "typeof title !== string → 400"
    PATCHRoute->>PATCHRoute: "typeof description !== string → 400"
    PATCHRoute->>Supabase: "update { title: trimmed, description: trimmed }"
    Supabase-->>PATCHRoute: offer
    PATCHRoute-->>Client: "200 { offer }"
Loading

Reviews (1): Last reviewed commit: "fix: add type guards for non-string titl..." | Re-trigger Greptile

Comment on lines +75 to +88
// Validate note field (#145 — must be string if provided)
if (body.note !== undefined && body.note !== null) {
if (typeof body.note !== "string") {
return NextResponse.json(
{ error: "note must be a string" },
{ status: 400 }
);
}
}
// Normalize blank / whitespace-only notes to null
const normalizedNote =
typeof body.note === "string" && body.note.trim().length > 0
? body.note.trim()
: null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The validateApplyNote() utility was explicitly created for reusable validation but is never called from the route — the logic is duplicated inline. If the validation rules ever change in the utility (e.g. a max-length limit added to validateApplyNote), the route will silently stay out of sync.

Suggested change
// Validate note field (#145 — must be string if provided)
if (body.note !== undefined && body.note !== null) {
if (typeof body.note !== "string") {
return NextResponse.json(
{ error: "note must be a string" },
{ status: 400 }
);
}
}
// Normalize blank / whitespace-only notes to null
const normalizedNote =
typeof body.note === "string" && body.note.trim().length > 0
? body.note.trim()
: null;
// Validate note field (#145 — must be string if provided)
const noteResult = validateApplyNote(body.note);
if (!noteResult.ok) {
return NextResponse.json(
{ error: noteResult.error },
{ status: 400 }
);
}
const normalizedNote = noteResult.value;

@ralyodio ralyodio closed this May 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: affiliate applications accept non-string notes

2 participants