Skip to content

security: harden CSP, headers, SSRF validation, and config#9

Merged
menottim merged 1 commit intomainfrom
security/fix-headers-csp
Feb 27, 2026
Merged

security: harden CSP, headers, SSRF validation, and config#9
menottim merged 1 commit intomainfrom
security/fix-headers-csp

Conversation

@menottim
Copy link
Copy Markdown
Owner

Summary

6 security fixes for headers, CSP, SSRF, and configuration:

  1. Add object-src 'none' to CSP (Medium) — Blocks <object>/<embed> plugin-based XSS vectors
  2. Add Permissions-Policy header (Medium) — Explicitly denies camera, microphone, geolocation, payment APIs
  3. Remove deprecated X-XSS-Protection header (Low) — Deprecated in all modern browsers, can cause issues. Nonce-based CSP is the correct replacement.
  4. Fix expose_headers: ["*"] in CORS (Medium) — Changed to []. Was exposing all response headers to cross-origin JS unnecessarily.
  5. Add SSRF validation to InstanceUpdate schema (High)InstanceCreate had URL validation, InstanceUpdate did not. An attacker could update an instance URL to http://169.254.169.254/ to achieve SSRF.
  6. Add bounds to /api/dashboard/activity limit parameter (Low) — Was unbounded, enabling DoS via massive DB scans. Now Query(10, ge=1, le=100).
  7. Constrain algorithm config to HS256 only (Medium) — The field was configurable but silently ignored. Now validates and rejects non-HS256 values.

Severity: High + Medium + Low

Test plan

  • Verify CSP header includes object-src 'none'
  • Verify Permissions-Policy header present in responses
  • Verify X-XSS-Protection header is absent
  • Verify CORS Access-Control-Expose-Headers is not *
  • Verify updating instance URL to a private IP is rejected
  • Verify /api/dashboard/activity?limit=10000 returns 422
  • Verify ALGORITHM=RS256 in env causes startup failure

Ref: docs/security-assessment-2026-02-27.md findings HIGH-1, MED-1, MED-4, MED-7, LOW-2, LOW-6

🤖 Generated with Claude Code

- Add object-src 'none' to CSP and Permissions-Policy header
- Remove deprecated X-XSS-Protection header (nonce-based CSP is sufficient)
- Restrict CORS expose_headers from wildcard to empty list
- Add SSRF URL validation to InstanceUpdate schema (matching InstanceCreate)
- Bound /api/dashboard/activity limit param to 1-100 via Query()
- Add validator constraining JWT algorithm config to HS256 only

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@menottim menottim merged commit 764d60e into main Feb 27, 2026
@menottim menottim deleted the security/fix-headers-csp branch February 27, 2026 03:44
menottim added a commit that referenced this pull request Mar 1, 2026
Approved design for Features #8 and #9:
- Prowlarr as separate config (not instance type)
- Read indexer limits/stats from Prowlarr API, fall back to per-instance rate
- Season pack search with per-queue threshold (default 3)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
menottim added a commit that referenced this pull request Mar 5, 2026
…alse positives

Deleted leftover development scripts that logged secrets in plaintext:
- test_sqlcipher.py (alerts #1, #2)
- verify_phase1.py (alert #3)

Dismissed 8 false positive alerts with explanations:
- #4-7: py/incomplete-url-substring-sanitization in test assertions (not sanitization code)
- #8: py/stack-trace-exposure in health check (str(e) only in logger, response is generic)
- #9-10: py/weak-sensitive-data-hashing (SHA256 is pepper mixing before Argon2id, not the hash)
- #11: py/stack-trace-exposure in prowlarr (hardcoded error message, not str(e))
- #13: py/stack-trace-exposure in dashboard (Instance model field, not exception)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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