diff --git a/packages/ic-http-certification/src/lib.rs b/packages/ic-http-certification/src/lib.rs index aa4da61a..2dc5478e 100644 --- a/packages/ic-http-certification/src/lib.rs +++ b/packages/ic-http-certification/src/lib.rs @@ -383,7 +383,7 @@ Typically these requests have been routed through `raw` Internet Computer URLs i )] pub mod cel; -pub use cel::{DefaultCelBuilder, DefaultResponseCertification}; +pub use cel::{CelExpression, DefaultCelBuilder, DefaultResponseCertification}; pub mod error; pub use error::*; pub mod http; diff --git a/packages/ic-response-verification/tests/v2_response_verification_certification_scenarios.rs b/packages/ic-response-verification/tests/v2_response_verification_certification_scenarios.rs index 9bc4aef7..87f2e6ba 100644 --- a/packages/ic-response-verification/tests/v2_response_verification_certification_scenarios.rs +++ b/packages/ic-response-verification/tests/v2_response_verification_certification_scenarios.rs @@ -296,13 +296,12 @@ mod tests { #[cfg(not(target_arch = "wasm32"))] mod fixtures { - use ic_http_certification::{HttpRequest, HttpResponse}; - use ic_response_verification::{ - cel::cel_to_certification, - hash::{request_hash, response_hash}, + use ic_http_certification::{ + cel::CelExpression, request_hash, response_hash, DefaultCelBuilder, + DefaultResponseCertification, HttpRequest, HttpResponse, }; use ic_response_verification_test_utils::{ - create_expr_tree_path, deflate_encode, gzip_encode, hash, remove_whitespace, ExprTree, + create_expr_tree_path, deflate_encode, gzip_encode, hash, ExprTree, }; use rstest::*; @@ -324,7 +323,7 @@ mod fixtures { headers: vec![ ("Content-Type".into(), "text/html".into()), ("Content-Encoding".into(), "gzip".into()), - ("IC-CertificateExpression".into(), cel), + ("IC-CertificateExpression".into(), cel.to_string()), ], } } @@ -340,7 +339,7 @@ mod fixtures { headers: vec![ ("Content-Type".into(), "text/javascript".into()), ("Content-Encoding".into(), "gzip".into()), - ("IC-CertificateExpression".into(), cel), + ("IC-CertificateExpression".into(), cel.to_string()), ], } } @@ -356,7 +355,7 @@ mod fixtures { headers: vec![ ("Content-Type".into(), "text/plain".into()), ("Content-Encoding".into(), "identity".into()), - ("IC-CertificateExpression".into(), cel), + ("IC-CertificateExpression".into(), cel.to_string()), ], } } @@ -371,7 +370,7 @@ mod fixtures { body: body.to_vec(), headers: vec![ ("Location".into(), "/new-path".into()), - ("IC-CertificateExpression".into(), cel), + ("IC-CertificateExpression".into(), cel.to_string()), ], } } @@ -386,7 +385,7 @@ mod fixtures { headers: vec![ ("Content-Type".into(), "text/html".into()), ("Content-Encoding".into(), "identity".into()), - ("IC-CertificateExpression".into(), cel), + ("IC-CertificateExpression".into(), cel.to_string()), ], } } @@ -401,7 +400,7 @@ mod fixtures { headers: vec![ ("Content-Type".into(), "text/html".into()), ("Content-Encoding".into(), "gzip".into()), - ("IC-CertificateExpression".into(), cel), + ("IC-CertificateExpression".into(), cel.to_string()), ], } } @@ -416,7 +415,7 @@ mod fixtures { headers: vec![ ("Content-Type".into(), "text/html".into()), ("Content-Encoding".into(), "deflate".into()), - ("IC-CertificateExpression".into(), cel), + ("IC-CertificateExpression".into(), cel.to_string()), ], } } @@ -448,7 +447,7 @@ mod fixtures { headers: vec![ ("Content-Type".into(), "text/html".into()), ("Content-Encoding".into(), "deflate".into()), - ("IC-CertificateExpression".into(), cel), + ("IC-CertificateExpression".into(), cel.to_string()), ], } } @@ -482,100 +481,57 @@ mod fixtures { ("Content-Type".into(), "text/html".into()), ("Content-Encoding".into(), "deflate".into()), ("ETag".into(), etag), - ("IC-CertificateExpression".into(), cel), + ("IC-CertificateExpression".into(), cel.to_string()), ], } } #[fixture] - pub fn asset_cel() -> String { - remove_whitespace( - r#" - default_certification ( - ValidationArgs { - certification: Certification { - no_request_certification: Empty {}, - response_certification: ResponseCertification { - certified_response_headers: ResponseHeaderList { - headers: ["Content-Type", "Content-Encoding"] - } - } - } - } - ) - "#, - ) + pub fn asset_cel() -> CelExpression<'static> { + DefaultCelBuilder::response_certification() + .with_response_certification(DefaultResponseCertification::certified_response_headers( + &["Content-Type", "Content-Encoding"], + )) + .build() } #[fixture] - pub fn redirect_cel() -> String { - remove_whitespace( - r#" - default_certification ( - ValidationArgs { - certification: Certification { - no_request_certification: Empty {}, - response_certification: ResponseCertification { - certified_response_headers: ResponseHeaderList { - headers: ["Location"] - } - } - } - } - ) - "#, - ) + pub fn redirect_cel() -> CelExpression<'static> { + DefaultCelBuilder::response_certification() + .with_response_certification(DefaultResponseCertification::certified_response_headers( + &["Location"], + )) + .build() } #[fixture] - pub fn etag_caching_match_cel() -> String { - remove_whitespace( - r#" - default_certification ( - ValidationArgs { - certification: Certification { - request_certification: RequestCertification { - certified_request_headers: ["If-None-Match"], - certified_query_parameters: [] - }, - response_certification: ResponseCertification { - certified_response_headers: ResponseHeaderList { - headers: ["Content-Type", "Content-Encoding"] - } - } - } - } - ) - "#, - ) + pub fn etag_caching_match_cel() -> CelExpression<'static> { + DefaultCelBuilder::full_certification() + .with_request_headers(&["If-None-Match"]) + .with_response_certification(DefaultResponseCertification::certified_response_headers( + &["Content-Type", "Content-Encoding", "ETag"], + )) + .build() } #[fixture] - pub fn etag_caching_mismatch_cel() -> String { - remove_whitespace( - r#" - default_certification ( - ValidationArgs { - certification: Certification { - no_request_certification: Empty {}, - response_certification: ResponseCertification { - certified_response_headers: ResponseHeaderList { - headers: ["Content-Type", "Content-Encoding", "ETag"] - } - } - } - } - ) - "#, - ) + pub fn etag_caching_mismatch_cel() -> CelExpression<'static> { + DefaultCelBuilder::response_certification() + .with_response_certification(DefaultResponseCertification::certified_response_headers( + &["Content-Type", "Content-Encoding", "ETag"], + )) + .build() } #[fixture] pub fn certificate_tree() -> ExprTree { - let asset_cel = asset_cel(); + let asset_certification = asset_cel(); + let asset_cel = asset_certification.to_string(); let asset_cel_hash = hash(asset_cel.as_bytes()); - let asset_certification = cel_to_certification(&asset_cel).unwrap().unwrap(); + let CelExpression::DefaultCertification(Some(asset_certification)) = asset_certification else { + panic!("Expected asset certification to have response certification") + }; let index_html_response_hash = response_hash( &index_html_response(), &asset_certification.response_certification, @@ -589,10 +545,13 @@ mod fixtures { &asset_certification.response_certification, ); - let redirect_cel = redirect_cel(); + let redirect_certification = redirect_cel(); + let redirect_cel = redirect_certification.to_string(); let redirect_cel_hash = hash(redirect_cel.as_bytes()); - let redirect_certification = cel_to_certification(&redirect_cel).unwrap().unwrap(); + let CelExpression::DefaultCertification(Some(redirect_certification)) = redirect_certification else { + panic!("Expected asset certification to have response certification") + }; let redirect_response_hash = response_hash( &redirect_response(), &redirect_certification.response_certification, @@ -660,11 +619,13 @@ mod fixtures { #[fixture] pub fn etag_certificate_tree() -> ExprTree { - let etag_caching_match_cel = etag_caching_match_cel(); + let etag_caching_match_certification = etag_caching_match_cel(); + let etag_caching_match_cel = etag_caching_match_certification.to_string(); let etag_caching_match_cel_hash = hash(etag_caching_match_cel.as_bytes()); - let etag_caching_match_certification = cel_to_certification(&etag_caching_match_cel) - .unwrap() - .unwrap(); + + let CelExpression::DefaultCertification(Some(etag_caching_match_certification)) = etag_caching_match_certification else { + panic!("Expected asset certification to have response certification") + }; let etag_caching_match_response_hash = response_hash( &etag_caching_match_response(), &etag_caching_match_certification.response_certification, @@ -677,11 +638,13 @@ mod fixtures { ) .unwrap(); - let etag_caching_mismatch_cel = etag_caching_mismatch_cel(); + let etag_caching_mismatch_certification = etag_caching_mismatch_cel(); + let etag_caching_mismatch_cel = etag_caching_mismatch_certification.to_string(); let etag_caching_mismatch_cel_hash = hash(etag_caching_mismatch_cel.as_bytes()); - let etag_caching_mismatch_certification = cel_to_certification(&etag_caching_mismatch_cel) - .unwrap() - .unwrap(); + + let CelExpression::DefaultCertification(Some(etag_caching_mismatch_certification)) = etag_caching_mismatch_certification else { + panic!("Expected asset certification to have response certification") + }; let etag_caching_mismatch_response_hash = response_hash( &etag_caching_mismatch_response(), &etag_caching_mismatch_certification.response_certification, diff --git a/packages/ic-response-verification/tests/v2_response_verification_happy_path.rs b/packages/ic-response-verification/tests/v2_response_verification_happy_path.rs index aceb0e64..90e714bb 100644 --- a/packages/ic-response-verification/tests/v2_response_verification_happy_path.rs +++ b/packages/ic-response-verification/tests/v2_response_verification_happy_path.rs @@ -1,12 +1,13 @@ #[cfg(not(target_arch = "wasm32"))] mod tests { - use ic_http_certification::{HttpRequest, HttpResponse}; - use ic_response_verification::cel::cel_to_certification; - use ic_response_verification::hash::{request_hash, response_hash}; + use ic_http_certification::{ + request_hash, response_hash, CelExpression, DefaultCelBuilder, + DefaultResponseCertification, HttpRequest, HttpResponse, + }; use ic_response_verification::types::{VerificationInfo, VerifiedResponse}; use ic_response_verification::verify_request_response_pair; use ic_response_verification_test_utils::{ - create_v2_fixture, get_current_timestamp, remove_whitespace, V2Fixture, + create_v2_fixture, get_current_timestamp, V2Fixture, }; const MAX_CERT_TIME_OFFSET_NS: u128 = 300_000_000_000; @@ -18,15 +19,7 @@ mod tests { let body = "Hello World!"; let current_time = get_current_timestamp(); let expr_path = ["", "<$>"]; - let cel_expr = remove_whitespace( - r#" - default_certification ( - ValidationArgs { - no_certification: Empty { } - } - ) - "#, - ); + let cel_expr = DefaultCelBuilder::skip_certification(); let request = HttpRequest { url: path.into(), @@ -38,7 +31,7 @@ mod tests { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![ - ("IC-CertificateExpression".into(), cel_expr.clone()), + ("IC-CertificateExpression".into(), cel_expr.to_string()), ("Cache-Control".into(), "max-age=604800".into()), ], }; @@ -47,7 +40,7 @@ mod tests { root_key, certificate_header, canister_id, - } = create_v2_fixture(&cel_expr, &expr_path, ¤t_time, None, None); + } = create_v2_fixture(&cel_expr.to_string(), &expr_path, ¤t_time, None, None); response .headers @@ -79,23 +72,16 @@ mod tests { let body = "Hello World!"; let current_time = get_current_timestamp(); let expr_path = ["", "<$>"]; - let cel_expr = remove_whitespace( - r#" - default_certification ( - ValidationArgs { - certification: Certification { - no_request_certification: Empty {}, - response_certification: ResponseCertification { - certified_response_headers: ResponseHeaderList { - headers: ["Cache-Control"] - } - } - } - } - ) - "#, - ); - let certification = cel_to_certification(&cel_expr).unwrap().unwrap(); + + let certification = DefaultCelBuilder::response_certification() + .with_response_certification(DefaultResponseCertification::certified_response_headers( + &["Cache-Control"], + )) + .build(); + let cel_expr = certification.to_string(); + let CelExpression::DefaultCertification(Some(certification)) = certification else { + panic!("Expected asset certification to have response certification") + }; let response_certification = certification.response_certification; let request = HttpRequest { @@ -163,26 +149,18 @@ mod tests { let body = "Hello World!"; let current_time = get_current_timestamp(); let expr_path = ["", "<$>"]; - let cel_expr = remove_whitespace( - r#" - default_certification ( - ValidationArgs { - certification: Certification { - request_certification: RequestCertification { - certified_request_headers: ["Cache-Control"], - certified_query_parameters: ["q"] - }, - response_certification: ResponseCertification { - certified_response_headers: ResponseHeaderList { - headers: ["Cache-Control"] - } - } - } - } - ) - "#, - ); - let certification = cel_to_certification(&cel_expr).unwrap().unwrap(); + + let certification = DefaultCelBuilder::full_certification() + .with_request_headers(&["Cache-Control"]) + .with_request_query_parameters(&["q"]) + .with_response_certification(DefaultResponseCertification::certified_response_headers( + &["Cache-Control"], + )) + .build(); + let cel_expr = certification.to_string(); + let CelExpression::DefaultCertification(Some(certification)) = certification else { + panic!("Expected asset certification to have response certification") + }; let request_certification = certification.request_certification.unwrap(); let response_certification = certification.response_certification; @@ -255,23 +233,16 @@ mod tests { let body = "Hello World!"; let current_time = get_current_timestamp(); let expr_path = ["", "<$>"]; - let cel_expr = remove_whitespace( - r#" - default_certification ( - ValidationArgs { - certification: Certification { - no_request_certification: Empty {}, - response_certification: ResponseCertification { - response_header_exclusions: ResponseHeaderList { - headers: ["Content-Language", "Content-Encoding"] - } - } - } - } - ) - "#, - ); - let certification = cel_to_certification(&cel_expr).unwrap().unwrap(); + + let certification = DefaultCelBuilder::response_certification() + .with_response_certification(DefaultResponseCertification::response_header_exclusions( + &["Content-Language", "Content-Encoding"], + )) + .build(); + let cel_expr = certification.to_string(); + let CelExpression::DefaultCertification(Some(certification)) = certification else { + panic!("Expected asset certification to have response certification") + }; let response_certification = certification.response_certification; let request = HttpRequest { diff --git a/packages/ic-response-verification/tests/v2_response_verification_sad_path.rs b/packages/ic-response-verification/tests/v2_response_verification_sad_path.rs index 09fac33c..5524454b 100644 --- a/packages/ic-response-verification/tests/v2_response_verification_sad_path.rs +++ b/packages/ic-response-verification/tests/v2_response_verification_sad_path.rs @@ -7,12 +7,10 @@ mod tests { }; use candid::Principal; use ic_certificate_verification::CertificateVerificationError; - use ic_http_certification::{HttpRequest, HttpResponse}; - use ic_response_verification::{ - cel::cel_to_certification, - hash::{request_hash, response_hash}, - verify_request_response_pair, ResponseVerificationError, + use ic_http_certification::{ + request_hash, response_hash, CelExpression, HttpRequest, HttpResponse, }; + use ic_response_verification::{verify_request_response_pair, ResponseVerificationError}; use ic_response_verification_test_utils::{ create_expr_tree_path, create_v2_certificate_fixture, create_v2_fixture, create_v2_header, create_v2_tree_fixture, get_current_timestamp, hash, hash_from_hex, ExprTree, @@ -21,12 +19,18 @@ mod tests { use rstest::*; #[rstest] - fn request_hash_mismatch_fails_verification(#[from(full_certification_cel)] cel_expr: String) { + fn request_hash_mismatch_fails_verification( + #[from(full_certification_cel)] certification: CelExpression<'static>, + ) { let path = "/?q=greeting"; let body = "Hello World!"; let current_time = get_current_timestamp(); let expr_path = ["", "<$>"]; - let certification = cel_to_certification(&cel_expr).unwrap().unwrap(); + + let cel_expr = certification.to_string(); + let CelExpression::DefaultCertification(Some(certification)) = certification else { + panic!("Expected asset certification to have response certification") + }; let response_certification = certification.response_certification; let request = HttpRequest { @@ -85,13 +89,17 @@ mod tests { #[rstest] pub fn response_hash_mismatch_fails_verification( - #[from(full_certification_cel)] cel_expr: String, + #[from(full_certification_cel)] certification: CelExpression<'static>, ) { let path = "/?q=greeting"; let body = "Hello World!"; let current_time = get_current_timestamp(); let expr_path = ["", "<$>"]; - let certification = cel_to_certification(&cel_expr).unwrap().unwrap(); + + let cel_expr = certification.to_string(); + let CelExpression::DefaultCertification(Some(certification)) = certification else { + panic!("Expected asset certification to have response certification") + }; let request_certification = certification.request_certification.unwrap(); let request = HttpRequest { @@ -150,14 +158,18 @@ mod tests { #[rstest] fn cel_expr_hash_fails_verification( - #[from(skip_certification_cel)] wrong_cel_expr: String, - #[from(full_certification_cel)] cel_expr: String, + #[from(skip_certification_cel)] wrong_cel_expr: CelExpression<'static>, + #[from(full_certification_cel)] certification: CelExpression<'static>, ) { let path = "/?q=greeting"; let body = "Hello World!"; let current_time = get_current_timestamp(); let expr_path = ["", "<$>"]; - let certification = cel_to_certification(&cel_expr).unwrap().unwrap(); + + let cel_expr = certification.to_string(); + let CelExpression::DefaultCertification(Some(certification)) = certification else { + panic!("Expected asset certification to have response certification") + }; let request_certification = certification.request_certification.unwrap(); let response_certification = certification.response_certification; @@ -174,7 +186,10 @@ mod tests { status_code: 200, body: body.as_bytes().to_vec(), headers: vec![ - ("IC-CertificateExpression".into(), wrong_cel_expr.clone()), + ( + "IC-CertificateExpression".into(), + wrong_cel_expr.to_string(), + ), ("Cache-Control".into(), "max-age=604800".into()), ], }; @@ -223,9 +238,10 @@ mod tests { #[case::more_specific_path_exists_in_tree(&["assets", "<*>"])] #[case::does_not_match_request_url(&["assets", "js", "dashboard.js", "<$>"])] fn invalid_expr_path_fails_verification( - #[from(skip_certification_cel)] cel_expr: String, + #[from(skip_certification_cel)] certification: CelExpression<'static>, #[case] expr_path: &[&str], ) { + let cel_expr = certification.to_string(); let current_time = get_current_timestamp(); let request = HttpRequest { @@ -240,7 +256,7 @@ mod tests { headers: vec![("IC-CertificateExpression".to_string(), cel_expr.clone())], }; - let cel_expr_hash = hash(cel_expr); + let cel_expr_hash = hash(&cel_expr); let mut expr_tree = ExprTree::new(); expr_tree.insert(&create_expr_tree_path( @@ -372,8 +388,9 @@ mod tests { #[cfg(not(target_arch = "wasm32"))] mod fixtures { + use ic_http_certification::{CelExpression, DefaultCelBuilder, DefaultResponseCertification}; use ic_response_verification_test_utils::{ - create_v2_fixture, get_current_timestamp, get_timestamp, remove_whitespace, V2Fixture, + create_v2_fixture, get_current_timestamp, get_timestamp, V2Fixture, }; use ic_types::CanisterId; use rstest::*; @@ -386,43 +403,23 @@ mod fixtures { pub const MIN_REQUESTED_VERIFICATION_VERSION: u8 = 2; #[fixture] - pub fn full_certification_cel() -> String { - remove_whitespace( - r#" - default_certification ( - ValidationArgs { - certification: Certification { - request_certification: RequestCertification { - certified_request_headers: ["Cache-Control"], - certified_query_parameters: ["q"] - }, - response_certification: ResponseCertification { - certified_response_headers: ResponseHeaderList { - headers: ["Cache-Control"] - } - } - } - } - ) - "#, - ) + pub fn full_certification_cel() -> CelExpression<'static> { + DefaultCelBuilder::full_certification() + .with_request_headers(&["Cache-Control"]) + .with_request_query_parameters(&["q"]) + .with_response_certification(DefaultResponseCertification::certified_response_headers( + &["Cache-Control"], + )) + .build() } #[fixture] - pub fn skip_certification_cel() -> String { - remove_whitespace( - r#" - default_certification ( - ValidationArgs { - no_certification: Empty { } - } - ) - "#, - ) + pub fn skip_certification_cel() -> CelExpression<'static> { + DefaultCelBuilder::skip_certification() } pub fn invalid_root_key_certificate() -> (V2Fixture, u128, String) { - let cel_expr = skip_certification_cel(); + let cel_expr = skip_certification_cel().to_string(); let expr_path = ["", "<$>"]; let current_time = get_current_timestamp(); @@ -442,7 +439,7 @@ mod fixtures { } pub fn expired_certificate() -> (V2Fixture, u128, String) { - let cel_expr = skip_certification_cel(); + let cel_expr = skip_certification_cel().to_string(); let expr_path = ["", "<$>"]; let current_time = get_current_timestamp(); @@ -458,7 +455,7 @@ mod fixtures { } pub fn future_certificate() -> (V2Fixture, u128, String) { - let cel_expr = skip_certification_cel(); + let cel_expr = skip_certification_cel().to_string(); let expr_path = ["", "<$>"]; let current_time = get_current_timestamp(); @@ -474,7 +471,7 @@ mod fixtures { } pub fn wrong_canister_certificate() -> (V2Fixture, u128, String) { - let cel_expr = skip_certification_cel(); + let cel_expr = skip_certification_cel().to_string(); let expr_path = ["", "<$>"]; let other_canister_id = CanisterId::from_u64(15); let current_time = get_current_timestamp();