diff --git a/.config/spellcheck.dic b/.config/spellcheck.dic index ae45af82cb..47a03546d7 100644 --- a/.config/spellcheck.dic +++ b/.config/spellcheck.dic @@ -1,4 +1,4 @@ -86 +88 Monterey IC ICP @@ -85,3 +85,5 @@ DoS Ctrl Cmd Banxa +Subresource +stylesheet diff --git a/CHANGELOG-Sns_Aggregator.md b/CHANGELOG-Sns_Aggregator.md index 99ba496590..04798ed351 100644 --- a/CHANGELOG-Sns_Aggregator.md +++ b/CHANGELOG-Sns_Aggregator.md @@ -15,6 +15,8 @@ The SNS Aggregator is released through proposals in the Network Nervous System. ### Fixed ### Security +- Pin the home page's Prism script and stylesheet with Subresource Integrity and load them from the certified gateway. + ## [Proposal 137283](https://dashboard.internetcomputer.org/proposal/137283) ### Added - Include SNS Governance metrics. diff --git a/rs/sns_aggregator/src/assets.rs b/rs/sns_aggregator/src/assets.rs index f22ce71103..07332f4833 100644 --- a/rs/sns_aggregator/src/assets.rs +++ b/rs/sns_aggregator/src/assets.rs @@ -8,6 +8,9 @@ use serde_bytes::ByteBuf; use sha2::{Digest, Sha256}; use std::collections::HashMap; +#[cfg(test)] +mod tests; + /// A standard HTTP header type HeaderField = (String, String); diff --git a/rs/sns_aggregator/src/assets/tests.rs b/rs/sns_aggregator/src/assets/tests.rs new file mode 100644 index 0000000000..d2561d8722 --- /dev/null +++ b/rs/sns_aggregator/src/assets/tests.rs @@ -0,0 +1,58 @@ +//! Tests for the aggregator assets +#![allow(clippy::panic)] +#![allow(clippy::expect_used)] +#![allow(clippy::unwrap_used)] + +/// The home page, exactly as it is compiled into the canister. +const HOME_PAGE: &str = include_str!("../index.html"); + +/// The text of every tag in the given HTML, without the enclosing angle brackets. +/// +/// For example `` yields `link href="x" /`. +fn tag_bodies(html: &str) -> Vec<&str> { + html.split('<') + .skip(1) + .filter_map(|chunk| chunk.split_once('>').map(|(body, _rest)| body)) + .collect() +} + +/// True if the tag body is a tag with the given name, such as `script` or `link`. +fn has_tag_name(tag_body: &str, name: &str) -> bool { + tag_body + .strip_prefix(name) + .is_some_and(|rest| rest.is_empty() || rest.starts_with('/') || rest.starts_with(char::is_whitespace)) +} + +/// Every external ` - + +