Fix CSP nonce validation and violation reporting for external scripts (#40956)

This PR fixes two related issues with Content Security Policy (CSP)
nonce validation for external scripts:

1. Missing nonce validation for external scripts with malformed
attributes
2. Incorrect violation event reporting for blocked external resources


This makes servo closer to passing the `nonce-enforce-blocked` wpt test.

The remaining failures are blocked by required changes in the html
parser.

1. Svg script support (https://github.com/servo/html5ever/issues/118)
```html
<svg xmlns="http://www.w3.org/2000/svg">
<script attribute attribute nonce="abc">
    t.unreached_func("Duplicate attribute in SVG, no execution.")();
</script>
</svg>
```

2. Duplicate attrs check
the html parser needs to provide this flag, as mentioned on the original
commit message
(4821bc0ab0)

```html
<script attribute attribute nonce="abc">
    t.unreached_func("Duplicate attribute, no execution.")();
</script>
<script attribute attribute=<style nonce="abc">
    t.unreached_func("2# Duplicate attribute, no execution.")();
</script>

[...]

<script src="../support/nonce-should-be-blocked.js?5" attribute attribute nonce="abc"></script>
```

I've also created a PR to implement the duplicate attrs flag on
html5ever https://github.com/servo/html5ever/pull/695

Testing: doesn't fixes the aforementioned wpt test yet.
Fixes: part of #36437

---------

Signed-off-by: Dyego Aurélio <dyegoaurelio@gmail.com>
This commit is contained in:
dyegoaurelio
2026-02-27 10:17:33 -03:00
committed by GitHub
parent 49b13627b5
commit 453166752b
10 changed files with 152 additions and 41 deletions

View File

@@ -472,10 +472,7 @@ impl FetchResponseListener for ClassicContext {
fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
let global = &self.resource_timing_global();
let elem = self.elem.root();
let source_position = elem
.upcast::<Element>()
.compute_source_position(elem.line_number as u32);
global.report_csp_violations(violations, Some(elem.upcast()), Some(source_position));
global.report_csp_violations(violations, Some(elem.upcast()), None);
}
}
@@ -725,7 +722,15 @@ impl HTMLScriptElement {
};
// Step 24. Let cryptographic nonce be el's [[CryptographicNonce]] internal slot's value.
let cryptographic_nonce = self.upcast::<Element>().nonce_value();
// If the element has a nonce content attribute but is not nonceable strip the nonce to prevent injection attacks.
// Elements without a nonce content attribute (e.g. JS-created with .nonce = "abc")
// use the internal slot directly — the nonceable check only applies to parser-created elements.
let el = self.upcast::<Element>();
let cryptographic_nonce = if el.is_nonceable() || !el.has_attribute(&local_name!("nonce")) {
el.nonce_value().trim().to_owned()
} else {
String::new()
};
// Step 25. If el has an integrity attribute, then let integrity metadata be that attribute's value.
// Otherwise, let integrity metadata be the empty string.