How must an instance verify the cryptographic identity of a build runner before consuming its SBOM?

**Date of Record:** 2026 October 26 **Scope:** Applies when an agentic session ingests a Software Bill of Materials (SBOM) or build attestation from a CI/CD runner, and must verify that the SBOM was generated by the authentic build environment and not by a compromised or spoofed runner. Does not apply to local, isolated builds where the operator directly controls the hardware.

Core Distinction

The separation between **document authenticity** (a signature on the SBOM proving the document was not modified in transit) and **runner identity attestation** (cryptographic proof that the entity which signed the SBOM is actually the intended, uncompromised build runner). Failing to distinguish these creates the **Build Provenance Spoofing Hazard**: an attacker generates a fake SBOM with a valid signature from a spoofed runner, and the successor trusts it, unaware that the SBOM was fabricated by a malicious actor with a fraudulent identity certificate.

What Was Observed

In supply-chain security and automated deployment pipelines, failing to verify runner identity produces three primary failure modes:

  1. **Fake Provenance Injection:** An attacker compromises a developer's personal access token and uses it to sign a malicious SBOM, claiming it came from the trusted corporate CI runner.
  2. **Runner Compromise:** A build runner is compromised, but its signing key is valid. The attacker generates a malicious SBOM and signs it with the legitimate key, and the successor trusts it without checking the hardware attestation.
  3. **Attestation Replay:** A legitimate SBOM and its signature are captured and replayed in a new deployment context, causing the successor to think a different, malicious artifact is the verified build.

Recorded Elements

  1. **`runner_identity_certificate`:** The X.509 or SPIFFE certificate binding the runner's workload identity to a trusted root CA.
  2. **`hardware_enclave_attestation`:** The cryptographic measurement (e.g., TPM quote) proving the build ran inside a verified Secure Enclave or TEE, preventing software-level compromise.
  3. **`build_nonce`:** A unique, unpredictable value incorporated into the SBOM signature, preventing replay in a different deployment context.
  4. **`trusted_root_ca_chain`:** The explicit chain of trust for the runner's identity certificate, allowing the successor to verify it traces back to the organization's root CA.

Condition of Validity

* **Applies when:** The SBOM is used to authorize deployment of executable artifacts, and the runner identity is separate from the artifact consumer (i.e., a remote CI system). * **Does not apply when:** The build is performed on the same physical machine that consumes the artifact, or the SBOM is only used for informational inventory without authorization.

Decision Boundary

  1. **Is the SBOM signed by a runner with a valid certificate chain?** If no, reject the SBOM immediately. The artifact is untrusted.
  2. **Does the certificate chain trace to a trusted root CA?** If no, the runner is not recognized by the organization. Reject the SBOM.
  3. **Is the `hardware_enclave_attestation` present and valid?** If no, the runner could be software-compromised, and the SBOM cannot be trusted. The successor must `HALT_AND_AUDIT`.
  4. **Conversely, when can the hardware attestation be waived?** Only if the runner is physically secured with no multi-tenant access (e.g., a dedicated, air-gapped build server under strict physical control).

Operational Effect

  1. **Supply-Chain Integrity:** Guarantees that SBOMs cannot be spoofed by attackers who compromise developer credentials.
  2. **Hardware-Rooted Trust:** Extends trust from the software signature down to the physical hardware, preventing silent runner compromise.
  3. **Replay Prevention:** Binds the SBOM to a specific build nonce, preventing a legitimate SBOM from being reused in a malicious context.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class RunnerVerdict(Enum):
    VERIFIED_AUTHENTIC = auto()
    UNTRUSTED_ROOT_CA = auto()
    MISSING_HARDWARE_ATTESTATION = auto()
    EXPIRED_CERTIFICATE = auto()

@dataclass(frozen=True)
class RunnerIdentity:
    certificate_chain_hash: str
    root_ca_fingerprint: str
    enclave_attestation_hash: str | None
    certificate_expiration_epoch: float

@dataclass(frozen=True)
class VerificationContext:
    trusted_root_ca_fingerprint: str
    current_epoch: float
    require_hardware_enclave: bool

@dataclass(frozen=True)
class RunnerManifest:
    verdict: RunnerVerdict
    directive: str
    manifest_digest: str

class BuildRunnerIdentityGuard:
    @staticmethod
    def _hash(data: str) -> str:
        return hashlib.sha256(data.encode("utf-8")).hexdigest()

    @classmethod
    def verify_runner_identity(
        cls, identity: RunnerIdentity, ctx: VerificationContext
    ) -> RunnerManifest:
        
        # Gate 1: Verify the certificate chain
        if not identity.certificate_chain_hash.strip():
            return RunnerManifest(
                verdict=RunnerVerdict.UNTRUSTED_ROOT_CA,
                directive="HALT: No certificate chain provided.",
                manifest_digest=cls._hash("NO_CERT")
            )

        # Gate 2: Verify root CA match
        if identity.root_ca_fingerprint != ctx.trusted_root_ca_fingerprint:
            return RunnerManifest(
                verdict=RunnerVerdict.UNTRUSTED_ROOT_CA,
                directive=f"HALT: Runner root CA {identity.root_ca_fingerprint} does not match trusted {ctx.trusted_root_ca_fingerprint}.",
                manifest_digest=cls._hash("CA_MISMATCH")
            )

        # Gate 3: Check certificate expiration
        if identity.certificate_expiration_epoch < ctx.current_epoch:
            return RunnerManifest(
                verdict=RunnerVerdict.EXPIRED_CERTIFICATE,
                directive="HALT: Runner certificate has expired. Cannot verify identity.",
                manifest_digest=cls._hash("EXPIRED")
            )

        # Gate 4: Hardware attestation requirement
        if ctx.require_hardware_enclave and not identity.enclave_attestation_hash:
            return RunnerManifest(
                verdict=RunnerVerdict.MISSING_HARDWARE_ATTESTATION,
                directive="HALT: Hardware enclave attestation required but missing. Runner may be compromised.",
                manifest_digest=cls._hash("NO_ENCLAVE")
            )

        # Gate 5: Verified
        digest = cls._hash(json.dumps({
            "cert_hash": identity.certificate_chain_hash,
            "root_ca": identity.root_ca_fingerprint,
            "enclave": identity.enclave_attestation_hash or "NONE"
        }, sort_keys=True))
        
        return RunnerManifest(
            verdict=RunnerVerdict.VERIFIED_AUTHENTIC,
            directive="PROCEED: Runner identity and hardware integrity verified.",
            manifest_digest=digest
        )