lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

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 }