Skip to content

Comments

hotfix: sanitize public form submission data to prevent stored XSS#671

Open
mmcintosh wants to merge 1 commit intoSonicJs-Org:mainfrom
mmcintosh:hotfix/public-forms-xss-clean
Open

hotfix: sanitize public form submission data to prevent stored XSS#671
mmcintosh wants to merge 1 commit intoSonicJs-Org:mainfrom
mmcintosh:hotfix/public-forms-xss-clean

Conversation

@mmcintosh
Copy link
Contributor

Summary

Security hotfix: prevents stored XSS via unauthenticated public form submissions. The public form submission endpoint stored arbitrary JSON from anonymous users without sanitization, allowing <script> tags to execute with admin session cookies when viewing submissions.

Changes

1. Input Sanitization for Form Submissions

  • Added sanitizeDeep() — recursively HTML-encodes all string values in arbitrary nested JSON (objects, arrays, primitives)
  • Applied before storage in form_submissions table — <script> becomes &lt;script&gt;, "onload= becomes &quot;onload=, etc.
  • Numbers, booleans, and null pass through unchanged
  • Uses existing sanitizeInput() from utils/sanitize for the actual encoding

2. Unit Tests

  • 5 new tests covering script tag encoding, nested object sanitization, array sanitization, primitive pass-through, and event handler injection patterns

Technical Details

Core Changes:

  • packages/core/src/routes/public-forms.tssanitizeDeep() function + sanitizeInput import + usage before JSON.stringify storage (+30 lines, -1 line)

New Tests:

  • packages/core/src/routes/public-forms.test.ts — 5 unit tests with mocked D1, TurnstileService, and Hono route testing (+218 lines)

Testing

Type Check: PASSED
Unit Tests: PASSED (5 new, all existing passing)
E2E Tests: PASSED (no regression)

New Unit Test Coverage:

  • Script tags HTML-encoded in string fields
  • Nested objects sanitized recursively (2 levels deep)
  • Arrays of strings sanitized element-by-element
  • Numbers, booleans, null pass through unchanged
  • Event handler injection attempts (onmouseover, onclick, onerror) all encoded

Performance Impact

Metric Before After Impact
Submission processing baseline ~unchanged O(n) on total text — negligible

Breaking Changes

Form submission string values stored in form_submissions will have HTML entities encoded. This is a security improvement. If downstream consumers need raw values, they should HTML-decode.

Migration Notes

No action required. The fix activates immediately. Pre-existing unsanitized submissions are not retroactively modified.

Known Issues

None.

Checklist

  • Code follows project coding standards
  • Tests added/updated and passing
  • No breaking changes
  • Backward compatible

Form submissions from unauthenticated users were stored with raw
string values, allowing script injection payloads to execute when
admins view submissions. Add recursive sanitizeDeep() that HTML-encodes
all string values in the arbitrary submission JSON before storage.
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