mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-11-13 17:58:18 +00:00
feat(access-control-prover): decode installed fact authority
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
"""Nested value codecs for the installed fact-authority wire boundary."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from collections.abc import Callable, Mapping
|
||||
from dataclasses import fields, is_dataclass
|
||||
from enum import Enum
|
||||
from typing import TypeVar, cast
|
||||
|
||||
from openultrasast.core import canonical, provenance, semantics
|
||||
from openultrasast.core.factpack import (
|
||||
IndependenceAttestation,
|
||||
KnowledgeEvidence,
|
||||
KnowledgeEvidenceKind,
|
||||
RelationEffect,
|
||||
ReviewIndependencePolicy,
|
||||
SemanticFact,
|
||||
StructuralRequirement,
|
||||
StructuralRequirementKind,
|
||||
)
|
||||
|
||||
_T = TypeVar("_T")
|
||||
_REF_FIELDS = ("artifact_id", "content_digest", "media_type", "byte_length", "retention_policy_digest", "storage_receipt_digest")
|
||||
_ATTESTATION_FIELDS = ("subject_digest", "producer_identity", "reviewer_identity", "conflict_disclosure", "attestation_digest")
|
||||
|
||||
|
||||
class InstalledAuthorityWireError(ValueError):
|
||||
"""Installed authority bytes do not satisfy the closed wire contract."""
|
||||
|
||||
|
||||
def wire_value(value: object) -> canonical.JsonValue:
|
||||
"""Project dataclasses, enums, and parsed JSON into canonical values."""
|
||||
if isinstance(value, Enum):
|
||||
return cast(canonical.JsonValue, value.value)
|
||||
if is_dataclass(value) and not isinstance(value, type):
|
||||
return {item.name: wire_value(getattr(value, item.name)) for item in fields(value)}
|
||||
if isinstance(value, Mapping):
|
||||
if not all(isinstance(key, str) for key in value):
|
||||
raise InstalledAuthorityWireError("JSON object keys must be text")
|
||||
return {cast(str, key): wire_value(item) for key, item in value.items()}
|
||||
if isinstance(value, (tuple, list)):
|
||||
return tuple(wire_value(item) for item in value)
|
||||
if value is None or isinstance(value, (str, bool, int, float)):
|
||||
return value
|
||||
raise InstalledAuthorityWireError(f"unsupported wire value: {type(value).__name__}")
|
||||
|
||||
|
||||
def closed_object(value: object, expected: tuple[str, ...], label: str) -> dict[str, object]:
|
||||
"""Require a JSON object with exactly the declared field set."""
|
||||
if not isinstance(value, dict) or not all(isinstance(key, str) for key in value):
|
||||
raise InstalledAuthorityWireError(f"{label} must be an object")
|
||||
record = cast(dict[str, object], value)
|
||||
missing = set(expected) - set(record)
|
||||
unknown = set(record) - set(expected)
|
||||
if missing or unknown:
|
||||
details = [f"missing {item}" for item in sorted(missing)]
|
||||
details.extend(f"unknown {item}" for item in sorted(unknown))
|
||||
raise InstalledAuthorityWireError(f"{label} has {', '.join(details)}")
|
||||
return record
|
||||
|
||||
|
||||
def items(value: object, label: str) -> tuple[object, ...]:
|
||||
if not isinstance(value, list):
|
||||
raise InstalledAuthorityWireError(f"{label} must be an array")
|
||||
return tuple(value)
|
||||
|
||||
|
||||
def text(value: object, label: str) -> str:
|
||||
if not isinstance(value, str) or not value:
|
||||
raise InstalledAuthorityWireError(f"{label} must be nonempty text")
|
||||
return value
|
||||
|
||||
|
||||
def integer(value: object, label: str) -> int:
|
||||
if isinstance(value, bool) or not isinstance(value, int):
|
||||
raise InstalledAuthorityWireError(f"{label} must be an integer")
|
||||
return value
|
||||
|
||||
|
||||
def boolean(value: object, label: str) -> bool:
|
||||
if not isinstance(value, bool):
|
||||
raise InstalledAuthorityWireError(f"{label} must be boolean")
|
||||
return value
|
||||
|
||||
|
||||
def strings(value: object, label: str) -> tuple[str, ...]:
|
||||
return tuple(text(item, label) for item in items(value, label))
|
||||
|
||||
|
||||
def pairs(value: object, label: str) -> tuple[tuple[str, str], ...]:
|
||||
decoded: list[tuple[str, str]] = []
|
||||
for item in items(value, label):
|
||||
pair = items(item, label)
|
||||
if len(pair) != 2:
|
||||
raise InstalledAuthorityWireError(f"{label} entries must be pairs")
|
||||
decoded.append((text(pair[0], label), text(pair[1], label)))
|
||||
return tuple(decoded)
|
||||
|
||||
|
||||
def parse_record(content: bytes, schema: str, expected: tuple[str, ...], label: str) -> dict[str, object]:
|
||||
"""Parse canonical JSON and enforce its top-level schema and fields."""
|
||||
if not isinstance(content, bytes):
|
||||
raise InstalledAuthorityWireError(f"{label} content must be bytes")
|
||||
try:
|
||||
raw: object = json.loads(content)
|
||||
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
|
||||
raise InstalledAuthorityWireError(f"{label} is not valid JSON") from exc
|
||||
record = closed_object(raw, expected, label)
|
||||
if record["schema_version"] != schema:
|
||||
raise InstalledAuthorityWireError(f"unknown {label} schema")
|
||||
if canonical.canonical_bytes(wire_value(record)) != content:
|
||||
raise InstalledAuthorityWireError(f"{label} bytes are not canonical")
|
||||
return record
|
||||
|
||||
|
||||
def decode_value(label: str, build: Callable[[], _T]) -> _T:
|
||||
"""Normalize typed construction failures to the wire error boundary."""
|
||||
try:
|
||||
return build()
|
||||
except InstalledAuthorityWireError:
|
||||
raise
|
||||
except (KeyError, TypeError, ValueError) as exc:
|
||||
raise InstalledAuthorityWireError(f"invalid {label}: {exc}") from exc
|
||||
|
||||
|
||||
def content_ref(value: object) -> canonical.ContentRef:
|
||||
row = closed_object(value, _REF_FIELDS, "content reference")
|
||||
return canonical.ContentRef(
|
||||
text(row["artifact_id"], "artifact_id"),
|
||||
text(row["content_digest"], "content_digest"),
|
||||
text(row["media_type"], "media_type"),
|
||||
integer(row["byte_length"], "byte_length"),
|
||||
text(row["retention_policy_digest"], "retention_policy_digest"),
|
||||
text(row["storage_receipt_digest"], "storage_receipt_digest"),
|
||||
)
|
||||
|
||||
|
||||
def attestation(value: object) -> IndependenceAttestation:
|
||||
row = closed_object(value, _ATTESTATION_FIELDS, "attestation")
|
||||
return IndependenceAttestation(
|
||||
text(row["subject_digest"], "subject_digest"),
|
||||
text(row["producer_identity"], "producer_identity"),
|
||||
text(row["reviewer_identity"], "reviewer_identity"),
|
||||
content_ref(row["conflict_disclosure"]),
|
||||
text(row["attestation_digest"], "attestation_digest"),
|
||||
)
|
||||
|
||||
|
||||
def evidence(value: object) -> KnowledgeEvidence:
|
||||
expected = ("kind", "content", "producer_identity", "reviewer_identity", "independence", "lineage_manifest_digest")
|
||||
row = closed_object(value, expected, "knowledge evidence")
|
||||
return KnowledgeEvidence(
|
||||
KnowledgeEvidenceKind(text(row["kind"], "evidence kind")),
|
||||
content_ref(row["content"]),
|
||||
text(row["producer_identity"], "producer_identity"),
|
||||
text(row["reviewer_identity"], "reviewer_identity"),
|
||||
attestation(row["independence"]),
|
||||
text(row["lineage_manifest_digest"], "lineage_manifest_digest"),
|
||||
)
|
||||
|
||||
|
||||
def requirement(value: object) -> StructuralRequirement:
|
||||
row = closed_object(value, ("requirement_id", "kind", "field", "expected"), "structural requirement")
|
||||
return StructuralRequirement(
|
||||
text(row["requirement_id"], "requirement_id"),
|
||||
StructuralRequirementKind(text(row["kind"], "requirement kind")),
|
||||
text(row["field"], "requirement field"),
|
||||
wire_value(row["expected"]),
|
||||
)
|
||||
|
||||
|
||||
def effect(value: object) -> RelationEffect:
|
||||
row = closed_object(value, ("kind", "role", "source_role", "target_role", "conditions"), "relation effect")
|
||||
return RelationEffect(
|
||||
semantics.RelationKind(text(row["kind"], "relation kind")),
|
||||
text(row["role"], "effect role"),
|
||||
text(row["source_role"], "source role"),
|
||||
text(row["target_role"], "target role"),
|
||||
strings(row["conditions"], "conditions"),
|
||||
)
|
||||
|
||||
|
||||
def fact(value: object) -> SemanticFact:
|
||||
row = closed_object(value, ("fact_id", "role", "structural_requirements", "relation_effects"), "semantic fact")
|
||||
requirements = items(row["structural_requirements"], "structural_requirements")
|
||||
effects = items(row["relation_effects"], "relation_effects")
|
||||
return SemanticFact(
|
||||
text(row["fact_id"], "fact_id"),
|
||||
text(row["role"], "fact role"),
|
||||
tuple(requirement(item) for item in requirements),
|
||||
tuple(effect(item) for item in effects),
|
||||
)
|
||||
|
||||
|
||||
def lineage(value: object) -> provenance.LineageManifest:
|
||||
row = closed_object(value, ("arm_id", "subject_digest", "uses", "manifest_digest"), "lineage")
|
||||
expected = (
|
||||
"purpose",
|
||||
"source_population_digest",
|
||||
"labels_accessed",
|
||||
"influenced_artifact_digest",
|
||||
"population_use_receipt_digest",
|
||||
)
|
||||
uses = tuple(_lineage_use(item, expected) for item in items(row["uses"], "lineage uses"))
|
||||
return provenance.LineageManifest(
|
||||
text(row["arm_id"], "arm_id"),
|
||||
text(row["subject_digest"], "subject_digest"),
|
||||
uses,
|
||||
text(row["manifest_digest"], "manifest_digest"),
|
||||
)
|
||||
|
||||
|
||||
def _lineage_use(value: object, expected: tuple[str, ...]) -> provenance.LineageUse:
|
||||
row = closed_object(value, expected, "lineage use")
|
||||
return provenance.LineageUse(
|
||||
provenance.LineagePurpose(text(row["purpose"], "lineage purpose")),
|
||||
text(row["source_population_digest"], "source population"),
|
||||
boolean(row["labels_accessed"], "labels_accessed"),
|
||||
text(row["influenced_artifact_digest"], "influenced artifact"),
|
||||
text(row["population_use_receipt_digest"], "population receipt"),
|
||||
)
|
||||
|
||||
|
||||
def custody_policy(value: object) -> provenance.PopulationCustodyPolicy:
|
||||
row = closed_object(value, ("reviewed_custodian_digests", "policy_digest"), "custody policy")
|
||||
return provenance.PopulationCustodyPolicy(
|
||||
strings(row["reviewed_custodian_digests"], "custodian digests"),
|
||||
text(row["policy_digest"], "custody policy digest"),
|
||||
)
|
||||
|
||||
|
||||
def review_policy(value: object) -> ReviewIndependencePolicy:
|
||||
expected = (
|
||||
"reviewed_reviewer_digests",
|
||||
"forbidden_identity_pairs",
|
||||
"trusted_store_identities",
|
||||
"maximum_evidence_bytes",
|
||||
"policy_digest",
|
||||
)
|
||||
row = closed_object(value, expected, "review policy")
|
||||
return ReviewIndependencePolicy(
|
||||
strings(row["reviewed_reviewer_digests"], "reviewer digests"),
|
||||
pairs(row["forbidden_identity_pairs"], "forbidden pairs"),
|
||||
strings(row["trusted_store_identities"], "store identities"),
|
||||
integer(row["maximum_evidence_bytes"], "maximum evidence bytes"),
|
||||
text(row["policy_digest"], "review policy digest"),
|
||||
)
|
||||
@@ -0,0 +1,242 @@
|
||||
"""Closed canonical wire format for installed entry-fact authority."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from collections.abc import Mapping
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import cast
|
||||
|
||||
from openultrasast.core import canonical
|
||||
from openultrasast.core.factpack import FACT_PACK_SCHEMA, FactPack
|
||||
from openultrasast.core.family import FamilyContract
|
||||
from openultrasast.mapping._installed_fact_authority_codec import (
|
||||
InstalledAuthorityWireError,
|
||||
attestation,
|
||||
content_ref,
|
||||
custody_policy,
|
||||
decode_value,
|
||||
evidence,
|
||||
fact,
|
||||
integer,
|
||||
items,
|
||||
lineage,
|
||||
pairs,
|
||||
parse_record,
|
||||
review_policy,
|
||||
strings,
|
||||
text,
|
||||
wire_value,
|
||||
)
|
||||
from openultrasast.mapping.fact_authority import (
|
||||
ENTRY_AUTHORITY_BOOTSTRAP_SCHEMA,
|
||||
EntryAuthorityBootstrapPolicy,
|
||||
EntryFactAuthorityBundle,
|
||||
)
|
||||
|
||||
INSTALLED_AUTHORITY_SCHEMA = "openultrasast.access-control.installed-entry-authority.v1"
|
||||
PACK_FILES = ("express.javascript.v2.json", "express.typescript.v2.json")
|
||||
_PACK_LANGUAGES = ("javascript", "typescript")
|
||||
_DIGEST = re.compile(r"[0-9a-f]{64}")
|
||||
_PACK_FIELDS = (
|
||||
"schema_version",
|
||||
"family_contract_digest",
|
||||
"family_id",
|
||||
"ecosystem",
|
||||
"language",
|
||||
"facts",
|
||||
"normalized_content_digest",
|
||||
"review_identity",
|
||||
"review_evidence",
|
||||
"review_evidence_digest",
|
||||
"review_policy_digest",
|
||||
)
|
||||
_BOOTSTRAP_FIELDS = (
|
||||
"schema_version",
|
||||
"reviewed_bundle_reviewer_digests",
|
||||
"forbidden_identity_pairs",
|
||||
"trusted_store_identities",
|
||||
"maximum_bundle_bytes",
|
||||
"policy_digest",
|
||||
)
|
||||
_AUTHORITY_FIELDS = (
|
||||
"schema_version",
|
||||
"pack_files",
|
||||
"recognition_projection",
|
||||
"lineages",
|
||||
"custody_policy",
|
||||
"review_policy",
|
||||
"matcher_revision",
|
||||
"matcher_behavior_digest",
|
||||
"matcher_lineage_manifest_digest",
|
||||
"authority_review",
|
||||
"bundle_digest",
|
||||
)
|
||||
|
||||
|
||||
def _record_bytes(value: object, schema: str) -> bytes:
|
||||
record = cast(Mapping[str, canonical.JsonValue], wire_value(value))
|
||||
return canonical.canonical_record_bytes(record, known_schema_versions=(schema,))
|
||||
|
||||
|
||||
def fact_pack_bytes(value: FactPack) -> bytes:
|
||||
"""Encode every public fact-pack field as canonical JSON."""
|
||||
return _record_bytes(value, FACT_PACK_SCHEMA)
|
||||
|
||||
|
||||
def decode_fact_pack_bytes(content: bytes) -> FactPack:
|
||||
"""Decode only the closed v2 fact-pack representation."""
|
||||
|
||||
def build() -> FactPack:
|
||||
row = parse_record(content, FACT_PACK_SCHEMA, _PACK_FIELDS, "fact pack")
|
||||
value = FactPack(
|
||||
FACT_PACK_SCHEMA,
|
||||
text(row["family_contract_digest"], "family_contract_digest"),
|
||||
text(row["family_id"], "family_id"),
|
||||
text(row["ecosystem"], "ecosystem"),
|
||||
text(row["language"], "language"),
|
||||
tuple(fact(item) for item in items(row["facts"], "facts")),
|
||||
text(row["normalized_content_digest"], "normalized_content_digest"),
|
||||
text(row["review_identity"], "review_identity"),
|
||||
tuple(evidence(item) for item in items(row["review_evidence"], "review_evidence")),
|
||||
text(row["review_evidence_digest"], "review_evidence_digest"),
|
||||
text(row["review_policy_digest"], "review_policy_digest"),
|
||||
)
|
||||
if fact_pack_bytes(value) != content:
|
||||
raise InstalledAuthorityWireError("fact pack does not round-trip canonically")
|
||||
return value
|
||||
|
||||
return decode_value("fact pack", build)
|
||||
|
||||
|
||||
def bootstrap_bytes(value: EntryAuthorityBootstrapPolicy) -> bytes:
|
||||
"""Encode the external bootstrap policy without authority data."""
|
||||
row = cast(dict[str, canonical.JsonValue], wire_value(value))
|
||||
return canonical.canonical_record_bytes(
|
||||
{"schema_version": ENTRY_AUTHORITY_BOOTSTRAP_SCHEMA, **row},
|
||||
known_schema_versions=(ENTRY_AUTHORITY_BOOTSTRAP_SCHEMA,),
|
||||
)
|
||||
|
||||
|
||||
def decode_bootstrap_bytes(content: bytes) -> EntryAuthorityBootstrapPolicy:
|
||||
"""Decode a closed external bootstrap policy."""
|
||||
|
||||
def build() -> EntryAuthorityBootstrapPolicy:
|
||||
row = parse_record(content, ENTRY_AUTHORITY_BOOTSTRAP_SCHEMA, _BOOTSTRAP_FIELDS, "bootstrap")
|
||||
value = EntryAuthorityBootstrapPolicy(
|
||||
strings(row["reviewed_bundle_reviewer_digests"], "bundle reviewers"),
|
||||
pairs(row["forbidden_identity_pairs"], "forbidden pairs"),
|
||||
strings(row["trusted_store_identities"], "store identities"),
|
||||
integer(row["maximum_bundle_bytes"], "maximum bundle bytes"),
|
||||
text(row["policy_digest"], "policy digest"),
|
||||
)
|
||||
if bootstrap_bytes(value) != content:
|
||||
raise InstalledAuthorityWireError("bootstrap does not round-trip canonically")
|
||||
return value
|
||||
|
||||
return decode_value("bootstrap", build)
|
||||
|
||||
|
||||
def _authority_record(value: EntryFactAuthorityBundle) -> dict[str, canonical.JsonValue]:
|
||||
return {
|
||||
"schema_version": INSTALLED_AUTHORITY_SCHEMA,
|
||||
"pack_files": PACK_FILES,
|
||||
"recognition_projection": wire_value(value.recognition_projection),
|
||||
"lineages": wire_value(value.lineages),
|
||||
"custody_policy": wire_value(value.custody_policy),
|
||||
"review_policy": wire_value(value.review_policy),
|
||||
"matcher_revision": value.matcher_revision,
|
||||
"matcher_behavior_digest": value.matcher_behavior_digest,
|
||||
"matcher_lineage_manifest_digest": value.matcher_lineage_manifest_digest,
|
||||
"authority_review": wire_value(value.authority_review),
|
||||
"bundle_digest": value.bundle_digest,
|
||||
}
|
||||
|
||||
|
||||
def installed_authority_bytes(value: EntryFactAuthorityBundle) -> bytes:
|
||||
"""Encode installed authority while leaving bootstrap trust external."""
|
||||
return canonical.canonical_record_bytes(
|
||||
_authority_record(value),
|
||||
known_schema_versions=(INSTALLED_AUTHORITY_SCHEMA,),
|
||||
)
|
||||
|
||||
|
||||
def decode_installed_authority_bytes(
|
||||
content: bytes,
|
||||
packs: tuple[FactPack, ...],
|
||||
contract: FamilyContract,
|
||||
) -> EntryFactAuthorityBundle:
|
||||
"""Decode closed installed authority and attach separately decoded packs."""
|
||||
|
||||
def build() -> EntryFactAuthorityBundle:
|
||||
row = parse_record(content, INSTALLED_AUTHORITY_SCHEMA, _AUTHORITY_FIELDS, "installed authority")
|
||||
if strings(row["pack_files"], "pack_files") != PACK_FILES:
|
||||
raise InstalledAuthorityWireError("pack_files must use the fixed order")
|
||||
if tuple(pack.language for pack in packs) != _PACK_LANGUAGES:
|
||||
raise InstalledAuthorityWireError("packs must use the fixed language order")
|
||||
value = EntryFactAuthorityBundle(
|
||||
contract,
|
||||
packs,
|
||||
content_ref(row["recognition_projection"]),
|
||||
tuple(lineage(item) for item in items(row["lineages"], "lineages")),
|
||||
custody_policy(row["custody_policy"]),
|
||||
review_policy(row["review_policy"]),
|
||||
text(row["matcher_revision"], "matcher_revision"),
|
||||
text(row["matcher_behavior_digest"], "matcher_behavior_digest"),
|
||||
text(row["matcher_lineage_manifest_digest"], "matcher_lineage_manifest_digest"),
|
||||
attestation(row["authority_review"]),
|
||||
text(row["bundle_digest"], "bundle_digest"),
|
||||
)
|
||||
if installed_authority_bytes(value) != content:
|
||||
raise InstalledAuthorityWireError("installed authority does not round-trip canonically")
|
||||
return value
|
||||
|
||||
return decode_value("installed authority", build)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DigestDirectoryResolver:
|
||||
"""Read immutable artifacts by validated content digest only."""
|
||||
|
||||
root: Path
|
||||
store_identity: str
|
||||
|
||||
def resolve(self, reference: canonical.ContentRef) -> canonical.ResolvedContent:
|
||||
if _DIGEST.fullmatch(reference.content_digest) is None:
|
||||
raise canonical.ArtifactResolutionError("content digest is not lowercase SHA-256")
|
||||
path = self.root / "artifacts" / "sha256" / reference.content_digest
|
||||
try:
|
||||
content = path.read_bytes()
|
||||
except OSError as exc:
|
||||
raise canonical.ArtifactResolutionError("digest-addressed artifact is unavailable") from exc
|
||||
if len(content) != reference.byte_length or canonical.bytes_digest(content) != reference.content_digest:
|
||||
raise canonical.ArtifactResolutionError("digest-addressed artifact bytes do not match reference")
|
||||
receipt_digest = canonical.derive_storage_receipt_digest(
|
||||
store_identity=self.store_identity,
|
||||
content_digest=reference.content_digest,
|
||||
byte_length=reference.byte_length,
|
||||
retention_policy_digest=reference.retention_policy_digest,
|
||||
)
|
||||
receipt = canonical.StorageReceipt(
|
||||
self.store_identity,
|
||||
reference.content_digest,
|
||||
reference.byte_length,
|
||||
reference.retention_policy_digest,
|
||||
receipt_digest,
|
||||
)
|
||||
return canonical.ResolvedContent(content, receipt)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"DigestDirectoryResolver",
|
||||
"INSTALLED_AUTHORITY_SCHEMA",
|
||||
"InstalledAuthorityWireError",
|
||||
"PACK_FILES",
|
||||
"bootstrap_bytes",
|
||||
"decode_bootstrap_bytes",
|
||||
"decode_fact_pack_bytes",
|
||||
"decode_installed_authority_bytes",
|
||||
"fact_pack_bytes",
|
||||
"installed_authority_bytes",
|
||||
]
|
||||
Reference in New Issue
Block a user