110 lines
5.1 KiB
Python
110 lines
5.1 KiB
Python
#!/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" / "ci.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, ("required-gate",))
|
|
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()
|