Skip to content
GitHubGHSA-rqq5-2gf9-4w4q

CSP directive injection via sandbox, plugin_types, and report_to when given untrusted input

Medium4.7CVE-2026-54163 · Published Jun 3, 2026 · updated Jun 12, 2026

## Summary `secure_headers` builds the `Content-Security-Policy` value by stitching every configured directive together with `; ` separators. Three directive builders (`build_sandbox_list_directive`, `build_media_type_list_directive`, `build_report_to_directive`) interpolate caller-supplied strings into that value without scrubbing `;`, `\r`, or `\n`. When an application forwards untrusted input into `SecureHeaders.override_content_security_policy_directives` (or `append_…`) for `:sandbox`, `:plugin_types`, or `:report_to`, an attacker can embed a literal `;` and inject an arbitrary CSP directive into the header value. Because `:sandbox` and `:plugin_types` both sort alphabetically before `:script_src` in `BODY_DIRECTIVES`, the injected `script-src` lands earlier in the header and wins under the [CSP first-occurrence rule](https://www.w3.org/TR/CSP3/#parse-serialized-policy), defeating the application's real `script-src`. End result: an `'unsafe-inline' *` policy is forced for inline `<script>` despite the configured strict CSP, giving full XSS reachability anywhere reflected or stored content meets one of these three sinks. An existing `;`/`\n` scrub is already present in the s...

GitHub advisory

Affected versions

PackageAffectedFixed in
secure_headers
RubyGems
< 7.3.07.3.0
Details and references

## Summary `secure_headers` builds the `Content-Security-Policy` value by stitching every configured directive together with `; ` separators. Three directive builders (`build_sandbox_list_directive`, `build_media_type_list_directive`, `build_report_to_directive`) interpolate caller-supplied strings into that value without scrubbing `;`, `\r`, or `\n`. When an application forwards untrusted input into `SecureHeaders.override_content_security_policy_directives` (or `append_…`) for `:sandbox`, `:plugin_types`, or `:report_to`, an attacker can embed a literal `;` and inject an arbitrary CSP directive into the header value. Because `:sandbox` and `:plugin_types` both sort alphabetically before `:script_src` in `BODY_DIRECTIVES`, the injected `script-src` lands earlier in the header and wins under the [CSP first-occurrence rule](https://www.w3.org/TR/CSP3/#parse-serialized-policy), defeating the application's real `script-src`. End result: an `'unsafe-inline' *` policy is forced for inline `<script>` despite the configured strict CSP, giving full XSS reachability anywhere reflected or stored content meets one of these three sinks. An existing `;`/`\n` scrub is already present in the source-list builder (`build_source_list_directive`), but the three sibling builders here never received the same treatment and still emit caller bytes verbatim into the CSP value. ## Impact Although piping untrusted input into CSP directives is generally discouraged, applications that do so for one of the three uncovered directives turn that endpoint into an XSS sink with an effective `*` `'unsafe-inline'` `script-src`, even though the global config says `script_src: %w('self')`. The same primitive can also be used to point `report-to` / `report-uri` at attacker infrastructure to silently siphon CSP violation reports — which include the violated URL, blocked-uri, source-file, line-number and a sample-snippet, useful for fingerprinting and for harvesting victim-internal URLs. The global default CSP set in `Configuration.default` is supposed to be a backstop: even if a controller appends a single risky value, the strict `script-src` should remain the first match. This bug breaks that property by letting the appended value redefine the policy header upstream of the legitimate `script-src`. ## Affected - **Package:** `secure_headers` (RubyGems) - **Vulnerable versions:** `<= 7.2.0` - **Patched version:** `7.3.0` Applications that set `:sandbox`, `:plugin_types`, or `:report_to` only from static configuration (no per-request or per-tenant input) are not exploitable and need only the version bump. Applications that pipe any user-controlled value into one of those three directives via the per-controller override APIs are exploitable and should both upgrade and audit those code paths. ## Mitigations / Workarounds Until upgrading to **7.3.0**, sanitize any user-controlled input before passing it to: - `SecureHeaders.override_content_security_policy_directives` - `SecureHeaders.append_content_security_policy_directives` - `SecureHeaders.use_content_security_policy_named_append` for `:sandbox`, `:plugin_types`, or `:report_to`. Reject or strip `;`, `\r`, and `\n` from values destined for these directives before they reach the gem. ## Vulnerable code Three sibling builders all join an attacker-controllable value into the CSP header value with no `;` / `\r` / `\n` scrubbing. - [`content_security_policy.rb#L72-L93`](https://github.com/github/secure_headers/blob/f224144c99002bcd3c06ed86c169429d4be1e5dc/lib/secure_headers/headers/content_security_policy.rb#L72-L93) — `build_sandbox_list_directive`: ```ruby elsif sandbox_list && sandbox_list.any? [ symbol_to_hyphen_case(directive), sandbox_list.uniq ].join(" ") end ``` - [`content_security_policy.rb#L95-L103`](https://github.com/github/secure_headers/blob/f224144c99002bcd3c06ed86c169429d4be1e5dc/lib/secure_headers/headers/content_security_policy

CVSS 3.1
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:N
Severity from
GitHub (reviewed advisory)
Weakness
CWE-79, CWE-113

More GitHub advisories

All GitHub
Advisory
GitHub Enterprise Server: denial of service
Medium6.6Aug 5
GitHub Enterprise Server: path traversal
High8.8Aug 5
GitHub Enterprise Server: missing authorization
Medium5.3Jul 17
GitHub Enterprise Server: path traversal
High8.6Jul 17
GitHub Enterprise Server: denial of service
Medium5.7Jul 17
GitHub Enterprise Server: improper authorization
Medium5.3Jul 1

Critical advisories by email

Wednesdays: the week’s critical and high advisories in the AI and data stack, with the fixed versions. Only in weeks that have some.

Double opt-in. Unsubscribe any time.