Skip to content

Commit

Permalink
Remove SRI hash from asset_util and use html transformer instead
Browse files Browse the repository at this point in the history
  • Loading branch information
Frederik Rothenberger committed Dec 1, 2023
1 parent 8d3d758 commit 7f94c23
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 32 deletions.
42 changes: 12 additions & 30 deletions src/asset_util/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
use base64::engine::general_purpose::STANDARD as BASE64;
use base64::Engine;
use ic_cdk::api;
use ic_certification::{
fork, fork_hash, labeled, labeled_hash, pruned, AsHashTree, Hash, HashTree, NestedTree, RbTree,
};
Expand Down Expand Up @@ -109,29 +106,8 @@ impl ContentType {
}
}

// The <script> tag that loads the 'index.js'
const JS_SETUP_SCRIPT: &str = "let s = document.createElement('script');s.type = 'module';s.src = '/index.js';document.head.appendChild(s);";

// Fix up HTML pages, by injecting canister ID & script tag
fn fixup_html(html: &str) -> String {
let canister_id = api::id();
let setup_js: String = JS_SETUP_SCRIPT.to_string();
html.replace(
r#"<script type="module" crossorigin src="/index.js"></script>"#,
&format!(r#"<script data-canister-id="{canister_id}" type="module">{setup_js}</script>"#),
)
}

lazy_static! {
// The SRI sha256 hash of the script tag, used by the CSP policy.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src
pub static ref JS_SETUP_SCRIPT_SRI_HASH: String = {
let hash = &sha2::Sha256::digest(JS_SETUP_SCRIPT.as_bytes());
let hash = BASE64.encode(hash);
format!("sha256-{hash}")
};

pub static ref EXPR_HASH: Hash = sha2::Sha256::digest(IC_CERTIFICATE_EXPRESSION).into();
pub static ref EXPR_HASH: Hash = sha2::Sha256::digest(IC_CERTIFICATE_EXPRESSION).into();
}

/// Takes a list of assets and returns a [CertifiedAssets] struct containing the assets and their
Expand Down Expand Up @@ -227,22 +203,28 @@ fn response_hash(headers: &[HeaderField], body_hash: &Hash) -> Hash {
response_hash
}

pub fn collect_assets_recursive(dir: &Dir) -> Vec<(String, Vec<u8>, ContentEncoding, ContentType)> {
let mut assets = collect_assets_from_dir(dir);
pub fn collect_assets_recursive(
dir: &Dir,
html_transformer: fn(&str) -> String,
) -> Vec<(String, Vec<u8>, ContentEncoding, ContentType)> {
let mut assets = collect_assets_from_dir(dir, html_transformer);
for subdir in dir.dirs() {
assets.extend(collect_assets_recursive(subdir).into_iter());
assets.extend(collect_assets_recursive(subdir, html_transformer).into_iter());
}
assets
}

pub fn collect_assets_from_dir(dir: &Dir) -> Vec<(String, Vec<u8>, ContentEncoding, ContentType)> {
pub fn collect_assets_from_dir(
dir: &Dir,
html_transformer: fn(&str) -> String,
) -> Vec<(String, Vec<u8>, ContentEncoding, ContentType)> {
let mut assets: Vec<(String, Vec<u8>, ContentEncoding, ContentType)> = vec![];
for asset in dir.files() {
let file_bytes = asset.contents().to_vec();
let (content, encoding, content_type) = match file_extension(asset) {
"css" => (file_bytes, ContentEncoding::Identity, ContentType::CSS),
"html" => (
fixup_html(String::from_utf8_lossy(&file_bytes).as_ref())
html_transformer(String::from_utf8_lossy(&file_bytes).as_ref())
.as_bytes()
.to_vec(),
ContentEncoding::Identity,
Expand Down
30 changes: 29 additions & 1 deletion src/internet_identity/src/assets.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
use ic_cdk::api;
// All assets
//
// This file describes which assets are used and how (content, content type and content encoding).
use crate::http::security_headers;
use crate::state;
use asset_util::{collect_assets_recursive, ContentEncoding, ContentType};
use base64::engine::general_purpose::STANDARD as BASE64;
use base64::Engine;
use include_dir::{include_dir, Dir};
use lazy_static::lazy_static;
use sha2::Digest;

// used both in init and post_upgrade
pub fn init_assets() {
Expand All @@ -13,11 +18,34 @@ pub fn init_assets() {
});
}

// The <script> tag that loads the 'index.js'
const JS_SETUP_SCRIPT: &str = "let s = document.createElement('script');s.type = 'module';s.src = '/index.js';document.head.appendChild(s);";

// Fix up HTML pages, by injecting canister ID & script tag
fn fixup_html(html: &str) -> String {
let canister_id = api::id();
let setup_js: String = JS_SETUP_SCRIPT.to_string();
html.replace(
r#"<script type="module" crossorigin src="/index.js"></script>"#,
&format!(r#"<script data-canister-id="{canister_id}" type="module">{setup_js}</script>"#),
)
}

lazy_static! {
// The SRI sha256 hash of the script tag, used by the CSP policy.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src
pub static ref JS_SETUP_SCRIPT_SRI_HASH: String = {
let hash = &sha2::Sha256::digest(JS_SETUP_SCRIPT.as_bytes());
let hash = BASE64.encode(hash);
format!("sha256-{hash}")
};
}

static ASSET_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/../../dist");

// Gets the static assets. All static assets are prepared only once (like injecting the canister ID).
fn get_static_assets() -> Vec<(String, Vec<u8>, ContentEncoding, ContentType)> {
let mut assets = collect_assets_recursive(&ASSET_DIR);
let mut assets = collect_assets_recursive(&ASSET_DIR, fixup_html);

// Required to make II available on the identity.internetcomputer.org domain.
// See https://internetcomputer.org/docs/current/developer-docs/production/custom-domain/#custom-domains-on-the-boundary-nodes
Expand Down
3 changes: 2 additions & 1 deletion src/internet_identity/src/http.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::assets::JS_SETUP_SCRIPT_SRI_HASH;
use crate::http::metrics::metrics;
use crate::state;
use asset_util::{EXACT_MATCH_TERMINATOR, IC_CERTIFICATE_EXPRESSION, JS_SETUP_SCRIPT_SRI_HASH};
use asset_util::{EXACT_MATCH_TERMINATOR, IC_CERTIFICATE_EXPRESSION};
use base64::engine::general_purpose::STANDARD as BASE64;
use base64::Engine;
use canister_sig_util::signature_map::LABEL_SIG;
Expand Down

0 comments on commit 7f94c23

Please sign in to comment.