Commit Graph

4 Commits

Author SHA1 Message Date
Marius 04d6a5ccaa Fix: CORS preflight 401'd by AuthMiddleware before CORSMiddleware (#3262)
AuthMiddleware is the outermost middleware, so a credential-less CORS preflight
(OPTIONS + Access-Control-Request-Method) was rejected with 401 before
CORSMiddleware could answer it. That blocks every cross-origin browser/WebView
client: the preflight fails, so the real request is never sent.

Let a genuine preflight through at the top of AuthMiddleware.dispatch via a pure,
unit-tested predicate (core.middleware.is_cors_preflight). Precise -- only
OPTIONS carrying Access-Control-Request-Method; a credentialed request is never
matched -- and no data access.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 15:23:23 +02:00
Giuseppe 95c2dca4b5 fix(security): add HSTS and Permissions-Policy to SecurityHeadersMiddleware (#3081)
* fix(security): add HSTS and Permissions-Policy headers to SecurityHeadersMiddleware

Strict-Transport-Security is sent only when the connection is HTTPS
(detected via request.url.scheme or X-Forwarded-Proto: https), so
plain-HTTP dev deployments behind a reverse proxy are unaffected.

Permissions-Policy disables camera, microphone, and geolocation APIs
unconditionally — Odysseus does not use them, and this prevents a
successful XSS from requesting browser-native sensor access.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(security): scope Permissions-Policy microphone directive to same-origin

Reviewers on PR #3081 (alteixeira20, NubsCarson) flagged that
microphone=() blocks mic access for same-origin (self) too, breaking
Odysseus's own voice/STT flow (getUserMedia({audio: true}) in
static/js/voiceRecorder.js). Scope it to microphone=(self) so
third-party origins stay locked out while the app's own UI keeps mic
access; camera and geolocation remain fully disabled as unused.

Adds focused middleware tests covering HSTS scoping (HTTPS direct,
X-Forwarded-Proto, absent on plain HTTP) and the Permissions-Policy
same-origin microphone contract.

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 04:58:33 +01:00
Jamieson O'Reilly 171c29dcf3 Fix email-thread HTML injection, attachment path traversal, and missing authz (#475)
Hardens issues found in a security review of the current tree (separate from
the cookbook SSH PR):

- Email thread rendering (static/js/emailLibrary.js): the flat read path runs
  inbound HTML through the allowlist sanitizer, but the two threaded paths
  (_renderTurnsAsBubbles / _renderTurnsFromServer — the default view) injected
  server-parsed `body_html` raw into the DOM. A crafted inbound email could
  inject arbitrary markup (phishing/form/credential-capture/tracking; full XSS
  if a deployment relaxes the script CSP). Now sanitized on all paths.

- Attachment extraction (routes/email_routes.py, routes/email_helpers.py): the
  on-disk extraction dir was `ATTACHMENTS_DIR / f"{folder}_{uid}"` with
  user-controlled folder/uid and no containment, so a folder like `../../tmp`
  could escape ATTACHMENTS_DIR. New attachment_extract_dir() flattens both to a
  single safe segment and asserts containment.

- Diagnostics routes (routes/diagnostics_routes.py): /api/db/stats,
  /api/rag/stats, /api/test/youtube, /api/test-research relied only on the
  global session check (any logged-in user). Now require_admin-gated.

- Defense-in-depth HTML escaping: session HTML export escapes the session name
  (routes/session_routes.py); the MCP OAuth page escapes the reflected Host
  header / server_id (routes/mcp_routes.py).

- Internal-tool token now compared with secrets.compare_digest (constant time)
  in core/middleware.py and app.py.

Adds regression tests in tests/test_security_regressions.py.
2026-06-01 22:20:17 +09:00
pewdiepie-archdaemon e5c99a5eee Odysseus v1.0 2026-05-31 23:58:26 +09:00