services_hardening_advisory_snapshot_decision.rs (11651B)
1 #![forbid(unsafe_code)] 2 3 use serde_json::Value; 4 use sha2::{Digest as _, Sha256}; 5 6 const DECISION: &str = include_str!( 7 "../../../contracts/architecture/decisions/services_hardening_advisory_snapshots.v1.json" 8 ); 9 const DECISION_SHA256: &str = "0eb19c747a160728f256329a36bd4000deca0fb433d4971f4472f6f792905212"; 10 11 fn decision() -> Value { 12 serde_json::from_str(DECISION).expect("advisory-snapshot decision must be valid JSON") 13 } 14 15 #[test] 16 fn authority_scope_and_complete_file_are_exact() { 17 let value = decision(); 18 assert_eq!(value.as_object().map(serde_json::Map::len), Some(19)); 19 assert_eq!( 20 hex::encode(Sha256::digest(DECISION.as_bytes())), 21 DECISION_SHA256 22 ); 23 assert_eq!( 24 value["schema"], 25 "radroots.services-hardening.advisory-snapshot-decisions.v1" 26 ); 27 assert_eq!(value["contract_version"], 1); 28 assert_eq!(value["decision_state"], "active"); 29 assert_eq!(value["owner"], "tools/xtask"); 30 assert_eq!( 31 value["self_test_command"], 32 "cargo xtask advisory-snapshot-self-test" 33 ); 34 assert_eq!( 35 value["platform_scope"], 36 serde_json::json!({ 37 "implementation": ["macos_aarch64"], 38 "source_gate": ["macos_aarch64"], 39 "cross_platform_promotion_owner": "step-297" 40 }) 41 ); 42 assert_eq!( 43 value["scope"], 44 serde_json::json!({ 45 "claims": [ 46 "closed_advisory_workload_inventory", 47 "exact_step_287_tool_acquisition_binding", 48 "one_external_immutable_rustsec_snapshot_per_candidate", 49 "one_bounded_nvd_only_update_per_candidate", 50 "offline_no_update_analysis", 51 "digest_time_freshness", 52 "owned_exact_expiring_suppressions", 53 "descriptor_admitted_actual_scanner_outputs", 54 "deterministic_byte_canonical_snapshot_archives" 55 ], 56 "nonclaims": [ 57 "live_rustsec_snapshot_acquisition", 58 "live_nvd_update_or_provider_access_at_step_294", 59 "scanner_or_provider_current_availability_before_step_297", 60 "candidate_or_generation_instance_created", 61 "step_314_advisory_qualification_or_pass_evidence", 62 "oss_index_or_credential_authority", 63 "cross_platform_runtime_promotion_before_step_297", 64 "artifact_nix_oci_signing_notarization_publication_deployment_or_production_qualification" 65 ] 66 }) 67 ); 68 } 69 70 #[test] 71 fn workload_inventory_and_tool_pins_are_exact() { 72 let value = decision(); 73 let cargo_ids = value["workload_inventory"]["cargo"] 74 .as_array() 75 .expect("Cargo inventory") 76 .iter() 77 .map(|row| row["id"].as_str().expect("Cargo workload id")) 78 .collect::<Vec<_>>(); 79 assert_eq!( 80 cargo_ids, 81 [ 82 "cli", 83 "harvest_core", 84 "harvest_xtask", 85 "ios_ffi", 86 "lib", 87 "myc", 88 "radrootsd", 89 "rhi", 90 "root", 91 "sdk" 92 ] 93 ); 94 let gradle_rows = value["workload_inventory"]["gradle_kotlin"] 95 .as_array() 96 .expect("Gradle inventory"); 97 assert_eq!( 98 gradle_rows 99 .iter() 100 .map(|row| ( 101 row["id"].as_str().expect("Gradle workload id"), 102 row["build_root"].as_str().expect("Gradle build root"), 103 row["project_path"].as_str().expect("Gradle project path"), 104 row["configuration"].as_str().expect("Gradle configuration") 105 )) 106 .collect::<Vec<_>>(), 107 [ 108 ( 109 "app_design_system", 110 ".", 111 ":app:design_system", 112 "desktopRuntimeClasspath" 113 ), 114 ("app_desktop", ".", ":app:desktop", "runtimeClasspath"), 115 ("app_shared", ".", ":app:shared", "desktopRuntimeClasspath"), 116 ( 117 "tools_design_catalog", 118 ".", 119 ":tools:design_catalog", 120 "desktopRuntimeClasspath" 121 ), 122 ( 123 "build_logic_contracts", 124 "build-logic", 125 ":contracts", 126 "runtimeClasspath" 127 ), 128 ( 129 "build_logic_plugins", 130 "build-logic", 131 ":plugins", 132 "runtimeClasspath" 133 ) 134 ] 135 ); 136 for (id, version, executable, source) in [ 137 ( 138 "cargo_audit", 139 "0.22.1", 140 "c5cd7c0da8a9d0dff338aa1a2a30b0c723fde8201c23481f49e75be0bb77fe74", 141 "2f4e27b0ab2d116c87c29db159ad42565cdcdccf77eb62ef0486ddd017a02da6", 142 ), 143 ( 144 "owasp_dependency_check", 145 "12.2.2", 146 "d683a49ec335eeca93d8707f3e8ce21d7ba63a1e619a325c6518f89c25efcdc4", 147 "bf07fefd81af3094c5f6850423b014df44db62ce2dbad0f79079a90df675e44a", 148 ), 149 ( 150 "java", 151 "21.0.12.1", 152 "9be1d0a740ff6502df1a762145e62860f5de4b7e17658d9cb9498da3acf9d16c", 153 "575bb8d9d604821d8f350325b28a35e49bcffd7ec33727b41edc8d709537dada", 154 ), 155 ( 156 "gradle_wrapper", 157 "9.5.0", 158 "ab5c0cad16305af2e619c159c1f58dd68d07fab9c11e36701e109c0277407f7a", 159 "553c78f50dafcd54d65b9a444649057857469edf836431389695608536d6b746", 160 ), 161 ] { 162 let row = &value["tool_acquisition"][id]["platform"][0]; 163 assert_eq!(row["normalized_version"], version); 164 assert_eq!( 165 row["package_receipt_projection"]["selected_executable"]["exact_bytes_sha256"], 166 executable 167 ); 168 assert_eq!(row["source_sha256"], source); 169 } 170 } 171 172 #[test] 173 fn immutable_archives_reports_and_execution_bounds_are_exact() { 174 let value = decision(); 175 assert_eq!( 176 value["snapshot_model"]["archive_reencoding_backend"], 177 serde_json::json!({ 178 "compression_level": 6, 179 "flate2": "1.1.9", 180 "flate2_checksum": "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c", 181 "miniz_oxide": "0.8.9", 182 "miniz_oxide_checksum": "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316", 183 "golden_fixture_byte_length": 147, 184 "golden_fixture_sha256": "6073c70a98ff9ab610085ed74b7886811da3fe64e9ec2771464300a94befd0fb" 185 }) 186 ); 187 assert_eq!( 188 value["owasp_dependency_check_nvd"]["report_template_binding"], 189 serde_json::json!({ 190 "distribution_package_receipt_sha256": "83d9a668179fb5e9ad6fd5c625a9ab58b8371a6d32b59808fc8d2973957f54a2", 191 "dependency_check_core_jar_relative_identity": "dependency-check-core-12.2.2.jar", 192 "dependency_check_core_jar_sha256": "1ebe55e542b2f4d2727380395843922381447dc8b9a4f1633e77096f47ebfb48", 193 "template_member": "templates/jsonReport.vsl", 194 "template_member_byte_length": 34639, 195 "template_member_sha256": "35881409102304d5a91d08566c1d0fda6fa5f9ec8810994fcdd5106aed3729b5", 196 "parser": "strict_exact_schema_for_report_scan_project_dependency_evidence_identifier_related_dependency_vulnerability_cvss_reference_and_vulnerable_software_objects_with_unknown_fields_rejected" 197 }) 198 ); 199 assert_eq!( 200 value["bounded_execution"], 201 serde_json::json!({ 202 "runner": "step_292_bounded_process_runner", 203 "environment": "replace_with_explicit_allowlist", 204 "stdin": "closed", 205 "deadline_clock": "monotonic", 206 "maximum_concurrent_top_level_operations": 1, 207 "required_process_receipts": 23, 208 "maximum_command_seconds": 3600, 209 "maximum_stdout_bytes": 67108864, 210 "maximum_stderr_bytes": 67108864, 211 "timeout_unavailable_interrupted_or_unparseable": "nonpass_without_fallback", 212 "exit_status_interpretation": "provider_specific_and_never_substitutes_for_complete_report_parsing", 213 "working_directories": "unique_opaque_identity_per_operation_with_kind_logical_uri_pre_and_post_entry_counts_and_tree_digests_cargo_audit_empty_config_free_gradle_retained_candidate_source_nvd_update_fresh_then_nonempty_and_odc_analysis_fresh_empty", 214 "environment_evidence": "exact_ordered_name_and_logical_role_allowlist_with_sha256_of_each_actual_replacement_value_fixed_C_and_UTC_values_shared_pinned_java_and_path_values_and_unique_private_roots_no_ambient_inheritance", 215 "authority_byte_limits": { 216 "snapshot_manifest": 67108864, 217 "producer_request": 1048576, 218 "tool_manifest": 16777216, 219 "tool_observation": 4194304, 220 "provider_execution_evidence": 67108864, 221 "raw_scanner_output_each": 33554432 222 } 223 }) 224 ); 225 } 226 227 #[test] 228 fn freshness_suppressions_results_and_required_vectors_are_exact() { 229 let value = decision(); 230 assert_eq!( 231 value["freshness"], 232 serde_json::json!({ 233 "clock": "caller_supplied_positive_utc_epoch_seconds", 234 "snapshot_time": "provider_digest_finalization_epoch_bound_to_the_accepted_snapshot_digest", 235 "future_snapshot_time": "reject", 236 "age_arithmetic": "checked_nonnegative_seconds", 237 "qualification_maximum_age_seconds": 86400, 238 "development_reuse_maximum_age_seconds": 604800, 239 "boundary": "fresh_when_age_is_less_than_or_equal_to_the_selected_maximum", 240 "providers_evaluated_independently": true, 241 "stale_or_missing_provider": "nonpass" 242 }) 243 ); 244 assert_eq!( 245 value["suppressions"], 246 serde_json::json!({ 247 "inventory": "canonical_ordered_exact_rows_bound_into_the_candidate_advisory_input_digest", 248 "required_fields": [ 249 "id", "provider", "advisory_id", "workload_id", "package_ecosystem", 250 "package_namespace", "package_name", "package_version", "owner", "rationale", 251 "created_at_epoch", "expires_at_epoch" 252 ], 253 "matching": "one_row_matches_one_finding_only_when_provider_advisory_id_workload_id_package_ecosystem_package_namespace_package_name_and_package_version_are_all_exact", 254 "wildcards_ranges_regex_and_lexical_patterns": "forbidden", 255 "owner_and_rationale": "nonempty_bounded_safe_text", 256 "expiry": "active_only_when_evaluation_epoch_is_strictly_less_than_expires_at_epoch", 257 "expiry_equality": "expired", 258 "future_creation_time": "reject", 259 "unused_duplicate_ambiguous_or_unknown_rows": "nonpass", 260 "non_waivable_advisory_ids": ["RUSTSEC-2026-0253"], 261 "non_waivable_match": "reject_even_when_a_suppression_row_is_otherwise_exact" 262 }) 263 ); 264 assert_eq!( 265 value["required_vectors"], 266 serde_json::json!([ 267 "known_vulnerable_fixture", 268 "missing_inventory", 269 "unavailable", 270 "stale", 271 "timeout", 272 "expired_suppression" 273 ]) 274 ); 275 assert_eq!( 276 value["result_model"]["pass"], 277 "both_provider_snapshots_fresh_all_sixteen_workloads_present_in_exact_order_all_scans_completed_and_every_finding_is_absent_or_exactly_suppressed" 278 ); 279 assert_eq!(value["result_model"]["unavailable"], "nonpass"); 280 assert_eq!(value["result_model"]["timeout"], "nonpass"); 281 assert_eq!(value["result_model"]["stale"], "nonpass"); 282 }