Repository navigation
fix(security+frontend): debug-session batch 3 — CSP wss tighten, ssot https detection - #6751
Merged
Merged
Conversation
…y wss Previous CSP `connect-src 'self' wss:` allowed WebSocket connections to ANY wss:// host — far broader than necessary. The SPA only opens WebSockets to the same origin that served the page, so we can scope to `wss://$host` (nginx interpolates the request's Host header at runtime, so the directive stays correct under any deployment IP/hostname without needing a Jinja round-trip). Note: `'self'` is included as well because some Chromium versions don't treat 'self' as authoritative for ws/wss schemes; the explicit `wss://$host` covers that path. Both redundant guards are cheaper than a CSP-violation audit later. Three CSP add_header sites updated: - Line 44 (default response headers) - Line 269 (SPA / route fallback) - Line 288 (login route fallback) Tested by reading the nginx $host variable definition: it is the value of the Host header from the request (or the value matched by the listening server_name). For HTTPS over IP it'll resolve to the IP; for DNS-named deployments it resolves to the hostname. In both cases this matches the origin the page was served from, satisfying same-origin WS. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rl getters PR #6642 already fixed this exact bug for websocketUrl: build-time VITE_HTTP_PROTOCOL defaults to 'http' and produced ws:// URLs that browsers blocked as mixed-content on HTTPS pages. The same bug remained in the backendUrl and frontendUrl getters at ssot-config.ts:440-446 — they returned `http://...` when the page was served over HTTPS, causing the browser to block requests like `http://172.16.168.20/api/secrets/` (user-reported in DevTools Network tab as 'status pending', then blocked). Fix: in browser context, derive protocol from `window.location.protocol` exactly like `websocketUrl` does (line 463-466). In SSR/build context fall back to the build-time `httpProtocol` value. Note: most production code paths actually take the `getBackendUrl()` proxy-mode branch (returns '' when VITE_BACKEND_HOST is unset, so all API calls go same-origin via nginx) — but any composable that reaches `getConfig().backendUrl` directly hits the broken path. Hardening the getter closes the gap for those callers and makes the dev/prod behaviour symmetric. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
|
| Metric | Count |
|---|---|
| Total Violations | 1 |
| SSOT Violations (high priority) | 1 |
| Other Violations | 0 |
⚠️ 1 values have SSOT config equivalents!
These should be replaced with SSOT config imports:
Python:
from src.config.ssot_config import config
# Use: config.vm.main, config.port.backend, config.backend_urlTypeScript:
import config from '@/config/ssot-config'
// Use: config.vm.main, config.port.backend, config.backendUrl📖 See SSOT_CONFIG_GUIDE.md for documentation.
This was referenced May 4, 2026
6 tasks done
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
Two independent fixes from the same /superpowers:systematic-debugging session as PRs #6718 and #6749. Two other items from the Chrome-plugin debug TODO were verified already-fixed against the live deployment and are documented below for traceability.
1. CSP
connect-srctightened (item #26)Previous CSP
connect-src 'self' wss:allowed WebSocket connections to ANYwss://host. The SPA only opens WebSockets to the same origin that served the page, so we can scope towss://$host(nginx interpolates the request's Host header at runtime — stays correct under any deployment IP/hostname). Three CSPadd_headersites updated inautobot-slm.conf.j2.'self'is kept because some Chromium versions don't treat 'self' as authoritative for ws/wss; both guards are cheap.2. ssot-config: runtime https detection for backendUrl + frontendUrl (item #14)
backendUrlandfrontendUrlgetters atssot-config.ts:440-446returnedhttp://...(from build-timeVITE_HTTP_PROTOCOLdefaulting to 'http'), causing the browser to block requests likehttp://172.16.168.20/api/secrets/as mixed-content on HTTPS pages — exactly the symptom user reported in DevTools (status 'pending'). PR #6642 already fixed this forwebsocketUrl; same pattern applied here. In browser context derive protocol fromwindow.location.protocol; in SSR fall back to build-time. Most production code paths take thegetBackendUrl()proxy-mode branch (returns '' → relative URLs), but any composable that hitsgetConfig().backendUrldirectly was broken.Verified already-fixed against live deployment
Item #10 — HEAD /api/health
curl -X HEAD https://172.16.168.20/api/healthreturns 200 (was 503). Source:initialization/endpoints.py:145registers@app.head("/api/health")explicitly. Already done.Item #9 — /api/secrets trailing slash
curl /api/secretsnow returns 307 (FastAPIredirect_slashes=Trueworking) instead of the previously-reported 503. The 307 redirects to/api/secrets/which returns 200 with data. Browsers follow 307 with method+headers preserved, so the frontend works correctly. Acceptance criterion 'both /api/secrets and /api/secrets/ return 200' is partially met (307→200 vs direct 200) — the practical outcome is identical for the SPA.Test plan
https://172.16.168.20/loads with no CSP violations in console🤖 Generated with Claude Code