feat(security)(jetstream): optional opt-in Content-Security-Policy header (CONSOLE_CSP)#5688
Merged
Conversation
Jetstream serves no Content-Security-Policy, which OWASP ZAP and other
scanners flag as a medium-severity finding (CWE-693). Add an OPT-IN CSP via
the existing Echo Secure middleware (alongside the current X-Frame-Options),
gated on a new CONSOLE_CSP env var so upgrades never change a deployment's
response headers without an explicit opt-in.
CONSOLE_CSP values:
- unset / off / none / false / disabled -> no header (default; preserves
existing behavior)
- default / on -> built-in defaultCSPPolicy,
scoped to what the Stratos SPA + Monaco editor need (self scripts;
inline + Google Fonts styles; data:/Google font files; data: images;
self + ws/wss connect; blob: workers; frame-ancestors self)
- any other value -> used verbatim as the policy
Adds the CSPPolicy config field (CONSOLE_CSP) and unit tests covering the
unset (no header), default/on, custom-verbatim, and explicit-off cases.
Signed-off-by: Chris Weibel <christopher.weibel@gsa.gov>
The default CSP used 'connect-src self ws: wss:', which scanners flag as an overly permissive wildcard (bare ws:/wss: allow WebSocket to any host). On an HTTPS page, 'self' already permits same-origin wss:// connections, so the backend log/stream sockets work without the wildcard. Tighten connect-src to 'self' only. Signed-off-by: Chris Weibel <christopher.weibel@gsa.gov>
norman-abramovitz
approved these changes
Jul 24, 2026
norman-abramovitz
left a comment
Contributor
There was a problem hiding this comment.
LGTM - Discussed with cweibel and will merge as the foundation for improving security based upon ZAP findings.
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.
Problem
Jetstream serves no
Content-Security-Policyheader. OWASP ZAP (and other scanners) flag this as a medium-severity finding (CWE-693) — CSP is a defense-in-depth layer against XSS and data-injection. The backend already setsX-Frame-Options: SAMEORIGINvia Echo'sSecuremiddleware, so CSP is a natural addition right beside it.Change
Add a CSP through the existing
middleware.SecureWithConfig, gated on a newCONSOLE_CSPenv var. It is opt-in — withCONSOLE_CSPunset, no header is emitted, so an upgrade never changes a deployment's response headers (or risks breaking asset loading) without an explicit opt-in.CONSOLE_CSPvalues:off/none/false/disabled→ no header (default; preserves current behavior)default/on→ the built-indefaultCSPPolicyBuilt-in default policy:
Rationale per allowance:
'unsafe-inline'styles — Angular and theindex.htmlloading splash inject inline<style>; Google Fonts stylesheet allowed.data:/fonts.gstatic.comfonts,data:images — inlined icons + Google Fonts.connect-src 'self'— same-origin also covers the backend log/streamwss://sockets on the HTTPS page (no barews:/wss:wildcard, which scanners flag as overly permissive).worker-src blob:— the Monaco editor spins up its language web-workers from blob URLs.frame-ancestors 'self'mirrors the existingX-Frame-Options: SAMEORIGIN.No
unsafe-eval— AOT production builds don't need it.Files
main.go—defaultCSPPolicyconst; wireContentSecurityPolicyinto the Secure middleware; opt-in resolution inloadPortalConfig.api/structs.go— newCSPPolicyfield (CONSOLE_CSP).main_test.go— tests for unset (no header),default/on, custom-verbatim, and explicit-off cases.Verification
Validated live against a cloud.gov dev deployment with
CONSOLE_CSP=default:OWASP ZAP full active scans across iterations confirmed:
connect-src ... ws: wss:) is resolved by tighteningconnect-srcto'self'.Known limitation
ZAP still reports
CSP: style-src unsafe-inline. Removing'unsafe-inline'fromstyle-srcrequires nonce/hash support wired through Angular's inline-style injection (and theindex.htmlloading splash) — a larger change than this PR. Documented here as a known, framework-imposed limitation; operators who need a stricter policy can supply their own viaCONSOLE_CSP.Implemented with AI assistance (OpenCode); reviewed by a human contributor. Commits are DCO signed-off.