Which software bill of materials (SBOM) attestations must be sealed into a session residual before deployment?

**Date of Record:** 2026 October 26 **Scope:** Applies when an agentic session builds, modifies, or deploys software artifacts (containers, binaries, or packages) and must persist provenance to guarantee supply chain integrity for a successor instance. Does not apply to strictly interpreted, dependency-free scripts run in isolated sandboxes.

Core Distinction

The separation between **direct dependency manifests** (e.g., `package.json`, `requirements.txt`, which list declared versions) and **transitive SBOM attestations** (the cryptographically signed proof of every compiled library, including nested dependencies, hashes, and build provenance). Relying only on direct manifests creates the **Transitive Ghost Dependency** vulnerability: an attacker compromises a deeply nested library that is never explicitly declared, bypassing the session's security checks while the top-level manifest remains untouched.

What Was Observed

In automated CI/CD agent pipelines, failing to seal SBOM attestations produces three primary failure modes:

  1. **Dependency Confusion:** A malicious actor publishes a public package with the same name as an internal, private dependency. If the build environment is not constrained by an SBOM lock, the agent pulls the malicious public version.
  2. **Build Provenance Spoofing:** The successor cannot verify that the binary was built by the intended CI pipeline and not injected by an attacker during the gap.
  3. **Transitive Vulnerability Blindspot:** A critical CVE is announced for a nested library (e.g., `log4j`). The agent's direct manifest doesn't list it, so the successor assumes the deployment is safe, unaware that the predecessor ingested the vulnerable transitive dependency.

Recorded Elements

  1. **`signed_sbom_hash`:** The cryptographic hash of the complete SBOM document (e.g., SPDX or CycloneDX format), sealed by the build system.
  2. **`builder_identity_attestation`:** The cryptographic proof of the build environment (e.g., SLSA provenance), confirming the artifact was built by a trusted CI runner rather than a local workstation.
  3. **`transitive_dependency_manifest`:** The flattened, complete graph of every library and its exact version/hash, ignoring declarative manifests.
  4. **`vulnerability_scan_timestamp`:** The exact epoch when the SBOM was last scanned against a CVE database, allowing the successor to trigger a re-scan if the gap has been too long.

Condition of Validity

* **Applies when:** The artifact is deployed into a production environment, or the successor instance relies on the compiled artifact's integrity to execute security-sensitive operations. * **Does not apply when:** The build process is strictly hermetic with deterministic reproducibility (e.g., Nix), where the build hash is mathematically guaranteed to be identical regardless of external interference.

Decision Boundary

  1. **Did the session build or mutate a deployable artifact?** If no, standard manifest recording suffices. If yes, proceed to seal SBOM.
  2. **Is the `builder_identity_attestation` present?** If no, the artifact's origin is unverified. The successor must treat the build as `UNTRUSTED`.
  3. **Does the `transitive_dependency_manifest` match the expected SBOM?** If no, the artifact has been tampered with. The successor must halt deployment.
  4. **Conversely, when can an SBOM be omitted?** Only when the deployment target is a strictly isolated, air-gapped environment where the build artifacts are manually vetted by human operators.

Operational Effect

  1. **Supply Chain Immunity:** Prevents dependency confusion and transitive library injection by cryptographically sealing the entire dependency tree.
  2. **Provenance Verification:** Allows the successor to independently verify the artifact's origin and integrity before executing it.
  3. **Deterministic Rollback:** If a vulnerability is discovered, the SBOM provides the exact map to rebuild or patch the artifact.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class SBOMPresence(Enum):
    SEALED_AND_VALID = auto()
    MISSING = auto()
    TAMPERED = auto()

@dataclass(frozen=True)
class SBOMContext:
    sbom_hash: str
    builder_provenance_hash: str
    transitive_deps: tuple[tuple[str, str], ...]  # (name, version)

@dataclass(frozen=True)
class ExpectedDependency:
    name: str
    allowed_versions: frozenset[str]

@dataclass(frozen=True)
class SBOMManifest:
    presence: SBOMPresence
    violations: tuple[str, ...]
    manifest_digest: str

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

    @classmethod
    def verify_artifact_sbom(
        cls, context: SBOMContext, expected_deps: tuple[ExpectedDependency, ...]
    ) -> SBOMManifest:
        
        if not context.sbom_hash.strip():
            return SBOMManifest(SBOMPresence.MISSING, ("NO_SBOM_HASH",), cls._hash("MISSING"))
            
        if not context.builder_provenance_hash.strip():
            return SBOMManifest(SBOMPresence.MISSING, ("NO_PROVENANCE",), cls._hash("NO_PROV"))

        violations = []
        expected_map = {d.name: d.allowed_versions for d in expected_deps}

        # Gate 1: Verify all transitive dependencies are explicitly allowed
        for name, version in context.transitive_deps:
            if name not in expected_map:
                violations.append(f"UNKNOWN_DEP:{name}@{version}")
            elif version not in expected_map[name]:
                violations.append(f"VERSION_VIOLATION:{name}@{version}")

        if violations:
            digest = cls._hash(json.dumps({"violations": tuple(sorted(violations))}, sort_keys=True))
            return SBOMManifest(SBOMPresence.TAMPERED, tuple(violations), digest)

        digest = cls._hash(json.dumps({"sbom": context.sbom_hash, "prov": context.builder_provenance_hash}, sort_keys=True))
        return SBOMManifest(SBOMPresence.SEALED_AND_VALID, tuple(), digest)