#!/usr/bin/env python3 """Audit issue 78's pure native RLV protocol ownership and evidence boundary.""" from __future__ import annotations from pathlib import Path import re import generate_api_shims ROOT = Path(__file__).resolve().parents[1] SOURCE = ROOT / "crates" / "libremetaverse-rlv" / "src" / "protocol.rs" GENERATED = ROOT / "crates" / "libremetaverse-rlv" / "src" / "generated.rs" TESTS = ROOT / "crates" / "libremetaverse-rlv" / "tests" / "protocol_parsing.rs" COMPAT = ROOT / "tests" / "compat" / "tests" / "rlv_common_semantics.rs" DOC = ROOT / "crates" / "libremetaverse-rlv" / "README.md" WORKFLOW = ROOT / ".gitea" / "workflows" / "rust-workspace.yml" STUB_RE = re.compile(r"\b(?:not_implemented|unimplemented_api)\b|\b(?:todo|unimplemented)!\s*\(") TYPES = { "T:LibreMetaverse.RLV.RlvCommon": "crate::protocol::RlvCommon", "T:LibreMetaverse.RLV.RlvRestriction": "crate::protocol::RlvRestriction", } MEMBERS = { "M:LibreMetaverse.RLV.RlvCommon.TryGetAttachmentPointFromItemName(System.String,System.Nullable{LibreMetaverse.RLV.RlvAttachmentPoint}@)", "M:LibreMetaverse.RLV.RlvRestriction.#ctor(LibreMetaverse.RLV.RlvRestrictionType,System.Guid,System.String,System.Collections.Generic.ICollection{System.Object})", "M:LibreMetaverse.RLV.RlvRestriction.Equals(System.Object)", "M:LibreMetaverse.RLV.RlvRestriction.GetHashCode", "M:LibreMetaverse.RLV.RlvRestriction.ToString", "P:LibreMetaverse.RLV.RlvRestriction.Args", "P:LibreMetaverse.RLV.RlvRestriction.Behavior", "P:LibreMetaverse.RLV.RlvRestriction.IsException", "P:LibreMetaverse.RLV.RlvRestriction.OriginalBehavior", "P:LibreMetaverse.RLV.RlvRestriction.Sender", "P:LibreMetaverse.RLV.RlvRestriction.SenderName", } def require_markers(path: Path, markers: tuple[str, ...]) -> None: text = path.read_text() missing = [marker for marker in markers if marker not in text] if missing: raise SystemExit(f"{path.name}: audit evidence missing: " + ", ".join(missing)) def generated_type_block(text: str, rust_name: str) -> str: marker = f"pub use crate::protocol::{rust_name};" start = text.find(marker) if start < 0: raise SystemExit(f"generated declaration for {rust_name} is missing") next_type = text.find("\n/// C# type:", start + len(marker)) return text[start:] if next_type < 0 else text[start:next_type] def main() -> None: for api_type, declaration in TYPES.items(): if generate_api_shims.NATIVE_DECLARATIONS.get(api_type) != declaration: raise SystemExit(f"issue 78 native declaration is missing for {api_type}") missing = sorted(MEMBERS - set(generate_api_shims.NATIVE_MEMBER_BODIES)) if missing: raise SystemExit("issue 78 native members missing: " + ", ".join(missing)) source = SOURCE.read_text() if STUB_RE.search(source): raise SystemExit("issue 78 owned Rust stubs remain in protocol.rs") table = source[source.index("macro_rules! restriction_table") : source.index("macro_rules! make_restriction_lookup")] if len(re.findall(r'"[^"]+"\s*=>\s*[A-Za-z0-9_]+', table)) != 119: raise SystemExit("issue 78 restriction table does not contain exactly 119 names") generated = GENERATED.read_text() for rust_name in ("RlvCommon", "RlvRestriction"): if STUB_RE.search(generated_type_block(generated, rust_name)): raise SystemExit(f"issue 78 owned generated stubs remain for {rust_name}") require_markers(SOURCE, ( "MAX_RLV_MESSAGE_BYTES", "MAX_RLV_COMMANDS", "pub struct RlvSourceSpan", "pub enum RlvParseErrorKind", "pub enum RlvDirective", "pub enum RlvAction", "pub enum RlvQuery", "pub enum RlvValue", "pub fn parse_message", "parse_restriction_values", "RLV_RESTRICTION_NAMES", '"root" | "avatar center" => P::AvatarCenter', "native_try_get_attachment_point", "pub struct RlvRestriction", "real_restriction", "is_exception", )) require_markers(TESTS, ( "message_preserves_original_casing_options_and_byte_locations", "query_variants_preserve_filters_separators_paths_and_aliases", "every_pinned_restriction_name_round_trips_without_normalization", "every_pinned_restriction_has_at_least_one_typed_valid_option_form", "deterministic_single_byte_mutations_are_bounded_and_never_panic", "command_count_and_message_size_are_bounded", "mapped_restriction_preserves_alias_exception_equality_and_snapshot_args", )) require_markers(COMPAT, ( "attachment_point_uses_last_known_tag", "attachment_point_avatar_center", "attachment_point_root_alias", "attachment_point_rejects_unknown_tags", )) require_markers(DOC, ( "side-effect-free", "119 behavior", "56 pinned", "half-open byte span", "64 KiB", "128 comma-separated", "does not wait", "ubuntu-latest", )) require_markers(WORKFLOW, ("python3 tools/check_milestone_10_issue_78.py",)) print( "issue 78 audit: bounded pure RLV parsing, typed commands/queries/restrictions, " "exact aliases, positioned errors, mapped values, mutation evidence, and docs are present" ) if __name__ == "__main__": main()