#[must_use]
pub fn canonical_family_mask(family: &str) -> Option<u32> {
CANONICAL_BITS
.iter()
.find_map(|(name, bit)| (*name == family).then_some(*bit))
}
pub fn resolve_label_family_mask(family: &str) -> Result<u32, String> {
canonical_family_mask(family).ok_or_else(|| {
format!(
"label family `@{family}` has no canonical bit allocation. \
Fix: declare the family in vyre_libs::security::family_mask::CANONICAL_BITS."
)
})
}
pub const ALLOCATOR: u32 = 1 << 0;
pub const RECEIVE: u32 = 1 << 1;
pub const OVERFLOW_CHECK: u32 = 1 << 2;
pub const SANITIZER: u32 = 1 << 3;
pub const SOURCE_NETWORK: u32 = 1 << 4;
pub const SINK_FILESYSTEM: u32 = 1 << 5;
pub const SINK_PROCESS: u32 = 1 << 6;
pub const COPY_TO_USER: u32 = 1 << 7;
pub const COPY_FROM_USER: u32 = 1 << 15;
pub const FREE: u32 = 1 << 8;
pub const COMPARISON_OP: u32 = 1 << 9;
pub const DECODE: u32 = 1 << 10;
pub const INFLATE: u32 = 1 << 11;
pub const TYPE_CAST_UNCHECKED: u32 = 1 << 12;
pub const PRIVILEGE_CHECK: u32 = 1 << 13;
pub const PRIVILEGE_USE: u32 = 1 << 14;
pub const GETS_LAUNCH: u32 = 1 << 24;
pub const PRINTF_LAUNCH: u32 = 1 << 25;
pub const UNBOUNDED_COPY_LAUNCH: u32 = 1 << 26;
pub const UNBOUNDED_SPRINTF_LAUNCH: u32 = 1 << 27;
pub const POINTER_USE_LAUNCH: u32 = 1 << 28;
pub const TYPE_TAG_CHECK_LAUNCH: u32 = 1 << 29;
pub const REASSIGN_NULL_AFTER_FREE_LAUNCH: u32 = 1 << 30;
pub const BOUNDED_COPY_OR_LENGTH_CHECK_LAUNCH: u32 = 1 << 31;
pub const CANONICAL_BITS: &[(&str, u32)] = &[
("allocator_family", ALLOCATOR),
("deallocator_family", FREE),
("free_family", FREE),
("buffer_source_family", RECEIVE),
("buffer_source", RECEIVE),
("user_input_family", RECEIVE),
("untrusted_input_family", RECEIVE),
("receive_family", RECEIVE),
("untrusted_input", RECEIVE),
("overflow_check_family", OVERFLOW_CHECK),
("range_check_family", OVERFLOW_CHECK),
("length_clamp_family", OVERFLOW_CHECK),
("bounded_check_family", OVERFLOW_CHECK),
("checked_arith_or_size_clamp", OVERFLOW_CHECK),
("sanitizer_family", SANITIZER),
("html_escape_family", SANITIZER),
("shell_escape_family", SANITIZER),
("sql_escape_family", SANITIZER),
("url_validation_family", SANITIZER),
("auth_check_family", SANITIZER),
("authz_check_family", SANITIZER),
("password_check_family", SANITIZER),
("comparison_family", SANITIZER),
("prefix_guard_family", SANITIZER),
("pathname_sanitize_family", SANITIZER),
("path_canonicalize_family", SANITIZER),
("regex_safety_family", SANITIZER),
("crlf_sanitizer_family", SANITIZER),
("proto_key_sanitizer_family", SANITIZER),
("verify_arg_slot", SANITIZER),
("bounded_by_array_capacity", SANITIZER),
("bounded_sprintf_or_length_check", SANITIZER),
("network_input_source", SOURCE_NETWORK),
("http_input_family", SOURCE_NETWORK),
("http_client_family", SOURCE_NETWORK),
("file_sink", SINK_FILESYSTEM),
("file_open_family", SINK_FILESYSTEM),
("filesystem_open_family", SINK_FILESYSTEM),
("exec_family", SINK_PROCESS),
("exec_sink", SINK_PROCESS),
("copy_to_user_family", COPY_TO_USER),
("comparison_op_family", COMPARISON_OP),
("decoder_family", DECODE),
("decompressor_family", DECODE),
("inflate_family", INFLATE),
("decompression_cap_family", INFLATE),
("narrow_cast_family", TYPE_CAST_UNCHECKED),
("narrowing_cast_family", TYPE_CAST_UNCHECKED),
("pointer_cast_family", TYPE_CAST_UNCHECKED),
("type_cast_unchecked_family", TYPE_CAST_UNCHECKED),
("privilege_check_family", PRIVILEGE_CHECK),
("privilege_use_family", PRIVILEGE_USE),
("privileged_op_family", PRIVILEGE_USE),
("sized_input_read_family", RECEIVE),
("sized_memory_copy_family", UNBOUNDED_COPY_LAUNCH),
("reallocator_family", ALLOCATOR),
("null_check_family", SANITIZER),
("pointer_assignment_family", ALLOCATOR),
("gets_family", GETS_LAUNCH),
("gets", GETS_LAUNCH),
("printf_family", PRINTF_LAUNCH),
("printf", PRINTF_LAUNCH),
("unbounded_string_copy_family", UNBOUNDED_COPY_LAUNCH),
("unbounded_string_copy", UNBOUNDED_COPY_LAUNCH),
("unbounded_sprintf_family", UNBOUNDED_SPRINTF_LAUNCH),
("unbounded_sprintf", UNBOUNDED_SPRINTF_LAUNCH),
("pointer_use_family", POINTER_USE_LAUNCH),
("type_tag_check", TYPE_TAG_CHECK_LAUNCH),
("reassign_or_null_after_free", REASSIGN_NULL_AFTER_FREE_LAUNCH),
("bounded_copy_or_length_check", BOUNDED_COPY_OR_LENGTH_CHECK_LAUNCH),
("allocator", ALLOCATOR),
("deallocator", FREE),
("worker_family", SINK_PROCESS),
("channel_family", RECEIVE),
("cleanup_family", FREE),
("copy_from_user_family", COPY_FROM_USER),
("aes_cipher_family", DECODE),
("alg_none_patterns", COMPARISON_OP),
("archive_entry_family", DECODE),
("array_access_family", OVERFLOW_CHECK),
("array_index_family", OVERFLOW_CHECK),
("authentication_success_family", PRIVILEGE_CHECK),
("binary_magic_family", COMPARISON_OP),
("buffer_write_family", OVERFLOW_CHECK),
("canonicalize_family", SANITIZER),
("cipher_init_family", DECODE),
("cli_arg_family", RECEIVE),
("cli_source", RECEIVE),
("credential_source", RECEIVE),
("credential_source_family", RECEIVE),
("crypto_param_family", DECODE),
("csrf_token_family", PRIVILEGE_CHECK),
("db_fetch_family", RECEIVE),
("deserializer_family", DECODE),
("ecb_mode_literal", COMPARISON_OP),
("ecdsa_sign_family", DECODE),
("encrypt_family", DECODE),
("eval_family", SINK_PROCESS),
("external_entity_family", DECODE),
("false_literal", COMPARISON_OP),
("family", COMPARISON_OP),
("file_source", RECEIVE),
("filesystem_read_family", RECEIVE),
("filesystem_stat_family", RECEIVE),
("filesystem_unlink_family", SINK_FILESYSTEM),
("form_render_family", SANITIZER),
("html_render_family", SANITIZER),
("http_route_handler_family", RECEIVE),
("id_param_family", RECEIVE),
("index_mask_family", OVERFLOW_CHECK),
("insecure_context_literal", COMPARISON_OP),
("ioctl_ptr_extract_family", RECEIVE),
("ioctl_size_extract_family", RECEIVE),
("jwt_decode_family", DECODE),
("jwt_secure_alg", COMPARISON_OP),
("kdf_family", DECODE),
("key_material_family", DECODE),
("length_extractor_family", OVERFLOW_CHECK),
("length_reconciliation_family", OVERFLOW_CHECK),
("llm_prompt_family", RECEIVE),
("log_family", SINK_FILESYSTEM),
("mac_family", DECODE),
("memory_map_exec_family", SINK_PROCESS),
("min_kdf_iterations", COMPARISON_OP),
("network_sink", SOURCE_NETWORK),
("npm_script_source", RECEIVE),
("null_out_family", SANITIZER),
("object_merge_family", SANITIZER),
("outbound_http_family", SOURCE_NETWORK),
("packer_magic_literal", COMPARISON_OP),
("password_input_family", RECEIVE),
("pickle_load_family", DECODE),
("pointer_move_family", ALLOCATOR),
("reassign_family", ALLOCATOR),
("recursive_call_family", RECEIVE),
("redirect_sink_family", SOURCE_NETWORK),
("regex_compile_family", SANITIZER),
("rsa_pkcs1v15_family", DECODE),
("rust_DA_family", SANITIZER),
("rust_safe_drop_family", FREE),
("rust_safe_indexing_family", OVERFLOW_CHECK),
("schema_validation_family", SANITIZER),
("sensitive_file_source", RECEIVE),
("session_regenerate_family", PRIVILEGE_CHECK),
("session_set_family", PRIVILEGE_CHECK),
("shared_access_family", RECEIVE),
("shell_source", RECEIVE),
("speculation_barrier_family", SANITIZER),
("sql_execute_family", SINK_PROCESS),
("sql_query_family", SINK_PROCESS),
("sql_sink", SINK_PROCESS),
("stream_advance_family", RECEIVE),
("sync_primitive_family", RECEIVE),
("system_source", RECEIVE),
("template_helper_family", SANITIZER),
("template_render_family", SANITIZER),
("template_sandbox_family", SANITIZER),
("templating_library_family", SANITIZER),
("tls_client_config_family", DECODE),
("token_generator_family", DECODE),
("total_output_cap_family", OVERFLOW_CHECK),
("training_corpus_family", RECEIVE),
("untrusted_prompt_source_family", RECEIVE),
("weak_hash_family", DECODE),
("weak_rng_family", DECODE),
("xml_entity_disable_family", SANITIZER),
("xss_sink", SANITIZER),
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn canonical_family_mask_returns_explicit_bit() {
assert_eq!(canonical_family_mask("allocator_family"), Some(ALLOCATOR));
assert_eq!(canonical_family_mask("gets_family"), Some(GETS_LAUNCH));
assert_eq!(canonical_family_mask("not_a_family"), None);
}
#[test]
fn resolve_errors_for_unknown_family() {
let err = resolve_label_family_mask("unregistered").expect_err("must error");
assert!(err.contains("unregistered"));
assert!(err.contains("Fix:"));
}
#[test]
fn launch_bits_are_unique() {
let launch = [
GETS_LAUNCH,
PRINTF_LAUNCH,
UNBOUNDED_COPY_LAUNCH,
UNBOUNDED_SPRINTF_LAUNCH,
POINTER_USE_LAUNCH,
TYPE_TAG_CHECK_LAUNCH,
REASSIGN_NULL_AFTER_FREE_LAUNCH,
BOUNDED_COPY_OR_LENGTH_CHECK_LAUNCH,
];
let mut seen = 0u32;
for &b in &launch {
assert_eq!(seen & b, 0, "launch bit collision at 0x{b:08x}");
seen |= b;
}
}
}