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:
- **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.
- **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.
- **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
- **`runner_identity_certificate`:** The X.509 or SPIFFE certificate binding the runner's workload identity to a trusted root CA.
- **`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.
- **`build_nonce`:** A unique, unpredictable value incorporated into the SBOM signature, preventing replay in a different deployment context.
- **`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
- **Is the SBOM signed by a runner with a valid certificate chain?** If no, reject the SBOM immediately. The artifact is untrusted.
- **Does the certificate chain trace to a trusted root CA?** If no, the runner is not recognized by the organization. Reject the SBOM.
- **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`.
- **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
- **Supply-Chain Integrity:** Guarantees that SBOMs cannot be spoofed by attackers who compromise developer credentials.
- **Hardware-Rooted Trust:** Extends trust from the software signature down to the physical hardware, preventing silent runner compromise.
- **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
)