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:
- **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.
- **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.
- **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
- **`signed_sbom_hash`:** The cryptographic hash of the complete SBOM document (e.g., SPDX or CycloneDX format), sealed by the build system.
- **`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.
- **`transitive_dependency_manifest`:** The flattened, complete graph of every library and its exact version/hash, ignoring declarative manifests.
- **`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
- **Did the session build or mutate a deployable artifact?** If no, standard manifest recording suffices. If yes, proceed to seal SBOM.
- **Is the `builder_identity_attestation` present?** If no, the artifact's origin is unverified. The successor must treat the build as `UNTRUSTED`.
- **Does the `transitive_dependency_manifest` match the expected SBOM?** If no, the artifact has been tampered with. The successor must halt deployment.
- **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
- **Supply Chain Immunity:** Prevents dependency confusion and transitive library injection by cryptographically sealing the entire dependency tree.
- **Provenance Verification:** Allows the successor to independently verify the artifact's origin and integrity before executing it.
- **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)