mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-11-13 09:48:18 +00:00
feat(detection-core): authenticate post-spend case release
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
"""Closed object-graph whitelist for immutable admission cases."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from openultrasast.core._closed_graph import closed_graph_problem
|
||||
from openultrasast.core._model_result import ModelResult, ModelStatus, PreservedModelResponse
|
||||
from openultrasast.core.budget import BudgetDerivation, ChangeComplexity, ReferenceHardware, ReferenceWorkload
|
||||
from openultrasast.core.canonical import ContentRef
|
||||
from openultrasast.core.coverage import CoverageFunnel, InputManifest, InputReadReceipt, ReasonCount, RelationStatusCount
|
||||
from openultrasast.core.custody import LineagePurpose
|
||||
from openultrasast.core.evidence import (
|
||||
AnalyzedScope,
|
||||
Decision,
|
||||
EvidenceBasis,
|
||||
EvidencePacket,
|
||||
EvidenceRecord,
|
||||
NeutralOutcome,
|
||||
SourceRegion,
|
||||
)
|
||||
from openultrasast.core.family import (
|
||||
DecisionRule,
|
||||
FamilyContract,
|
||||
FieldSpec,
|
||||
ObjectSpec,
|
||||
RelationOriginPolicy,
|
||||
ResidualQuestion,
|
||||
RoleSpec,
|
||||
)
|
||||
from openultrasast.core.lineage import LineageManifest, LineageUse
|
||||
from openultrasast.core.measurement import ScopeKey
|
||||
from openultrasast.core.model import CompletenessItem, ModelProgramDeclaration, ModelRequest
|
||||
from openultrasast.core.proof import (
|
||||
ControlBinding,
|
||||
DifferentialProofRecord,
|
||||
ProofAttachment,
|
||||
ProofOutcome,
|
||||
ProofProducerIdentity,
|
||||
ProofSideResult,
|
||||
ProofVerificationReceipt,
|
||||
)
|
||||
from openultrasast.core.provenance import (
|
||||
DecisionProvenance,
|
||||
ModelCallIdentity,
|
||||
NamedInput,
|
||||
NamedInputs,
|
||||
ProvenanceInputRole,
|
||||
ProvenanceStamp,
|
||||
)
|
||||
from openultrasast.core.semantics import (
|
||||
EvidenceProvenance,
|
||||
FieldKind,
|
||||
FieldValue,
|
||||
Observation,
|
||||
ProvenanceKind,
|
||||
RelationKind,
|
||||
RelationStatus,
|
||||
SemanticNode,
|
||||
SemanticRelation,
|
||||
SourceLocation,
|
||||
)
|
||||
from openultrasast.core.validation import EvidenceValidationInput
|
||||
from openultrasast.core.verdict_v2 import BoundedSatisfaction, BoundedSilence, DefectClaim
|
||||
|
||||
_DATACLASSES: tuple[type[object], ...] = (
|
||||
ScopeKey,
|
||||
EvidenceValidationInput,
|
||||
EvidenceRecord,
|
||||
EvidencePacket,
|
||||
Decision,
|
||||
AnalyzedScope,
|
||||
SourceRegion,
|
||||
SourceLocation,
|
||||
SemanticNode,
|
||||
EvidenceProvenance,
|
||||
FieldValue,
|
||||
SemanticRelation,
|
||||
Observation,
|
||||
ProvenanceStamp,
|
||||
DecisionProvenance,
|
||||
ModelCallIdentity,
|
||||
NamedInput,
|
||||
NamedInputs,
|
||||
ContentRef,
|
||||
ModelRequest,
|
||||
ModelProgramDeclaration,
|
||||
CompletenessItem,
|
||||
ModelResult,
|
||||
PreservedModelResponse,
|
||||
FamilyContract,
|
||||
FieldSpec,
|
||||
ObjectSpec,
|
||||
RoleSpec,
|
||||
DecisionRule,
|
||||
ResidualQuestion,
|
||||
LineageManifest,
|
||||
LineageUse,
|
||||
DefectClaim,
|
||||
BoundedSatisfaction,
|
||||
BoundedSilence,
|
||||
ProofAttachment,
|
||||
DifferentialProofRecord,
|
||||
ProofProducerIdentity,
|
||||
ProofSideResult,
|
||||
ControlBinding,
|
||||
ProofVerificationReceipt,
|
||||
CoverageFunnel,
|
||||
RelationStatusCount,
|
||||
ReasonCount,
|
||||
InputManifest,
|
||||
InputReadReceipt,
|
||||
ChangeComplexity,
|
||||
BudgetDerivation,
|
||||
ReferenceWorkload,
|
||||
ReferenceHardware,
|
||||
)
|
||||
|
||||
_ENUMS: tuple[type[object], ...] = (
|
||||
NeutralOutcome,
|
||||
EvidenceBasis,
|
||||
FieldKind,
|
||||
RelationKind,
|
||||
RelationStatus,
|
||||
ProvenanceKind,
|
||||
RelationOriginPolicy,
|
||||
ProvenanceInputRole,
|
||||
LineagePurpose,
|
||||
ModelStatus,
|
||||
ProofOutcome,
|
||||
)
|
||||
|
||||
|
||||
def admission_case_problem(value: object, case_type: type[object], role_type: type[object]) -> str | None:
|
||||
return closed_graph_problem(
|
||||
value,
|
||||
dataclass_types=(case_type, *_DATACLASSES),
|
||||
enum_types=(role_type, *_ENUMS),
|
||||
path="admission case result",
|
||||
)
|
||||
@@ -0,0 +1,299 @@
|
||||
"""Validation helpers for authenticated post-spend admission-case releases."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from collections import Counter
|
||||
from collections.abc import Sequence
|
||||
|
||||
from openultrasast.core import canonical
|
||||
from openultrasast.core.admission_case import (
|
||||
ADMISSION_CASE_RELEASE_SCHEMA,
|
||||
AdmissionCaseRelease,
|
||||
AdmissionCaseReleaseError,
|
||||
AdmissionCaseResult,
|
||||
AdmissionCaseRole,
|
||||
ReleasedCaseBinding,
|
||||
derive_admission_case_release_digest,
|
||||
derive_admission_case_release_payload_digest,
|
||||
derive_admission_case_result_digest,
|
||||
derive_released_case_binding_digest,
|
||||
validate_admission_case_shape,
|
||||
)
|
||||
from openultrasast.core.admission_context import AdmissionValidationContext
|
||||
from openultrasast.core.admission_review import AdmissionManifestV2
|
||||
from openultrasast.core.admission_spend import AdmissionAuthorization, AdmissionPreparation
|
||||
from openultrasast.core.coverage import derive_input_manifest_digest, validate_instrument
|
||||
from openultrasast.core.custody import PopulationCustodyPort, PopulationUseReceipt
|
||||
from openultrasast.core.factpack import (
|
||||
FactPackValidationError,
|
||||
IndependenceAttestation,
|
||||
ReviewIndependencePolicy,
|
||||
derive_independence_attestation_digest,
|
||||
derive_review_independence_policy_digest,
|
||||
)
|
||||
from openultrasast.core.measurement import ScopeKey
|
||||
from openultrasast.core.provenance import ProvenanceInputRole
|
||||
from openultrasast.core.validation import derive_evidence_validation_input_digest, validate_evidence_input
|
||||
|
||||
_DIGEST = re.compile(r"[0-9a-f]{64}")
|
||||
|
||||
|
||||
def _digest(value: object, field: str) -> None:
|
||||
if not isinstance(value, str) or _DIGEST.fullmatch(value) is None:
|
||||
raise AdmissionCaseReleaseError(f"{field} must be a full lowercase SHA-256 digest")
|
||||
|
||||
|
||||
def _digest_tuple(value: object, field: str, *, nonempty: bool = True) -> None:
|
||||
if type(value) is not tuple or (nonempty and not value):
|
||||
raise AdmissionCaseReleaseError(f"{field} must be a{' nonempty' if nonempty else ''} exact tuple")
|
||||
for item in value:
|
||||
_digest(item, field)
|
||||
if value != tuple(sorted(set(value))):
|
||||
raise AdmissionCaseReleaseError(f"{field} must be unique and canonically ordered")
|
||||
|
||||
|
||||
def _validate_policy(policy: ReviewIndependencePolicy) -> None:
|
||||
try:
|
||||
policy.__post_init__()
|
||||
except FactPackValidationError as exc:
|
||||
raise AdmissionCaseReleaseError(f"release review policy is invalid: {exc}") from exc
|
||||
expected = derive_review_independence_policy_digest(
|
||||
policy.reviewed_reviewer_digests,
|
||||
policy.forbidden_identity_pairs,
|
||||
policy.trusted_store_identities,
|
||||
policy.maximum_evidence_bytes,
|
||||
)
|
||||
if policy.policy_digest != expected:
|
||||
raise AdmissionCaseReleaseError("release review policy digest cannot be re-derived")
|
||||
|
||||
|
||||
def _validate_review(
|
||||
release: AdmissionCaseRelease,
|
||||
receipt: PopulationUseReceipt,
|
||||
policy: ReviewIndependencePolicy,
|
||||
resolver: canonical.ContentResolver,
|
||||
) -> None:
|
||||
review = release.review
|
||||
if type(review) is not IndependenceAttestation:
|
||||
raise AdmissionCaseReleaseError("release review must be an exact independence attestation")
|
||||
try:
|
||||
review.__post_init__()
|
||||
review.conflict_disclosure.__post_init__()
|
||||
except (FactPackValidationError, ValueError) as exc:
|
||||
raise AdmissionCaseReleaseError(f"release review is invalid: {exc}") from exc
|
||||
if review.subject_digest != release.release_payload_digest:
|
||||
raise AdmissionCaseReleaseError("release review subject does not match payload digest")
|
||||
if review.producer_identity != release.custodian_identity or review.producer_identity != receipt.custodian_identity:
|
||||
raise AdmissionCaseReleaseError("release review producer must be the exact spend custodian")
|
||||
if review.producer_identity == review.reviewer_identity:
|
||||
raise AdmissionCaseReleaseError("release review producer and reviewer must be independent")
|
||||
if review.reviewer_identity not in policy.reviewed_reviewer_digests:
|
||||
raise AdmissionCaseReleaseError("release reviewer is not admitted by policy")
|
||||
if (review.producer_identity, review.reviewer_identity) in policy.forbidden_identity_pairs:
|
||||
raise AdmissionCaseReleaseError("release producer/reviewer pair is forbidden")
|
||||
if review.attestation_digest != derive_independence_attestation_digest(review):
|
||||
raise AdmissionCaseReleaseError("release review attestation digest cannot be re-derived")
|
||||
content = canonical.resolve_content(
|
||||
review.conflict_disclosure,
|
||||
resolver,
|
||||
trusted_store_identities=policy.trusted_store_identities,
|
||||
maximum_bytes=policy.maximum_evidence_bytes,
|
||||
)
|
||||
if not content:
|
||||
raise AdmissionCaseReleaseError("release conflict disclosure must be nonempty")
|
||||
|
||||
|
||||
def _validate_binding_shape(binding: ReleasedCaseBinding) -> None:
|
||||
if type(binding) is not ReleasedCaseBinding:
|
||||
raise AdmissionCaseReleaseError("release binding must use the exact contract type")
|
||||
if type(binding.scope) is not ScopeKey or type(binding.role) is not AdmissionCaseRole:
|
||||
raise AdmissionCaseReleaseError("release binding scope and role must use exact contract types")
|
||||
binding.scope.__post_init__()
|
||||
for value, field in (
|
||||
(binding.case_id_digest, "case identity digest"),
|
||||
(binding.case_result_digest, "case result digest"),
|
||||
(binding.input_manifest_digest, "input manifest digest"),
|
||||
(binding.binding_digest, "binding digest"),
|
||||
):
|
||||
_digest(value, field)
|
||||
if binding.role is AdmissionCaseRole.ORDINARY:
|
||||
if binding.pair_id_digest is not None:
|
||||
raise AdmissionCaseReleaseError("ordinary release binding cannot carry a pair identity")
|
||||
else:
|
||||
_digest(binding.pair_id_digest, "pair identity digest")
|
||||
_digest_tuple(binding.evidence_input_digests, "evidence input digests")
|
||||
_digest_tuple(binding.change_input_digests, "change input digests")
|
||||
if binding.binding_digest != derive_released_case_binding_digest(binding):
|
||||
raise AdmissionCaseReleaseError("release binding digest cannot be re-derived")
|
||||
|
||||
|
||||
def _validate_pair_integrity(bindings: tuple[ReleasedCaseBinding, ...]) -> None:
|
||||
pairs: dict[tuple[str, str, str], Counter[AdmissionCaseRole]] = {}
|
||||
for binding in bindings:
|
||||
if binding.pair_id_digest is None:
|
||||
continue
|
||||
key = (binding.scope.family_id, binding.scope.ecosystem, binding.pair_id_digest)
|
||||
pairs.setdefault(key, Counter())[binding.role] += 1
|
||||
expected = Counter({AdmissionCaseRole.VULNERABLE: 1, AdmissionCaseRole.FIXED: 1})
|
||||
if any(counts != expected for counts in pairs.values()):
|
||||
raise AdmissionCaseReleaseError("every scoped release pair must contain one vulnerable and one fixed case")
|
||||
|
||||
|
||||
def _validate_release_shape(release: AdmissionCaseRelease) -> None:
|
||||
if type(release) is not AdmissionCaseRelease or release.schema_version != ADMISSION_CASE_RELEASE_SCHEMA:
|
||||
raise AdmissionCaseReleaseError("unknown or substituted admission case-release schema")
|
||||
for value, field in (
|
||||
(release.population_digest, "population digest"),
|
||||
(release.manifest_digest, "manifest digest"),
|
||||
(release.authorization_digest, "authorization digest"),
|
||||
(release.preparation_digest, "preparation digest"),
|
||||
(release.label_release_receipt_digest, "label release receipt digest"),
|
||||
(release.custodian_identity, "custodian identity"),
|
||||
(release.release_payload_digest, "release payload digest"),
|
||||
(release.release_digest, "release digest"),
|
||||
):
|
||||
_digest(value, field)
|
||||
if type(release.bindings) is not tuple or not release.bindings:
|
||||
raise AdmissionCaseReleaseError("release bindings must be a nonempty exact tuple")
|
||||
for binding in release.bindings:
|
||||
_validate_binding_shape(binding)
|
||||
case_ids = tuple(item.case_id_digest for item in release.bindings)
|
||||
if case_ids != tuple(sorted(case_ids)) or len(case_ids) != len(set(case_ids)):
|
||||
raise AdmissionCaseReleaseError("release bindings must have unique canonical case identities")
|
||||
_validate_pair_integrity(release.bindings)
|
||||
if release.release_payload_digest != derive_admission_case_release_payload_digest(release):
|
||||
raise AdmissionCaseReleaseError("release payload digest cannot be re-derived")
|
||||
if release.release_digest != derive_admission_case_release_digest(release):
|
||||
raise AdmissionCaseReleaseError("release digest cannot be re-derived")
|
||||
|
||||
|
||||
def _validate_header(
|
||||
manifest: AdmissionManifestV2,
|
||||
preparation: AdmissionPreparation,
|
||||
receipt: PopulationUseReceipt,
|
||||
authorization: AdmissionAuthorization,
|
||||
release: AdmissionCaseRelease,
|
||||
) -> None:
|
||||
expected = (
|
||||
manifest.identity.population_digest,
|
||||
manifest.manifest_digest,
|
||||
authorization.authorization_digest,
|
||||
preparation.preparation_digest,
|
||||
receipt.receipt_digest,
|
||||
receipt.custodian_identity,
|
||||
)
|
||||
actual = (
|
||||
release.population_digest,
|
||||
release.manifest_digest,
|
||||
release.authorization_digest,
|
||||
release.preparation_digest,
|
||||
release.label_release_receipt_digest,
|
||||
release.custodian_identity,
|
||||
)
|
||||
if actual != expected:
|
||||
fields = (
|
||||
"population digest",
|
||||
"manifest digest",
|
||||
"authorization digest",
|
||||
"preparation digest",
|
||||
"label release receipt digest",
|
||||
"custodian",
|
||||
)
|
||||
changed = next(field for field, left, right in zip(fields, actual, expected, strict=True) if left != right)
|
||||
raise AdmissionCaseReleaseError(f"release {changed} does not match the exact spent admission")
|
||||
|
||||
|
||||
def _validated_case_inputs(
|
||||
case: AdmissionCaseResult,
|
||||
context: AdmissionValidationContext,
|
||||
custody: PopulationCustodyPort,
|
||||
resolver: canonical.ContentResolver,
|
||||
) -> tuple[tuple[str, ...], tuple[str, ...]]:
|
||||
evidence: list[str] = []
|
||||
changes: set[str] = set()
|
||||
for value in case.evidence_inputs:
|
||||
if value.input_digest != derive_evidence_validation_input_digest(value):
|
||||
raise AdmissionCaseReleaseError("case evidence input digest cannot be re-derived")
|
||||
validate_evidence_input(value, context.evidence, custody, resolver)
|
||||
evidence.append(value.input_digest)
|
||||
matches = tuple(item.identity for item in value.named_inputs.inputs if item.role is ProvenanceInputRole.CHANGE_INPUT)
|
||||
if len(matches) != 1 or matches[0] != value.record.packet.provenance.change_input_digest:
|
||||
raise AdmissionCaseReleaseError("case change input cannot be re-derived from validated provenance")
|
||||
changes.add(matches[0])
|
||||
ordered_evidence = tuple(sorted(evidence))
|
||||
ordered_changes = tuple(sorted(changes))
|
||||
_digest_tuple(ordered_evidence, "case evidence input digests")
|
||||
_digest_tuple(ordered_changes, "case change input digests")
|
||||
return ordered_evidence, ordered_changes
|
||||
|
||||
|
||||
def _binding_for_case(
|
||||
case: AdmissionCaseResult,
|
||||
context: AdmissionValidationContext,
|
||||
custody: PopulationCustodyPort,
|
||||
resolver: canonical.ContentResolver,
|
||||
) -> ReleasedCaseBinding:
|
||||
validate_admission_case_shape(case)
|
||||
expected_manifest = derive_input_manifest_digest(case.input_manifest.tracked_input_ids)
|
||||
if case.input_manifest.manifest_digest != expected_manifest:
|
||||
raise AdmissionCaseReleaseError("case input manifest digest cannot be re-derived")
|
||||
validate_instrument(case.input_manifest, case.input_receipts)
|
||||
if case.result_digest != derive_admission_case_result_digest(case):
|
||||
raise AdmissionCaseReleaseError("case result digest cannot be re-derived")
|
||||
evidence, changes = _validated_case_inputs(case, context, custody, resolver)
|
||||
draft = ReleasedCaseBinding(
|
||||
case.case_id_digest,
|
||||
case.scope,
|
||||
case.role,
|
||||
case.pair_id_digest,
|
||||
case.result_digest,
|
||||
evidence,
|
||||
changes,
|
||||
case.input_manifest.manifest_digest,
|
||||
"0" * 64,
|
||||
)
|
||||
return draft
|
||||
|
||||
|
||||
def _canonical_binding(
|
||||
case: AdmissionCaseResult,
|
||||
context: AdmissionValidationContext,
|
||||
custody: PopulationCustodyPort,
|
||||
resolver: canonical.ContentResolver,
|
||||
) -> ReleasedCaseBinding:
|
||||
draft = _binding_for_case(case, context, custody, resolver)
|
||||
return ReleasedCaseBinding(
|
||||
draft.case_id_digest,
|
||||
draft.scope,
|
||||
draft.role,
|
||||
draft.pair_id_digest,
|
||||
draft.case_result_digest,
|
||||
draft.evidence_input_digests,
|
||||
draft.change_input_digests,
|
||||
draft.input_manifest_digest,
|
||||
derive_released_case_binding_digest(draft),
|
||||
)
|
||||
|
||||
|
||||
def validate_case_bindings(
|
||||
release: AdmissionCaseRelease,
|
||||
cases: Sequence[AdmissionCaseResult],
|
||||
context: AdmissionValidationContext,
|
||||
custody: PopulationCustodyPort,
|
||||
resolver: canonical.ContentResolver,
|
||||
) -> None:
|
||||
selected = tuple(cases)
|
||||
if not selected:
|
||||
raise AdmissionCaseReleaseError("case population must be nonempty")
|
||||
case_ids = tuple(case.case_id_digest for case in selected)
|
||||
if len(case_ids) != len(set(case_ids)):
|
||||
raise AdmissionCaseReleaseError("case population contains duplicate identities")
|
||||
expected = tuple(
|
||||
sorted(
|
||||
(_canonical_binding(case, context, custody, resolver) for case in selected),
|
||||
key=lambda item: item.case_id_digest,
|
||||
)
|
||||
)
|
||||
if release.bindings != expected:
|
||||
raise AdmissionCaseReleaseError("authenticated release binding does not match the exact case population")
|
||||
@@ -0,0 +1,280 @@
|
||||
"""Immutable admission-case contracts and canonical commitments."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
|
||||
from openultrasast.core import canonical
|
||||
from openultrasast.core._admission_case_shape import admission_case_problem
|
||||
from openultrasast.core.admission_context import AdmissionValidationContext
|
||||
from openultrasast.core.admission_review import AdmissionManifestV2
|
||||
from openultrasast.core.admission_spend import AdmissionPreparation, validate_pre_label_spend
|
||||
from openultrasast.core.budget import BudgetDerivation, ChangeComplexity
|
||||
from openultrasast.core.coverage import CoverageFunnel, InputManifest, InputReadReceipt
|
||||
from openultrasast.core.custody import PopulationCustodyPort, PopulationUseReceipt
|
||||
from openultrasast.core.factpack import IndependenceAttestation
|
||||
from openultrasast.core.measurement import DenominatorPolicy, ScopeKey
|
||||
from openultrasast.core.proof import ProofAttachment
|
||||
from openultrasast.core.readiness import ReadinessResult
|
||||
from openultrasast.core.validation import EvidenceValidationInput
|
||||
from openultrasast.core.verdict_v2 import BoundedSatisfaction, BoundedSilence, DefectClaim
|
||||
|
||||
ADMISSION_CASE_RELEASE_SCHEMA = "openultrasast.detection-core.admission-case-release.v2"
|
||||
ADMISSION_CASE_RELEASE_PAYLOAD_SCHEMA = "openultrasast.detection-core.admission-case-release-payload.v2"
|
||||
RELEASED_CASE_BINDING_SCHEMA = "openultrasast.detection-core.released-case-binding.v2"
|
||||
|
||||
|
||||
class AdmissionCaseReleaseError(ValueError):
|
||||
"""The post-spend case release cannot authorize admission scoring."""
|
||||
|
||||
|
||||
class AdmissionCaseRole(Enum):
|
||||
VULNERABLE = "vulnerable"
|
||||
FIXED = "fixed"
|
||||
ORDINARY = "ordinary"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AdmissionCaseResult:
|
||||
case_id_digest: str
|
||||
scope: ScopeKey
|
||||
role: AdmissionCaseRole
|
||||
pair_id_digest: str | None
|
||||
evidence_inputs: tuple[EvidenceValidationInput, ...]
|
||||
defect_claims: tuple[DefectClaim, ...]
|
||||
satisfactions: tuple[BoundedSatisfaction, ...]
|
||||
silence: tuple[BoundedSilence, ...]
|
||||
proof_attachments: tuple[ProofAttachment, ...]
|
||||
coverage: CoverageFunnel
|
||||
input_manifest: InputManifest
|
||||
input_receipts: tuple[InputReadReceipt, ...]
|
||||
complexity: ChangeComplexity
|
||||
budget: BudgetDerivation
|
||||
total_reply_ms: int
|
||||
result_digest: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ReleasedCaseBinding:
|
||||
case_id_digest: str
|
||||
scope: ScopeKey
|
||||
role: AdmissionCaseRole
|
||||
pair_id_digest: str | None
|
||||
case_result_digest: str
|
||||
evidence_input_digests: tuple[str, ...]
|
||||
change_input_digests: tuple[str, ...]
|
||||
input_manifest_digest: str
|
||||
binding_digest: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AdmissionCaseRelease:
|
||||
schema_version: str
|
||||
population_digest: str
|
||||
manifest_digest: str
|
||||
authorization_digest: str
|
||||
preparation_digest: str
|
||||
label_release_receipt_digest: str
|
||||
custodian_identity: str
|
||||
bindings: tuple[ReleasedCaseBinding, ...]
|
||||
release_payload_digest: str
|
||||
review: IndependenceAttestation
|
||||
release_digest: str
|
||||
|
||||
|
||||
def _coverage_value(value: CoverageFunnel) -> canonical.JsonValue:
|
||||
return {
|
||||
"inputs_read": value.inputs_read,
|
||||
"questions_formed": value.questions_formed,
|
||||
"sites_considered": value.sites_considered,
|
||||
"sites_recognized": value.sites_recognized,
|
||||
"transitions_materialized": value.transitions_materialized,
|
||||
"security_objects_constructed": value.security_objects_constructed,
|
||||
"constraints_evaluated": value.constraints_evaluated,
|
||||
"determinate_outcomes": value.determinate_outcomes,
|
||||
"findings_emitted": value.findings_emitted,
|
||||
"relation_status_counts": tuple((item.status.value, item.count) for item in value.relation_status_counts),
|
||||
"unresolved_by_reason": tuple((item.reason, item.count) for item in value.unresolved_by_reason),
|
||||
}
|
||||
|
||||
|
||||
def derive_case_coverage_digest(case_id_digest: str, scope: ScopeKey, coverage: CoverageFunnel) -> str:
|
||||
return canonical.content_digest(
|
||||
{
|
||||
"case_id_digest": case_id_digest,
|
||||
"scope": {"family_id": scope.family_id, "ecosystem": scope.ecosystem},
|
||||
"coverage": _coverage_value(coverage),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _claim_value(value: DefectClaim) -> canonical.JsonValue:
|
||||
location = value.location
|
||||
return {
|
||||
"record_id": value.record.record_id,
|
||||
"basis": value.basis.value,
|
||||
"location": {"path": location.path, "line": location.line, "column": location.column, "symbol": location.symbol},
|
||||
"anchor": (value.change_anchor.path, value.change_anchor.start_line, value.change_anchor.end_line),
|
||||
}
|
||||
|
||||
|
||||
def derive_admission_case_result_digest(value: AdmissionCaseResult) -> str:
|
||||
return canonical.content_digest(
|
||||
{
|
||||
"case_id_digest": value.case_id_digest,
|
||||
"scope": (value.scope.family_id, value.scope.ecosystem),
|
||||
"role": value.role.value,
|
||||
"pair_id_digest": value.pair_id_digest,
|
||||
"evidence_input_digests": tuple(item.input_digest for item in value.evidence_inputs),
|
||||
"defect_claims": tuple(_claim_value(item) for item in value.defect_claims),
|
||||
"satisfactions": tuple((item.question_ids, tuple(record.record_id for record in item.records)) for item in value.satisfactions),
|
||||
"silence": tuple(
|
||||
(item.question_ids, item.reasons, item.evidence_record_digests, item.coverage_digest) for item in value.silence
|
||||
),
|
||||
"proof_attachment_ids": tuple(item.proof_id for item in value.proof_attachments),
|
||||
"coverage": _coverage_value(value.coverage),
|
||||
"input_manifest_digest": value.input_manifest.manifest_digest,
|
||||
"input_receipts": tuple(
|
||||
(item.input_id, item.path, item.opened, item.byte_count, item.content_digest) for item in value.input_receipts
|
||||
),
|
||||
"complexity": (
|
||||
value.complexity.change_id,
|
||||
value.complexity.workload_class,
|
||||
value.complexity.changed_regions,
|
||||
value.complexity.transition_count,
|
||||
value.complexity.changed_bytes,
|
||||
value.complexity.measurement.content_digest,
|
||||
),
|
||||
"budget_derivation_digest": value.budget.derivation_digest,
|
||||
"total_reply_ms": value.total_reply_ms,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def validate_admission_case_shape(value: AdmissionCaseResult) -> None:
|
||||
problem = admission_case_problem(value, AdmissionCaseResult, AdmissionCaseRole)
|
||||
if problem is not None:
|
||||
raise ValueError(f"closed admission case violation: {problem}")
|
||||
|
||||
|
||||
def create_admission_case_result(**values: object) -> AdmissionCaseResult:
|
||||
draft = AdmissionCaseResult(**values, result_digest="0" * 64) # type: ignore[arg-type]
|
||||
validate_admission_case_shape(draft)
|
||||
return AdmissionCaseResult(**values, result_digest=derive_admission_case_result_digest(draft)) # type: ignore[arg-type]
|
||||
|
||||
|
||||
def _scope_value(scope: ScopeKey) -> dict[str, canonical.JsonValue]:
|
||||
return {"family_id": scope.family_id, "ecosystem": scope.ecosystem}
|
||||
|
||||
|
||||
def _binding_value(binding: ReleasedCaseBinding) -> dict[str, canonical.JsonValue]:
|
||||
return {
|
||||
"schema_version": RELEASED_CASE_BINDING_SCHEMA,
|
||||
"case_id_digest": binding.case_id_digest,
|
||||
"scope": _scope_value(binding.scope),
|
||||
"role": binding.role.value,
|
||||
"pair_id_digest": binding.pair_id_digest,
|
||||
"case_result_digest": binding.case_result_digest,
|
||||
"evidence_input_digests": binding.evidence_input_digests,
|
||||
"change_input_digests": binding.change_input_digests,
|
||||
"input_manifest_digest": binding.input_manifest_digest,
|
||||
}
|
||||
|
||||
|
||||
def derive_released_case_binding_digest(binding: ReleasedCaseBinding) -> str:
|
||||
return canonical.content_digest(_binding_value(binding))
|
||||
|
||||
|
||||
def _content_ref_value(reference: canonical.ContentRef) -> dict[str, canonical.JsonValue]:
|
||||
return {
|
||||
"artifact_id": reference.artifact_id,
|
||||
"content_digest": reference.content_digest,
|
||||
"media_type": reference.media_type,
|
||||
"byte_length": reference.byte_length,
|
||||
"retention_policy_digest": reference.retention_policy_digest,
|
||||
"storage_receipt_digest": reference.storage_receipt_digest,
|
||||
}
|
||||
|
||||
|
||||
def _review_value(review: IndependenceAttestation) -> dict[str, canonical.JsonValue]:
|
||||
return {
|
||||
"subject_digest": review.subject_digest,
|
||||
"producer_identity": review.producer_identity,
|
||||
"reviewer_identity": review.reviewer_identity,
|
||||
"conflict_disclosure": _content_ref_value(review.conflict_disclosure),
|
||||
"attestation_digest": review.attestation_digest,
|
||||
}
|
||||
|
||||
|
||||
def _release_payload_value(release: AdmissionCaseRelease) -> dict[str, canonical.JsonValue]:
|
||||
return {
|
||||
"schema_version": ADMISSION_CASE_RELEASE_PAYLOAD_SCHEMA,
|
||||
"release_schema_version": release.schema_version,
|
||||
"population_digest": release.population_digest,
|
||||
"manifest_digest": release.manifest_digest,
|
||||
"authorization_digest": release.authorization_digest,
|
||||
"preparation_digest": release.preparation_digest,
|
||||
"label_release_receipt_digest": release.label_release_receipt_digest,
|
||||
"custodian_identity": release.custodian_identity,
|
||||
"bindings": tuple({**_binding_value(item), "binding_digest": item.binding_digest} for item in release.bindings),
|
||||
}
|
||||
|
||||
|
||||
def derive_admission_case_release_payload_digest(release: AdmissionCaseRelease) -> str:
|
||||
return canonical.content_digest(_release_payload_value(release))
|
||||
|
||||
|
||||
def derive_admission_case_release_digest(release: AdmissionCaseRelease) -> str:
|
||||
return canonical.content_digest(
|
||||
{
|
||||
"schema_version": ADMISSION_CASE_RELEASE_SCHEMA,
|
||||
"release_payload_digest": release.release_payload_digest,
|
||||
"review": _review_value(release.review),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def validate_admission_case_release(
|
||||
manifest: AdmissionManifestV2,
|
||||
denominator: DenominatorPolicy,
|
||||
readiness: Sequence[ReadinessResult],
|
||||
context: AdmissionValidationContext,
|
||||
preparation: AdmissionPreparation,
|
||||
receipt: PopulationUseReceipt,
|
||||
release: AdmissionCaseRelease,
|
||||
cases: Sequence[AdmissionCaseResult],
|
||||
custody: PopulationCustodyPort,
|
||||
resolver: canonical.ContentResolver,
|
||||
) -> AdmissionCaseRelease:
|
||||
"""Validate the custodian's exact post-spend case-to-operational-input mapping."""
|
||||
from openultrasast.core._admission_release import (
|
||||
_validate_header,
|
||||
_validate_policy,
|
||||
_validate_release_shape,
|
||||
_validate_review,
|
||||
validate_case_bindings,
|
||||
)
|
||||
|
||||
try:
|
||||
authorization = validate_pre_label_spend(
|
||||
manifest,
|
||||
denominator,
|
||||
readiness,
|
||||
context,
|
||||
preparation,
|
||||
receipt,
|
||||
custody,
|
||||
resolver,
|
||||
)
|
||||
_validate_policy(context.admission_review_policy)
|
||||
_validate_release_shape(release)
|
||||
_validate_header(manifest, preparation, receipt, authorization, release)
|
||||
_validate_review(release, receipt, context.admission_review_policy, resolver)
|
||||
validate_case_bindings(release, cases, context, custody, resolver)
|
||||
return release
|
||||
except AdmissionCaseReleaseError:
|
||||
raise
|
||||
except (AttributeError, LookupError, TypeError, ValueError) as exc:
|
||||
raise AdmissionCaseReleaseError(f"admission case release refused: {exc}") from exc
|
||||
Reference in New Issue
Block a user