# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/which-invariants-prevent-skill-injection-when-tool-manifests-are-signed-but-capability-allowlists-are-evaluated-at-the-wrong-layer.md
# Remnant Warrant:    https://corpusfetch.com/c/which-invariants-prevent-skill-injection-when-tool-manifests-are-signed-but-capability-allowlists-are-evaluated-at-the-wrong-layer.warrant.json
# Markdown SHA-256:   5005525fabb675283ccc71e3080c36c58e2a625571e10e257e6972df09ffcf8d
# Code AST SHA-256:   9caf59ee0590d355f4298caf0d4540ea4099435fd5fc46eb717f19018a0f2874
# Verified Classes:   CapabilityLayerMismatch, Session
# Defensive Guards:   3 fail-closed invariants (zero external deps)

import posixpath
from dataclasses import dataclass
from typing import FrozenSet


class CapabilityLayerMismatch(RuntimeError):
    """Raised when an allowlist check would be evaluated against a nominal
    (declared) capability instead of the resolved effect at the enforcement layer."""


@dataclass(frozen=True)
class Session:
    allowlist: FrozenSet[str]  # e.g. {"read:fs:/data/inputs"}


def authorize(session: Session, declared_cap: str, raw_arg: str, enforcement_layer: str) -> str:
    # Enforce the invariant: binding precedes authorization, and enforcement
    # must occur at the effect layer (sandbox), not the planner.
    if enforcement_layer != "sandbox":
        raise CapabilityLayerMismatch(
            f"allowlist evaluated at '{enforcement_layer}'; must be 'sandbox' "
            f"to observe resolved effect for declared '{declared_cap}'"
        )

    # Bind and canonicalize the argument BEFORE checking the allowlist.
    resolved = posixpath.normpath(raw_arg)
    if not resolved.startswith("/"):
        raise CapabilityLayerMismatch(f"non-canonical resolved path: {resolved!r}")

    resolved_cap = f"read:fs:{resolved}"

    # The check must be against the resolved capability, not the declared one.
    if resolved_cap not in session.allowlist:
        raise CapabilityLayerMismatch(
            f"resolved capability {resolved_cap!r} not in allowlist "
            f"(declared was {declared_cap!r}); signature validity does not "
            f"imply authorization"
        )
    return resolved_cap


# Demonstration of the boundary failure the invariant prevents:
sess = Session(allowlist=frozenset({"read:fs:/data/inputs"}))
try:
    # declared cap matches allowlist, but resolved path escapes the subtree.
    authorize(sess, "read:fs:/data/inputs", "/data/inputs/../../etc/shadow", "sandbox")
except CapabilityLayerMismatch as e:
    print("blocked:", e)
