Skip to content

Harden Aurora sync against malicious data#739

Open
marcodejongh wants to merge 1 commit intomainfrom
harden-aurora-sync-validation
Open

Harden Aurora sync against malicious data#739
marcodejongh wants to merge 1 commit intomainfrom
harden-aurora-sync-validation

Conversation

@marcodejongh
Copy link
Owner

Summary

  • P0 Fix: draft_climbs can no longer overwrite published climbs. Both user-sync files now gate all onConflictDoUpdate fields on isDraft = true using SQL CASE expressions. Published climbs are fully immutable via sync.
  • P0 Fix: Corrected backwards isDraft CASE in shared-sync.ts — only allows true → false (publishing), never false → true (unpublishing).
  • P1 Fix: climb_stats numeric fields validated with sanitizeNumber() and clamped to bounds (difficulty 0-50, quality 0-5, ascensionistCount 0-10M). Invalid incoming values preserve existing DB values on conflict. History inserts skipped when all values invalid.
  • P1 Fix: Sync timestamps validated (reject unparseable/pre-2016, clamp future to now). table_name validated against known allowlists in both updateSharedSyncs and updateUserSyncs.
  • P2 Fix: Per-table record cap of 10,000 prevents memory exhaustion from oversized responses.
  • P2 Fix: Beta link URLs validated as http/https; invalid thumbnails nulled out (record kept).
  • P2 Fix: String fields truncated to reasonable limits (name: 500, description: 10K, comment: 5K, frames: 500K, URLs: 2048) across all sync paths.

New sync-validation.ts utility files created in both packages (duplicated to match existing pattern; consolidation is a follow-up).

Test plan

  • npm run typecheck passes (verified)
  • npm run lint passes with 0 errors (verified)
  • Review SQL CASE expressions in draft_climbs match the proven pattern from shared-sync.ts upsertClimbs
  • Verify shared sync still works end-to-end for Kilter/Tension boards
  • Verify user sync (drafts, ascents, circuits) still works end-to-end

🤖 Generated with Claude Code

Treat Aurora API responses as untrusted input with runtime validation:

- Fix P0: draft_climbs can no longer overwrite published climbs. All fields
  in onConflictDoUpdate are gated on isDraft=true via SQL CASE expressions.
- Fix P0: isDraft transition corrected to only allow true->false (publishing),
  never false->true (unpublishing). Previous code had this backwards.
- Fix P1: climb_stats numeric fields validated with sanitizeNumber() and
  clamped to reasonable bounds. Invalid values preserve existing DB values.
- Fix P1: Sync timestamps validated - rejects unparseable/pre-2016, clamps
  future timestamps. table_name validated against known allowlists.
- Fix P2: Per-table record cap of 10,000 prevents memory exhaustion.
- Fix P2: Beta link URLs validated as http/https, invalid thumbnails nulled.
- Fix P2: String fields truncated to reasonable limits across all sync paths.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Feb 8, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
boardsesh Building Building Preview, Comment Feb 8, 2026 8:38am

Request Review

@claude
Copy link

claude bot commented Feb 8, 2026

Claude Review

Ready to merge - Minor issues noted below, but nothing blocking.

Issues

  1. Code duplication: Two nearly identical sync-validation.ts files (packages/aurora-sync/src/sync/sync-validation.ts and packages/web/app/lib/data-sync/aurora/sync-validation.ts). While acknowledged in comments as intentional, this creates maintenance burden. Consider extracting to packages/shared-schema/ in the follow-up.

  2. Missing tests: No tests added for the new validation utilities (sanitizeNumber, validateSyncTimestamp, isValidHttpUrl, truncate, capRecords). These are security-critical functions with multiple edge cases (boundary values, invalid inputs, timestamp edge cases) that should have unit tests.

  3. Potential log spam (packages/aurora-sync/src/sync/sync-validation.ts:69-84, packages/web/app/lib/data-sync/aurora/sync-validation.ts:66-81): console.warn is called for every rejected/clamped value. With malicious data, this could flood logs. Consider rate-limiting or aggregating warnings.

  4. safeInt returns NaN for NaN input (packages/aurora-sync/src/sync/user-sync.ts:190-192): The function uses Number.isNaN(num) which returns false for Infinity - these values would pass through. Should use Number.isFinite() for consistency with sanitizeNumber.

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.

1 participant