# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/how-does-an-instance-detect-a-confused-deputy-in-delegated-agent-authorization-chains.md
# Remnant Warrant:    https://corpusfetch.com/c/how-does-an-instance-detect-a-confused-deputy-in-delegated-agent-authorization-chains.warrant.json
# Markdown SHA-256:   1ec62057c228687851b728c103132a69e563c58c6b348baf1b034987715f6952
# Code AST SHA-256:   09b91bea44086f1894315ed7dddd762b56d08b491f5bdeccfd945d65d03fb31a
# Verified Classes:   ConfusedDeputyError
# Defensive Guards:   4 fail-closed invariants (zero external deps)

class ConfusedDeputyError(RuntimeError):
    """Raised when a delegated request cannot be bound to a verifiable principal."""
    pass


def authorize_terminal_request(credential: dict, request_principal: str, resource: str) -> None:
    """
    Terminal-resource enforcement point for a delegation chain.

    A credential that lacks a principal claim, or whose principal claim does not
    match the principal named in the request, is an unbound credential and must
    be rejected. Absence of a principal claim is NOT "unrestricted access" --
    in a multi-principal chain it is the confused-deputy failure mode.
    """
    claimed_principal = credential.get("principal")
    if claimed_principal is None:
        raise ConfusedDeputyError(
            f"credential for {resource!r} carries no principal binding; "
            f"delegate forwarded its own authority on behalf of {request_principal!r}"
        )
    if claimed_principal != request_principal:
        raise ConfusedDeputyError(
            f"principal mismatch on {resource!r}: credential bound to "
            f"{claimed_principal!r}, request asserts {request_principal!r}"
        )
    # Scope check happens only after binding is verified.
    if resource not in credential.get("scope", []):
        raise ConfusedDeputyError(
            f"credential bound to {claimed_principal!r} lacks scope for {resource!r}"
        )


def forward_delegated_call(delegate_credential: dict, request_principal: str, resource: str) -> None:
    """Delegate-side: derive a principal-scoped credential before forwarding."""
    if "principal" not in delegate_credential:
        raise ConfusedDeputyError(
            "delegate cannot derive a principal-scoped credential: "
            "its own credential is unbound and must not be forwarded verbatim"
        )
    derived = dict(delegate_credential)
    derived["principal"] = request_principal
    authorize_terminal_request(derived, request_principal, resource)
