冻结地图1二十分钟纵切版的平衡、证据与金标登记。 新增 ReferenceAsset/2 清单、策略、release、受信快照及 CLI/Service/acceptance provenance /4 消费链;保持 survivor live、R1 签名与部署关闭。
813 lines
32 KiB
Python
813 lines
32 KiB
Python
"""参照资产 v2 的受信 fd 消费门与双身份验证器。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import hashlib
|
||
import json
|
||
import os
|
||
import re
|
||
import unicodedata
|
||
from collections.abc import Mapping, Sequence
|
||
from dataclasses import dataclass
|
||
from pathlib import Path
|
||
from types import MappingProxyType
|
||
|
||
try:
|
||
import artifact_snapshot
|
||
except ImportError: # pragma: no cover - 直接以 cheap-worker 目录为 cwd 时使用绝对导入
|
||
from . import artifact_snapshot
|
||
|
||
|
||
POLICY_ID = "survivor-gold-v1"
|
||
POLICY_RECORD_ID = "gac-shanhai-xingji"
|
||
POLICY_ROLE = "game_content_gold"
|
||
POLICY_CONSUMER_REF = "generation-runtime@reference-assets/2"
|
||
POLICY_MODE = "frozen_preflight"
|
||
VERIFIER_VERSION = "reference-asset-verifier/1.0.0"
|
||
TRUSTED_ROOT_ID = "wanxiang-reference-assets-root-v1"
|
||
SNAPSHOT_HASH_DOMAIN = b"reference-asset-consumption-snapshot/1\n"
|
||
|
||
MAX_MANIFEST_BYTES = 1 * 1024 * 1024
|
||
MAX_MANIFEST_ENTRIES = 512
|
||
MAX_FILE_BYTES = 16 * 1024 * 1024
|
||
MAX_RECORD_BYTES = 64 * 1024 * 1024
|
||
MAX_TOTAL_BYTES = 128 * 1024 * 1024
|
||
|
||
_SHA256_RE = re.compile(r"^[0-9a-f]{64}$")
|
||
_IDENTIFIER_RE = re.compile(r"^[A-Za-z0-9_][A-Za-z0-9._-]*$")
|
||
_VERIFIER_RE = re.compile(r"^[A-Za-z][A-Za-z0-9._-]*/[0-9]+\.[0-9]+\.[0-9]+$")
|
||
_TRUSTED_ROOT_ID_RE = re.compile(r"^[A-Za-z0-9_][A-Za-z0-9._:-]*$")
|
||
_SIGNED_AT_RE = re.compile(
|
||
r"^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2})?(Z|[+-]\d{2}:?\d{2})?)?$"
|
||
)
|
||
|
||
_ALLOWED_ROLES = {
|
||
"harness_fixture",
|
||
"prompt_eval_gold",
|
||
"generation_exemplar",
|
||
"game_content_gold",
|
||
}
|
||
_ALLOWED_LIFECYCLE = {"candidate", "migration_pending", "active", "retired"}
|
||
_RECORD_KEYS = {
|
||
"schemaVersion",
|
||
"recordId",
|
||
"role",
|
||
"lifecycleStatus",
|
||
"assetRef",
|
||
"assetVersion",
|
||
"artifactHash",
|
||
"consumerRef",
|
||
"designRef",
|
||
"evidenceRefs",
|
||
"signedBy",
|
||
"signedAt",
|
||
"artifactRef",
|
||
"consumptionManifestRef",
|
||
"consumptionManifestHash",
|
||
}
|
||
_RELEASE_KEYS = {
|
||
"schemaVersion",
|
||
"releaseId",
|
||
"registryRef",
|
||
"registryHash",
|
||
"policyRef",
|
||
"policyHash",
|
||
"verifierVersion",
|
||
"trustedRootId",
|
||
}
|
||
_POLICY_KEYS = {
|
||
"schemaVersion",
|
||
"policyId",
|
||
"recordId",
|
||
"role",
|
||
"consumerRef",
|
||
"route",
|
||
"autoSelect",
|
||
"mode",
|
||
}
|
||
_MANIFEST_KEYS = {"schemaVersion", "manifestId", "canonicalization", "entries"}
|
||
_MANIFEST_ENTRY_KEYS = {"path", "size", "sha256"}
|
||
|
||
|
||
class ReferenceAssetGateError(ValueError):
|
||
"""稳定的可信消费错误;异常正文只含 code、recordId 和逻辑路径。"""
|
||
|
||
def __init__(self, code: str, record_id: str | None = None, logical_path: str | None = None) -> None:
|
||
self.code = code
|
||
self.record_id = record_id
|
||
self.logical_path = logical_path
|
||
fields = [f"code={code}"]
|
||
if record_id is not None:
|
||
fields.append(f"recordId={record_id}")
|
||
if logical_path is not None:
|
||
fields.append(f"path={logical_path}")
|
||
super().__init__(" ".join(fields))
|
||
|
||
|
||
# 为调用方保留两个直观别名,实际异常类型只有一套稳定字段。
|
||
ReferenceAssetError = ReferenceAssetGateError
|
||
ReferenceAssetVerificationError = ReferenceAssetGateError
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class VerifiedReferenceAssets:
|
||
"""验证成功后的只读消费闭包;失败时不会构造或返回该对象。"""
|
||
|
||
constraint_records: tuple[Mapping[str, object], ...]
|
||
files: Mapping[str, bytes]
|
||
receipts: tuple[Mapping[str, object], ...]
|
||
snapshot_hash: str
|
||
reference_roots: Mapping[str, tuple[str, ...]]
|
||
|
||
@property
|
||
def records(self) -> tuple[Mapping[str, object], ...]:
|
||
"""兼容调用方使用 records 读取已冻结的约束记录。"""
|
||
return self.constraint_records
|
||
|
||
@property
|
||
def canonical_snapshot_hash(self) -> str:
|
||
"""返回消费快照 canonical hash 的显式别名。"""
|
||
return self.snapshot_hash
|
||
|
||
@property
|
||
def reference_files(self) -> Mapping[str, bytes]:
|
||
"""返回 Task 4 Toolkit 使用的只读文件映射。"""
|
||
return self.files
|
||
|
||
@property
|
||
def protected_roots(self) -> Mapping[str, tuple[str, ...]]:
|
||
"""返回按 recordId 索引的受保护根集合。"""
|
||
return self.reference_roots
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class _StagedReferenceAssets:
|
||
"""单条记录的内部暂存结果;batch 全部成功前不暴露公开结果对象。"""
|
||
|
||
record: Mapping[str, object]
|
||
files: Mapping[str, bytes]
|
||
receipt: Mapping[str, object]
|
||
roots: tuple[str, ...]
|
||
|
||
|
||
def _freeze(value):
|
||
"""递归冻结记录、回执和根索引,防止调用方改写验证结果。"""
|
||
if isinstance(value, Mapping):
|
||
return MappingProxyType({key: _freeze(item) for key, item in value.items()})
|
||
if isinstance(value, (list, tuple)):
|
||
return tuple(_freeze(item) for item in value)
|
||
return value
|
||
|
||
|
||
def to_json_value(value):
|
||
"""把只读验证结果显式复制成普通 JSON 容器,供落盘和 schema 校验边界使用。"""
|
||
if isinstance(value, Mapping):
|
||
return {key: to_json_value(item) for key, item in value.items()}
|
||
if isinstance(value, tuple):
|
||
return [to_json_value(item) for item in value]
|
||
return value
|
||
|
||
|
||
def canonical_json_bytes(value: object) -> bytes:
|
||
"""生成 manifest 使用的 UTF-8 canonical JSON 原始字节。"""
|
||
return json.dumps(
|
||
value,
|
||
ensure_ascii=False,
|
||
sort_keys=True,
|
||
separators=(",", ":"),
|
||
allow_nan=False,
|
||
).encode("utf-8")
|
||
|
||
|
||
def snapshot_hash(files: Mapping[str, bytes]) -> str:
|
||
"""按路径 UTF-8 字节序计算消费快照 hash 向量。"""
|
||
return artifact_snapshot.consumption_snapshot_hash(files)
|
||
|
||
|
||
def canonical_snapshot_hash(files: Mapping[str, bytes]) -> str:
|
||
"""snapshot_hash 的公开语义别名,供跨语言向量测试调用。"""
|
||
return snapshot_hash(files)
|
||
|
||
|
||
def _fail(code: str, record_id: str | None = None, logical_path: str | None = None):
|
||
"""统一抛出稳定异常,禁止把底层系统错误和输入内容向外传播。"""
|
||
raise ReferenceAssetGateError(code, record_id, logical_path)
|
||
|
||
|
||
def _path(value, *, record_id: str | None = None) -> str:
|
||
"""校验仓根相对 NFC POSIX 路径;越界与其它路径语法错误分码。"""
|
||
if isinstance(value, Path):
|
||
value = value.as_posix()
|
||
if not isinstance(value, str) or not value or len(value) > 1024 or "\x00" in value:
|
||
_fail("reference_path_invalid", record_id, "<path>")
|
||
if value.startswith("/"):
|
||
_fail("reference_path_escape", record_id, "<absolute>")
|
||
if "\\" in value or unicodedata.normalize("NFC", value) != value:
|
||
_fail("reference_path_invalid", record_id, "<path>")
|
||
parts = value.split("/")
|
||
if any(part == ".." for part in parts):
|
||
_fail("reference_path_escape", record_id, "<path>")
|
||
if any(part in ("", ".") for part in parts):
|
||
_fail("reference_path_invalid", record_id, "<path>")
|
||
if any(ord(char) < 0x20 or ord(char) == 0x7F for char in value):
|
||
_fail("reference_path_invalid", record_id, "<path>")
|
||
return value
|
||
|
||
|
||
def _keys(value, allowed: set[str], *, record_id: str | None = None, code: str = "reference_registry_invalid") -> None:
|
||
"""执行 strict object key 检查,拒绝未知字段和非对象。"""
|
||
if not isinstance(value, dict) or set(value) != allowed:
|
||
_fail(code, record_id)
|
||
|
||
|
||
def _keys_optional(value, required: set[str], optional: set[str], *, record_id: str | None = None) -> None:
|
||
"""执行带 optional 字段的 strict object key 检查。"""
|
||
if not isinstance(value, dict) or not required.issubset(value) or set(value) - required - optional:
|
||
_fail("reference_registry_invalid", record_id)
|
||
|
||
|
||
def _is_sha256(value) -> bool:
|
||
"""判断是否为小写 64 位 SHA-256 字符串。"""
|
||
return isinstance(value, str) and _SHA256_RE.fullmatch(value) is not None
|
||
|
||
|
||
def _is_identifier(value) -> bool:
|
||
"""判断 schema 中的稳定标识符。"""
|
||
return isinstance(value, str) and _IDENTIFIER_RE.fullmatch(value) is not None
|
||
|
||
|
||
def _load_json(raw: bytes, *, code: str, record_id: str | None, logical_path: str):
|
||
"""只解析可信 fd 已读取的 UTF-8 JSON,并拒绝重复 object key。"""
|
||
def pairs(pairs_list):
|
||
result = {}
|
||
for key, value in pairs_list:
|
||
if key in result:
|
||
raise ValueError("duplicate-key")
|
||
result[key] = value
|
||
return result
|
||
|
||
try:
|
||
text = raw.decode("utf-8")
|
||
value = json.loads(text, object_pairs_hook=pairs, parse_constant=lambda _: (_ for _ in ()).throw(ValueError()))
|
||
except (UnicodeDecodeError, json.JSONDecodeError, ValueError):
|
||
_fail(code, record_id, logical_path)
|
||
if not isinstance(value, dict):
|
||
_fail(code, record_id, logical_path)
|
||
return value
|
||
|
||
|
||
def _read_files(root_fd: int, paths: Sequence[str], *, limits: Mapping[str, int], record_id: str | None,
|
||
missing_code: str | None = None) -> dict[str, bytes]:
|
||
"""通过 artifact_snapshot 的受信选择性读取器取回文件,统一异常类型。"""
|
||
try:
|
||
snapshot = artifact_snapshot.capture_selected_files(root_fd, paths, limits)
|
||
except artifact_snapshot.ArtifactSnapshotError as exc:
|
||
code = missing_code if missing_code is not None and exc.code == "reference_missing" else exc.code
|
||
_fail(code, record_id, exc.logical_path)
|
||
except (OSError, ValueError, TypeError):
|
||
_fail("reference_unreadable", record_id)
|
||
return dict(snapshot.files)
|
||
|
||
|
||
def _open_trusted_root(trusted_root) -> int:
|
||
"""只打开可信根一次,之后所有引用均通过该 fd 解析。"""
|
||
try:
|
||
root_fd, _ = artifact_snapshot._selected_root_fd(trusted_root)
|
||
return root_fd
|
||
except artifact_snapshot.ArtifactSnapshotError as exc:
|
||
_fail(exc.code, logical_path=exc.logical_path)
|
||
except (OSError, TypeError, ValueError):
|
||
_fail("reference_unreadable", logical_path="<root>")
|
||
|
||
|
||
def _read_json_file(root_fd: int, logical_path: str, *, record_id: str | None, limits: Mapping[str, int],
|
||
missing_code: str | None = None) -> tuple[dict, bytes, str]:
|
||
"""从同一可信根读取 JSON,并返回对象、原始字节和原始 SHA-256。"""
|
||
logical_path = _path(logical_path, record_id=record_id)
|
||
raw = _read_files(
|
||
root_fd,
|
||
[logical_path],
|
||
limits=limits,
|
||
record_id=record_id,
|
||
missing_code=missing_code,
|
||
)[logical_path]
|
||
value = _load_json(raw, code="reference_registry_invalid", record_id=record_id, logical_path=logical_path)
|
||
return value, raw, hashlib.sha256(raw).hexdigest()
|
||
|
||
|
||
def _validate_release(release: dict, *, record_id: str | None = None) -> None:
|
||
"""校验 release/1 的 strict 结构和引用字段。"""
|
||
_keys(release, _RELEASE_KEYS, record_id=record_id)
|
||
if release.get("schemaVersion") != "ReferenceAssetRelease/1":
|
||
_fail("reference_registry_invalid", record_id)
|
||
if not _is_identifier(release.get("releaseId")):
|
||
_fail("reference_registry_invalid", record_id)
|
||
for field in ("registryRef", "policyRef"):
|
||
_path(release.get(field), record_id=record_id)
|
||
for field in ("registryHash", "policyHash"):
|
||
if not _is_sha256(release.get(field)):
|
||
_fail("reference_registry_invalid", record_id)
|
||
if release.get("verifierVersion") != VERIFIER_VERSION:
|
||
_fail("reference_registry_invalid", record_id)
|
||
if release.get("trustedRootId") != TRUSTED_ROOT_ID:
|
||
_fail("reference_registry_invalid", record_id)
|
||
|
||
|
||
def _validate_nullable_string(value) -> bool:
|
||
"""检查允许 null 的非空字符串字段。"""
|
||
return value is None or (isinstance(value, str) and bool(value))
|
||
|
||
|
||
def _validate_record(record: dict, *, record_id: str | None = None) -> None:
|
||
"""校验 Record/2 字段、生命周期条件和所有仓内路径。"""
|
||
_keys_optional(
|
||
record,
|
||
{
|
||
"schemaVersion",
|
||
"recordId",
|
||
"role",
|
||
"lifecycleStatus",
|
||
"assetRef",
|
||
"assetVersion",
|
||
"artifactHash",
|
||
"evidenceRefs",
|
||
},
|
||
_RECORD_KEYS - {
|
||
"schemaVersion",
|
||
"recordId",
|
||
"role",
|
||
"lifecycleStatus",
|
||
"assetRef",
|
||
"assetVersion",
|
||
"artifactHash",
|
||
"evidenceRefs",
|
||
},
|
||
record_id=record_id,
|
||
)
|
||
current_id = record.get("recordId")
|
||
if record.get("schemaVersion") != "ReferenceAssetRecord/2" or not _is_identifier(current_id):
|
||
# 只有已经通过 identifier 校验的上下文 ID 才能进入异常,避免回显原始输入。
|
||
safe_record_id = current_id if _is_identifier(current_id) else (
|
||
record_id if _is_identifier(record_id) else None
|
||
)
|
||
_fail("reference_registry_invalid", safe_record_id)
|
||
if record.get("role") not in _ALLOWED_ROLES or record.get("lifecycleStatus") not in _ALLOWED_LIFECYCLE:
|
||
_fail("reference_registry_invalid", current_id)
|
||
_path(record.get("assetRef"), record_id=current_id)
|
||
if not isinstance(record.get("assetVersion"), str) or not record["assetVersion"]:
|
||
_fail("reference_registry_invalid", current_id)
|
||
if not _is_sha256(record.get("artifactHash")):
|
||
_fail("reference_registry_invalid", current_id)
|
||
for field in ("consumerRef", "signedBy"):
|
||
if not _validate_nullable_string(record.get(field)):
|
||
_fail("reference_registry_invalid", current_id)
|
||
signed_at = record.get("signedAt")
|
||
if signed_at is not None and (not isinstance(signed_at, str) or _SIGNED_AT_RE.fullmatch(signed_at) is None):
|
||
_fail("reference_registry_invalid", current_id)
|
||
design_ref = record.get("designRef")
|
||
if design_ref is not None:
|
||
if not isinstance(design_ref, list) or not design_ref or not all(
|
||
isinstance(value, str) and value for value in design_ref
|
||
):
|
||
_fail("reference_registry_invalid", current_id)
|
||
for value in design_ref:
|
||
_path(value, record_id=current_id)
|
||
evidence_refs = record.get("evidenceRefs")
|
||
if not isinstance(evidence_refs, list) or not all(isinstance(value, str) and value for value in evidence_refs):
|
||
_fail("reference_registry_invalid", current_id)
|
||
for value in evidence_refs:
|
||
_path(value, record_id=current_id)
|
||
for field in ("artifactRef", "consumptionManifestRef"):
|
||
value = record.get(field)
|
||
if value is not None:
|
||
_path(value, record_id=current_id)
|
||
if not (record.get("consumptionManifestHash") is None or _is_sha256(record.get("consumptionManifestHash"))):
|
||
_fail("reference_registry_invalid", current_id)
|
||
|
||
active = record["lifecycleStatus"] == "active"
|
||
historical = record["lifecycleStatus"] == "retired" and any(
|
||
record.get(field) is not None for field in ("consumerRef", "signedBy", "signedAt")
|
||
)
|
||
if active or historical:
|
||
required_identity = (
|
||
"consumerRef",
|
||
"signedBy",
|
||
"signedAt",
|
||
"artifactRef",
|
||
"consumptionManifestRef",
|
||
"consumptionManifestHash",
|
||
)
|
||
if any(record.get(field) in (None, "") for field in required_identity):
|
||
_fail("reference_registry_invalid", current_id)
|
||
if active and record["role"] == "game_content_gold":
|
||
if not isinstance(design_ref, list) or not design_ref:
|
||
_fail("reference_registry_invalid", current_id)
|
||
|
||
|
||
def _validate_registry(registry: dict) -> dict[str, dict]:
|
||
"""校验 Registry/2 的 strict 结构、recordId 唯一性和迁移语义。"""
|
||
_keys_optional(
|
||
registry,
|
||
{"schemaVersion", "registryVersion", "sourceOfTruth", "records"},
|
||
{"migrationNotes"},
|
||
)
|
||
if registry.get("schemaVersion") != "ReferenceAssetRegistry/2":
|
||
_fail("reference_registry_invalid")
|
||
if not isinstance(registry.get("registryVersion"), str) or not registry["registryVersion"]:
|
||
_fail("reference_registry_invalid")
|
||
if not isinstance(registry.get("sourceOfTruth"), str) or not registry["sourceOfTruth"]:
|
||
_fail("reference_registry_invalid")
|
||
records = registry.get("records")
|
||
if not isinstance(records, list):
|
||
_fail("reference_registry_invalid")
|
||
notes = registry.get("migrationNotes", {})
|
||
if not isinstance(notes, dict) or any(
|
||
not _is_identifier(key) or not isinstance(value, str) or not value for key, value in notes.items()
|
||
):
|
||
_fail("reference_registry_invalid")
|
||
by_id: dict[str, dict] = {}
|
||
for record in records:
|
||
_validate_record(record)
|
||
current_id = record["recordId"]
|
||
if current_id in by_id:
|
||
_fail("reference_registry_invalid", current_id)
|
||
by_id[current_id] = record
|
||
for key in notes:
|
||
if key != "_registry" and key not in by_id:
|
||
_fail("reference_registry_invalid", key)
|
||
if registry["registryVersion"].endswith(".migration-list") and any(
|
||
record.get("lifecycleStatus") == "active" for record in records
|
||
):
|
||
_fail("reference_registry_invalid")
|
||
return by_id
|
||
|
||
|
||
def _validate_policy(policy: dict, requested_policy_id: str, requested_mode: str) -> None:
|
||
"""校验 release 绑定 policy;当前冻结策略另执行固定身份闭包。"""
|
||
_keys(policy, _POLICY_KEYS, code="reference_policy_missing")
|
||
if policy.get("schemaVersion") != "ReferenceAssetConsumptionPolicy/1":
|
||
_fail("reference_policy_missing")
|
||
if policy.get("policyId") != requested_policy_id or not _is_identifier(policy.get("policyId")):
|
||
_fail("reference_policy_missing")
|
||
if not _is_identifier(policy.get("recordId")) or policy.get("role") not in _ALLOWED_ROLES:
|
||
_fail("reference_policy_missing")
|
||
if not isinstance(policy.get("consumerRef"), str) or not policy["consumerRef"]:
|
||
_fail("reference_policy_missing")
|
||
if not isinstance(policy.get("route"), str) or not policy["route"] or policy.get("autoSelect") is not False:
|
||
_fail("reference_policy_missing")
|
||
if policy.get("mode") != POLICY_MODE:
|
||
_fail("reference_declaration_mismatch", policy["recordId"])
|
||
if requested_policy_id == POLICY_ID:
|
||
fixed = {
|
||
"recordId": POLICY_RECORD_ID,
|
||
"role": POLICY_ROLE,
|
||
"consumerRef": POLICY_CONSUMER_REF,
|
||
"route": "survivor-gold",
|
||
}
|
||
if any(policy.get(field) != expected for field, expected in fixed.items()):
|
||
_fail("reference_policy_missing")
|
||
if requested_mode != policy["mode"]:
|
||
_fail("reference_declaration_mismatch", policy["recordId"])
|
||
|
||
|
||
def _declarations_match(declarations, record: dict, policy: dict) -> bool:
|
||
"""把调用方声明投影成 recordId/role/consumerRef 三元组后逐项对账。"""
|
||
if declarations is None:
|
||
return True
|
||
if isinstance(declarations, Mapping):
|
||
record_ids = declarations.get("referenceAssetRecordIds", declarations.get("recordIds"))
|
||
if record_ids is None and "recordId" in declarations:
|
||
record_ids = [declarations.get("recordId")]
|
||
consumer_ref = declarations.get("consumerRef")
|
||
role = declarations.get("role")
|
||
elif isinstance(declarations, Sequence) and not isinstance(declarations, (str, bytes, bytearray)):
|
||
record_ids = list(declarations)
|
||
consumer_ref = None
|
||
role = None
|
||
else:
|
||
return False
|
||
if record_ids != [record["recordId"]] or consumer_ref not in (None, policy["consumerRef"]):
|
||
return False
|
||
return role in (None, policy["role"])
|
||
|
||
|
||
def _under(path: str, root: str) -> bool:
|
||
"""判断仓内路径是否位于目录根或等于批准的文件引用。"""
|
||
return path == root or path.startswith(root + "/")
|
||
|
||
|
||
def _validate_manifest(manifest: dict, raw: bytes, *, record_id: str, allowed_root: str,
|
||
design_refs: Sequence[str]) -> list[dict]:
|
||
"""校验 manifest canonical 字节、排序、路径范围和单记录资源预算。"""
|
||
if len(raw) > MAX_MANIFEST_BYTES:
|
||
_fail("reference_oversize", record_id)
|
||
if raw != canonical_json_bytes(manifest):
|
||
_fail("reference_manifest_hash_mismatch", record_id)
|
||
_keys(manifest, _MANIFEST_KEYS, record_id=record_id, code="reference_manifest_hash_mismatch")
|
||
if manifest.get("schemaVersion") != "ReferenceAssetConsumptionManifest/1":
|
||
_fail("reference_manifest_hash_mismatch", record_id)
|
||
if not _is_identifier(manifest.get("manifestId")):
|
||
_fail("reference_manifest_hash_mismatch", record_id)
|
||
if manifest.get("canonicalization") != "reference-asset-consumption-manifest/1":
|
||
_fail("reference_manifest_hash_mismatch", record_id)
|
||
entries = manifest.get("entries")
|
||
if not isinstance(entries, list) or not entries:
|
||
_fail("reference_manifest_hash_mismatch", record_id)
|
||
if len(entries) > MAX_MANIFEST_ENTRIES:
|
||
_fail("reference_oversize", record_id)
|
||
|
||
normalized_paths: list[str] = []
|
||
seen: set[str] = set()
|
||
total_declared = 0
|
||
for entry in entries:
|
||
_keys(entry, _MANIFEST_ENTRY_KEYS, record_id=record_id, code="reference_manifest_hash_mismatch")
|
||
path = _path(entry.get("path"), record_id=record_id)
|
||
if path in seen:
|
||
_fail("reference_path_invalid", record_id, path)
|
||
seen.add(path)
|
||
normalized_paths.append(path)
|
||
size = entry.get("size")
|
||
if not isinstance(size, int) or isinstance(size, bool) or size < 0 or size > 9007199254740991:
|
||
_fail("reference_manifest_hash_mismatch", record_id, path)
|
||
if not _is_sha256(entry.get("sha256")):
|
||
_fail("reference_manifest_hash_mismatch", record_id, path)
|
||
if size > MAX_FILE_BYTES:
|
||
_fail("reference_oversize", record_id, path)
|
||
total_declared += size
|
||
if total_declared > MAX_RECORD_BYTES:
|
||
_fail("reference_oversize", record_id, path)
|
||
if not _under(path, allowed_root) and path not in design_refs:
|
||
_fail("reference_path_escape", record_id, path)
|
||
if normalized_paths != sorted(normalized_paths, key=lambda value: value.encode("utf-8")):
|
||
_fail("reference_path_invalid", record_id)
|
||
return entries
|
||
|
||
|
||
def _stage_one_policy(
|
||
policy_id: str,
|
||
*,
|
||
release_ref: str,
|
||
expected_release_hash: str,
|
||
trusted_root,
|
||
mode: str,
|
||
declarations=None,
|
||
) -> _StagedReferenceAssets:
|
||
"""在已锚定根 fd 上完成单条 policy 验证,仅返回 batch 内部暂存值。"""
|
||
if not _is_sha256(expected_release_hash):
|
||
_fail("reference_registry_untrusted")
|
||
|
||
release_ref = _path(release_ref)
|
||
root_fd = _open_trusted_root(trusted_root)
|
||
try:
|
||
release, _, release_hash = _read_json_file(
|
||
root_fd,
|
||
release_ref,
|
||
record_id=None,
|
||
limits={"max_files": 1, "max_file_bytes": MAX_MANIFEST_BYTES, "max_record_bytes": MAX_MANIFEST_BYTES},
|
||
)
|
||
if release_hash != expected_release_hash:
|
||
_fail("reference_registry_untrusted", logical_path=release_ref)
|
||
_validate_release(release)
|
||
|
||
registry_ref = _path(release["registryRef"])
|
||
registry, _, registry_hash = _read_json_file(
|
||
root_fd,
|
||
registry_ref,
|
||
record_id=None,
|
||
limits={"max_files": 1, "max_file_bytes": MAX_RECORD_BYTES, "max_record_bytes": MAX_RECORD_BYTES},
|
||
)
|
||
if registry_hash != release["registryHash"]:
|
||
_fail("reference_registry_untrusted", logical_path=registry_ref)
|
||
by_id = _validate_registry(registry)
|
||
|
||
policy_ref = _path(release["policyRef"])
|
||
try:
|
||
policy, _, policy_hash = _read_json_file(
|
||
root_fd,
|
||
policy_ref,
|
||
record_id=None,
|
||
limits={"max_files": 1, "max_file_bytes": MAX_MANIFEST_BYTES, "max_record_bytes": MAX_MANIFEST_BYTES},
|
||
missing_code="reference_policy_missing",
|
||
)
|
||
except ReferenceAssetGateError as exc:
|
||
if exc.code == "reference_registry_invalid":
|
||
_fail("reference_policy_missing", logical_path=policy_ref)
|
||
raise
|
||
if policy_hash != release["policyHash"]:
|
||
_fail("reference_registry_untrusted", logical_path=policy_ref)
|
||
_validate_policy(policy, policy_id, mode)
|
||
|
||
record = by_id.get(policy["recordId"])
|
||
if record is None:
|
||
_fail("reference_registry_invalid", policy["recordId"])
|
||
if record.get("lifecycleStatus") != "active":
|
||
_fail("reference_registry_invalid", record["recordId"])
|
||
if record.get("role") != policy["role"] or record.get("consumerRef") != policy["consumerRef"]:
|
||
_fail("reference_declaration_mismatch", record["recordId"])
|
||
if not _declarations_match(declarations, record, policy):
|
||
_fail("reference_declaration_mismatch", record["recordId"])
|
||
|
||
record_id = record["recordId"]
|
||
asset_root = _path(record["assetRef"], record_id=record_id)
|
||
artifact_ref = _path(record.get("artifactRef"), record_id=record_id)
|
||
manifest_ref = _path(record.get("consumptionManifestRef"), record_id=record_id)
|
||
if not _under(artifact_ref, asset_root) or not _under(manifest_ref, asset_root):
|
||
_fail("reference_path_escape", record_id)
|
||
design_refs = tuple(record.get("designRef") or ())
|
||
|
||
# 先读取并核验 bundle;manifest 使用独立的 1 MiB 读前预算,避免把超限清单
|
||
# 先读入内存。两次读取仍共用同一个 trusted root fd 和逐级 O_NOFOLLOW 边界。
|
||
artifact_files = _read_files(
|
||
root_fd,
|
||
[artifact_ref],
|
||
limits={
|
||
"max_files": 1,
|
||
"max_file_bytes": MAX_FILE_BYTES,
|
||
"max_record_bytes": MAX_RECORD_BYTES,
|
||
},
|
||
record_id=record_id,
|
||
)
|
||
observed_artifact_hash = hashlib.sha256(artifact_files[artifact_ref]).hexdigest()
|
||
if observed_artifact_hash != record["artifactHash"]:
|
||
_fail("reference_artifact_hash_mismatch", record_id, artifact_ref)
|
||
manifest_files = _read_files(
|
||
root_fd,
|
||
[manifest_ref],
|
||
limits={
|
||
"max_files": 1,
|
||
"max_file_bytes": MAX_MANIFEST_BYTES,
|
||
"max_record_bytes": MAX_MANIFEST_BYTES,
|
||
},
|
||
record_id=record_id,
|
||
)
|
||
manifest_raw = manifest_files[manifest_ref]
|
||
observed_manifest_hash = hashlib.sha256(manifest_raw).hexdigest()
|
||
if observed_manifest_hash != record["consumptionManifestHash"]:
|
||
_fail("reference_manifest_hash_mismatch", record_id, manifest_ref)
|
||
manifest = _load_json(
|
||
manifest_raw,
|
||
code="reference_manifest_hash_mismatch",
|
||
record_id=record_id,
|
||
logical_path=manifest_ref,
|
||
)
|
||
entries = _validate_manifest(
|
||
manifest,
|
||
manifest_raw,
|
||
record_id=record_id,
|
||
allowed_root=asset_root,
|
||
design_refs=design_refs,
|
||
)
|
||
entry_paths = [entry["path"] for entry in entries]
|
||
selected = _read_files(
|
||
root_fd,
|
||
entry_paths,
|
||
limits={
|
||
"max_files": MAX_MANIFEST_ENTRIES,
|
||
"max_file_bytes": MAX_FILE_BYTES,
|
||
"max_record_bytes": MAX_RECORD_BYTES,
|
||
},
|
||
record_id=record_id,
|
||
)
|
||
for entry in entries:
|
||
path = entry["path"]
|
||
content = selected[path]
|
||
if len(content) != entry["size"] or hashlib.sha256(content).hexdigest() != entry["sha256"]:
|
||
_fail("reference_entry_hash_mismatch", record_id, path)
|
||
|
||
immutable_files = MappingProxyType(dict(selected))
|
||
receipt = {
|
||
"schemaVersion": "ReferenceAssetVerificationReceipt/1",
|
||
"receiptId": f"receipt-{policy_id}-{record_id}-{observed_manifest_hash[:12]}",
|
||
"releaseRef": release_ref,
|
||
"registryVersion": registry["registryVersion"],
|
||
"expectedRegistryHash": release["registryHash"],
|
||
"observedRegistryHash": registry_hash,
|
||
"policyHash": policy_hash,
|
||
"verifierVersion": release["verifierVersion"],
|
||
"trustedRootId": release["trustedRootId"],
|
||
"recordId": record_id,
|
||
"role": record["role"],
|
||
"consumerRef": record["consumerRef"],
|
||
"artifactRef": artifact_ref,
|
||
"consumptionManifestRef": manifest_ref,
|
||
"expected": {
|
||
"artifactHash": record["artifactHash"],
|
||
"consumptionManifestHash": record["consumptionManifestHash"],
|
||
},
|
||
"observed": {
|
||
"artifactHash": observed_artifact_hash,
|
||
"consumptionManifestHash": observed_manifest_hash,
|
||
},
|
||
"finalSnapshotHash": snapshot_hash(immutable_files),
|
||
}
|
||
return _StagedReferenceAssets(
|
||
record=dict(record),
|
||
files=immutable_files,
|
||
receipt=receipt,
|
||
roots=tuple((asset_root, *design_refs)),
|
||
)
|
||
finally:
|
||
os.close(root_fd)
|
||
|
||
|
||
def verify_policy(
|
||
policy_id: str,
|
||
*,
|
||
release_ref: str,
|
||
expected_release_hash: str,
|
||
trusted_root,
|
||
mode: str,
|
||
declarations=None,
|
||
) -> VerifiedReferenceAssets:
|
||
"""验证当前唯一受批准 policy,并委托单元素原子 batch。"""
|
||
if policy_id != POLICY_ID:
|
||
_fail("reference_policy_missing")
|
||
if mode != POLICY_MODE:
|
||
_fail("reference_declaration_mismatch")
|
||
return verify_policies([{
|
||
"policy_id": policy_id,
|
||
"release_ref": release_ref,
|
||
"expected_release_hash": expected_release_hash,
|
||
"trusted_root": trusted_root,
|
||
"mode": mode,
|
||
"declarations": declarations,
|
||
}])
|
||
|
||
|
||
def verify_policies(requests: Sequence[Mapping[str, object]]) -> VerifiedReferenceAssets:
|
||
"""原子验证并合并多条 policy,任一失败都不会构造公开结果。"""
|
||
if not isinstance(requests, Sequence) or isinstance(requests, (str, bytes, bytearray)) or not requests:
|
||
_fail("reference_policy_missing")
|
||
|
||
required_keys = {
|
||
"policy_id",
|
||
"release_ref",
|
||
"expected_release_hash",
|
||
"trusted_root",
|
||
"mode",
|
||
}
|
||
allowed_keys = required_keys | {"declarations"}
|
||
merged_files: dict[str, bytes] = {}
|
||
merged_records: list[Mapping[str, object]] = []
|
||
merged_receipts: list[Mapping[str, object]] = []
|
||
merged_roots: dict[str, tuple[str, ...]] = {}
|
||
total_bytes = 0
|
||
|
||
for request in requests:
|
||
if not isinstance(request, Mapping) or not required_keys.issubset(request) or set(request) - allowed_keys:
|
||
_fail("reference_declaration_mismatch")
|
||
item = _stage_one_policy(
|
||
request["policy_id"],
|
||
release_ref=request["release_ref"],
|
||
expected_release_hash=request["expected_release_hash"],
|
||
trusted_root=request["trusted_root"],
|
||
mode=request["mode"],
|
||
declarations=request.get("declarations"),
|
||
)
|
||
record_id = item.record["recordId"]
|
||
if record_id in merged_roots:
|
||
_fail("reference_registry_invalid", record_id)
|
||
for path, content in item.files.items():
|
||
existing = merged_files.get(path)
|
||
if existing is not None:
|
||
if existing != content:
|
||
_fail("reference_entry_hash_mismatch", record_id, path)
|
||
continue
|
||
total_bytes += len(content)
|
||
if total_bytes > MAX_TOTAL_BYTES:
|
||
_fail("reference_oversize", record_id, path)
|
||
merged_files[path] = content
|
||
# 记录和回执在当前 stage 合并成功后立即冻结,避免积存可变暂存对象。
|
||
merged_records.append(_freeze(item.record))
|
||
merged_receipts.append(_freeze(item.receipt))
|
||
merged_roots[record_id] = tuple(item.roots)
|
||
|
||
immutable_files = MappingProxyType(dict(merged_files))
|
||
return VerifiedReferenceAssets(
|
||
constraint_records=tuple(merged_records),
|
||
files=immutable_files,
|
||
receipts=tuple(merged_receipts),
|
||
snapshot_hash=snapshot_hash(immutable_files),
|
||
reference_roots=_freeze(merged_roots),
|
||
)
|
||
|
||
|
||
__all__ = [
|
||
"MAX_FILE_BYTES",
|
||
"MAX_MANIFEST_BYTES",
|
||
"MAX_MANIFEST_ENTRIES",
|
||
"MAX_RECORD_BYTES",
|
||
"MAX_TOTAL_BYTES",
|
||
"POLICY_ID",
|
||
"ReferenceAssetError",
|
||
"ReferenceAssetGateError",
|
||
"ReferenceAssetVerificationError",
|
||
"VerifiedReferenceAssets",
|
||
"canonical_json_bytes",
|
||
"canonical_snapshot_hash",
|
||
"snapshot_hash",
|
||
"to_json_value",
|
||
"verify_policy",
|
||
"verify_policies",
|
||
]
|