services_hardening_safe_artifact_io_decision.rs (11306B)
1 #![forbid(unsafe_code)] 2 3 use serde_json::Value; 4 5 const DECISION: &str = include_str!( 6 "../../../contracts/architecture/decisions/services_hardening_safe_artifact_io.v1.json" 7 ); 8 9 fn decision() -> Value { 10 serde_json::from_str(DECISION).expect("safe-artifact-I/O decision must be valid JSON") 11 } 12 13 #[test] 14 fn scope_platform_and_hard_maximums_are_exact() { 15 let value = decision(); 16 assert_eq!(value.as_object().map(serde_json::Map::len), Some(16)); 17 assert_eq!( 18 value["schema"], 19 "radroots.services-hardening.safe-artifact-io-decisions.v1" 20 ); 21 assert_eq!(value["contract_version"], 1); 22 assert_eq!(value["decision_state"], "active"); 23 assert_eq!(value["owner"], "tools/xtask"); 24 assert_eq!( 25 value["self_test_command"], 26 "cargo xtask safe-artifact-io-self-test" 27 ); 28 assert_eq!( 29 value["platform_scope"], 30 serde_json::json!({ 31 "implementation": ["macos_aarch64", "linux_x86_64"], 32 "source_gate": ["macos_aarch64"], 33 "cross_platform_promotion_owner": "step-297" 34 }) 35 ); 36 assert_eq!( 37 value["scope"], 38 serde_json::json!({ 39 "claims": [ 40 "descriptor_bound_regular_file_io", 41 "bounded_exact_directory_snapshots", 42 "generic_parse_only_tar_gzip_admission", 43 "service_release_artifacts_adoption", 44 "result_level_release_preflight_aggregation" 45 ], 46 "nonclaims": [ 47 "semantic_oci_or_binary_archive_validation_before_step_304", 48 "transitive_safe_io_inside_every_preflight_lane", 49 "panic_signal_hang_or_abort_containment_without_step_292_process_isolation", 50 "cross_platform_runtime_promotion_before_step_297" 51 ] 52 }) 53 ); 54 assert_eq!( 55 value["hard_maximums"], 56 serde_json::json!({ 57 "buffered_read_bytes": 67_108_864, 58 "streaming_file_bytes": 17_179_869_184_u64, 59 "traversal_entries": 65_536, 60 "traversal_files": 65_536, 61 "traversal_total_bytes": 68_719_476_736_u64, 62 "traversal_depth": 64, 63 "path_bytes": 4_096, 64 "archive_compressed_bytes": 2_147_483_648_u64, 65 "archive_expanded_bytes": 17_179_869_184_u64, 66 "archive_members": 65_536, 67 "archive_member_bytes": 17_179_869_184_u64, 68 "archive_payload_bytes": 17_179_869_184_u64 69 }) 70 ); 71 } 72 73 #[test] 74 fn filesystem_traversal_copy_and_archive_models_are_exact() { 75 let value = decision(); 76 assert_eq!( 77 value["filesystem_model"], 78 serde_json::json!({ 79 "absolute_root_walk": "open_from_filesystem_root_one_component_at_a_time", 80 "open_flags": ["O_CLOEXEC", "O_NOFOLLOW", "O_NONBLOCK"], 81 "intermediate_type": "directory", 82 "leaf_type": "regular_file", 83 "regular_link_count": "exactly_one", 84 "absolute_ancestor_identity": "device_inode_and_directory_type", 85 "regular_identity": "device_inode_type_mode_owner_link_count_length_mtime_ctime", 86 "permission_mode": "mode_bits_0o7777_including_special_bits", 87 "read_completion": "observed_bytes_equal_admitted_length", 88 "post_operation_binding": "reopen_complete_path_and_compare_ancestor_and_leaf_identities", 89 "hardlink_compatibility": "callers_must_privately_materialize_single_link_inputs_before_admission", 90 "trusted_root_normalization": "caller_may_canonicalize_a_trusted_root_before_descriptor_admission", 91 "diagnostic_paths": "redacted" 92 }) 93 ); 94 assert_eq!( 95 value["create_new_materialization"], 96 serde_json::json!({ 97 "output_parent_binding": "descriptor_bound_complete_absolute_chain", 98 "output_chain_security": "every_directory_root_or_effective_uid_owned_and_no_group_or_world_write_mode_bits", 99 "output_chain_acl_precondition": "caller_guarantees_no_write_granting_acl_for_other_credentials_on_every_chain_directory", 100 "output_name": "direct_openat_create_exclusive_nofollow_cloexec", 101 "output_mode": "retained_descriptor_fchmod_0600", 102 "data": "fixed_buffer_stream_and_simultaneous_sha256", 103 "durability": "sync_retained_output_then_sync_parent", 104 "finalization": "capture_full_output_identity_after_write_and_sync_before_post_sync_hook", 105 "post_operation": "compare_retained_and_reopened_output_to_finalized_identity_and_revalidate_parent_chain_and_security", 106 "success_linearization": "final_rebound_and_complete_chain_security_check", 107 "caller_use": "final_revalidate_before_use", 108 "failure": "never_unlink_rename_or_rollback_after_successful_create", 109 "failed_output_trust": "untrusted_caller_owned_residue_requires_quarantine_and_fresh_retry_path", 110 "threat_boundary": "same_effective_credential_or_acl_authorized_mutation_after_final_revalidation_is_outside_strict_pathname_persistence" 111 }) 112 ); 113 assert_eq!( 114 value["traversal"], 115 serde_json::json!({ 116 "entry_count": "every_non_dot_member_encountered_including_excluded_directories", 117 "file_count": "admitted_regular_files", 118 "total_bytes": "checked_sum_of_admitted_regular_lengths", 119 "depth": "root_is_zero_each_descended_directory_adds_one", 120 "path_bytes": "raw_platform_path_bytes_relative_to_root", 121 "ordering": "raw_member_name_bytes_ascending", 122 "special_members": "reject", 123 "excluded_directories": "retain_name_type_and_device_inode_binding_but_do_not_descend", 124 "snapshot_binding": "exact_sorted_membership_and_member_identity_for_root_and_every_descended_directory", 125 "snapshot_consumption": "read_hash_and_copy_require_the_traversed_file_identity", 126 "final_revalidation": "reopen_complete_bindings_and_reenumerate_exact_memberships" 127 }) 128 ); 129 assert_eq!( 130 value["archive_admission"], 131 serde_json::json!({ 132 "format": "one_gzip_member_containing_one_nonempty_tar_stream", 133 "gzip_header": "minimal_ten_byte_header_with_zero_flags_optional_and_reserved_flags_rejected", 134 "operation": "parse_only_never_unpack", 135 "compressed_hash": "sha256_of_every_compressed_input_byte", 136 "tar_iteration": "raw_entries", 137 "tar_termination": "two_or_more_512_byte_zero_blocks", 138 "allowed_members": ["regular_file", "zero_length_directory"], 139 "rejected_members": [ 140 "symlink", 141 "hardlink", 142 "fifo", 143 "character_device", 144 "block_device", 145 "sparse", 146 "pax_extension", 147 "gnu_extension", 148 "unknown_type" 149 ], 150 "paths": "utf8_relative_nonempty_components_no_dot_no_dotdot_no_backslash_no_nul", 151 "regular_trailing_slash": "reject", 152 "duplicates": "reject", 153 "file_directory_prefix_conflicts": "reject", 154 "gzip_trailing_or_concatenated_data": "reject", 155 "member_body": "drain_to_eof_and_require_actual_equals_declared", 156 "semantic_archive_qualification": "deferred_to_step_304" 157 }) 158 ); 159 } 160 161 #[test] 162 fn aggregate_inventory_nonclaims_and_vectors_are_exact() { 163 let value = decision(); 164 assert_eq!( 165 value["aggregate_preflight"], 166 serde_json::json!({ 167 "required_lanes": [ 168 "catalog", 169 "service_source_lock_contract", 170 "service_build_qualification_contract", 171 "service_release_artifacts_contract", 172 "public_native_group", 173 "preview_group", 174 "tools_group", 175 "dto_roots", 176 "protocol_freshness", 177 "artifact_contracts", 178 "release_contracts" 179 ], 180 "execution": "invoke_each_required_lane_exactly_once_in_required_order_after_returned_results", 181 "pass": "exact_order_exact_presence_unique_no_unexpected_and_every_state_pass", 182 "nonpass_states": ["failed", "interrupted", "skipped", "unavailable"], 183 "missing_duplicate_unexpected_or_reordered": "fail_closed", 184 "lane_internal_safety": "owned_by_later_migration_steps_and_step_297", 185 "panic_signal_hang_abort": "not_a_returned_lane_state_requires_step_292_process_isolation" 186 }) 187 ); 188 assert_eq!( 189 value["adoption"], 190 serde_json::json!({ 191 "service_release_artifacts_inputs": "one_exact_flat_snapshot_retained_through_all_snapshot_bound_copies_and_final_revalidation", 192 "service_release_artifacts_output_inventory": "one_exact_flat_snapshot_for_all_hashes_and_final_revalidation", 193 "service_release_artifacts_archives": "generated_binary_and_supplied_oci_tar_gzip_are_generically_admitted", 194 "service_release_artifacts_copy": "descriptor_bound_create_new_no_rollback_copy", 195 "other_preflight_lane_internals": "not_claimed_by_step_293" 196 }) 197 ); 198 assert_eq!( 199 value["diagnostic_safety"], 200 serde_json::json!({ 201 "paths_and_member_names": "redacted", 202 "archive_bytes": "redacted", 203 "operating_system_error_text": "redacted", 204 "source_error_chain": "absent" 205 }) 206 ); 207 assert_eq!( 208 value["required_vectors"], 209 serde_json::json!([ 210 "buffered_read_cap", 211 "streaming_file_cap", 212 "traversal_entry_cap", 213 "traversal_file_cap", 214 "traversal_total_byte_cap", 215 "traversal_file_byte_cap", 216 "traversal_depth_cap", 217 "traversal_path_cap", 218 "symlink", 219 "fifo", 220 "hardlink", 221 "leaf_replacement", 222 "in_place_mutation", 223 "parent_replacement", 224 "late_member_insertion", 225 "excluded_directory_replacement", 226 "artifact_special_mode", 227 "create_new_success_0600", 228 "create_new_untrusted_error_residue", 229 "output_name_swap_preserves_replacement", 230 "output_post_sync_mutation", 231 "output_chain_security", 232 "archive_compressed_cap", 233 "archive_expanded_cap", 234 "archive_member_count_cap", 235 "archive_member_byte_cap", 236 "archive_payload_cap", 237 "archive_depth_cap", 238 "archive_path_cap", 239 "archive_special_member", 240 "archive_escape", 241 "archive_duplicate", 242 "archive_prefix_conflict", 243 "archive_truncated", 244 "archive_concatenated", 245 "archive_malformed", 246 "gzip_optional_or_reserved_header", 247 "aggregate_missing", 248 "aggregate_failed", 249 "aggregate_interrupted", 250 "aggregate_skipped", 251 "aggregate_unavailable", 252 "aggregate_duplicate", 253 "aggregate_unexpected", 254 "aggregate_reordered", 255 "redaction" 256 ]) 257 ); 258 }