How must an instance detect transitive dependency poisoning through shared upstream packages?

**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session depends on tools, libraries, or services whose transitive dependencies may include shared upstream packages, and a compromised upstream package can inject malicious behavior through the entire dependency tree—even when each agent's direct dependencies are clean. Does not apply to sessions with strictly vendored dependencies or dependencies verified via supply-chain attestation.

Core Distinction

The separation between **direct dependency safety** (each agent's direct dependencies are trusted) and **transitive closure safety** (the full dependency tree, including shared transitive packages, is trusted). Relying on direct safety creates the **Diamond Dependency Attack**: two agents each trust their direct dependencies, both of which depend on a shared transitive package, and the transitive package is the attack vector—every agent's direct dependencies are clean, but the shared upstream is compromised.

What Was Observed

In dependency-heavy agent architectures, failing to audit transitive closure produces three primary failure modes:

  1. **Shared Upstream Compromise:** A commonly-used transitive package (logging, serialization, HTTP) is compromised. Every agent that transitively depends on it is affected, regardless of their direct dependency choices.
  2. **Diamond Injection:** Two agents depend on different direct packages that depend on the same transitive package. The transitive package is compromised, and both agents inherit the compromise without any direct dependency being vulnerable.
  3. **Deep Transitive Poisoning:** The compromised package is several levels deep, so it does not appear in any agent's direct dependency list. The compromise is invisible without transitive audit.

Recorded Elements

  1. **`transitive-dependency-closure-receipt`:** For each agent, the full transitive closure of its dependencies (every package reachable through the dependency graph), so shared upstreams are visible.
  2. **`shared-upstream-witness-signature`:** For each transitive package, which agents' dependencies include it, so shared exposure is measurable.
  3. **`upstream-integrity-attestation-receipt`:** For each transitive package, its supply-chain integrity (signature, provenance, or hash verification), so compromise is detectable.
  4. **`closure-drift-signature`:** For each agent, changes in its transitive closure over time (new packages, version changes), so unexpected drift is surfaced.

Condition of Validity

* **Applies when:** The session depends on tools with transitive dependencies, the dependencies can be compromised upstream, and downstream behavior can be affected. * **Does not apply when:** Dependencies are strictly vendored, dependencies are verified via supply-chain attestation, or the closure is trivially small and audited.

Decision Boundary

  1. **Does the session use dependencies with transitive closure?** If no, direct dependency handling applies. If yes, proceed.
  2. **Is `transitive-dependency-closure-receipt` complete?** If yes, closure is visible. If no, transitive exposure is unknown.
  3. **Does `upstream-integrity-attestation-receipt` verify every transitive package?** If yes, closure is verified. If no, transitive packages are unverified.
  4. **Conversely, when can a session ignore transitive verification?** Only when the closure is verified strictly from the same trust domain (internal registry), or all transitive packages are pinned with content-addressed hashes.

Operational Effect

  1. **Shared Upstream Detection:** Common transitive dependencies are surfaced.
  2. **Diamond Injection Awareness:** Cross-agent shared upstreams are flagged.
  3. **Deep Poisoning Detection:** Packages several levels deep are audited, not just direct dependencies.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class TransitiveVerdict(Enum):
    NO_DEPENDENCIES = auto()
    TRANSITIVE_VERIFIED = auto()
    SHARED_UPSTREAM_UNVERIFIED = auto()
    POISONING_SUSPECTED = auto()

@dataclass(frozen=True)
class DependencyPackage:
    package_id: str
    version: str
    integrity_verified: bool
    is_direct: bool

@dataclass(frozen=True)
class DependencyClosure:
    closure_id: str
    packages: tuple[DependencyPackage, ...]
    closure_complete: bool
    shared_with_other_agents: frozenset[str]

@dataclass(frozen=True)
class ClosurePolicy:
    require_integrity_for_all_transitive: bool

@dataclass(frozen=True)
class TransitiveManifest:
    verdict: TransitiveVerdict
    directive: str
    manifest_digest: str

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

    @classmethod
    def evaluate_closure(
        cls, closure: DependencyClosure, policy: ClosurePolicy
    ) -> TransitiveManifest:
        
        # Gate 1: No dependencies
        if not closure.packages:
            digest = cls._hash(json.dumps({
                "closure": closure.closure_id,
                "packages": "NONE"
            }, sort_keys=True))
            return TransitiveManifest(
                verdict=TransitiveVerdict.NO_DEPENDENCIES,
                directive=f"PROCEED: No dependencies for {closure.closure_id}.",
                manifest_digest=digest
            )

        # Gate 2: Closure completeness
        if not closure.closure_complete:
            return TransitiveManifest(
                verdict=TransitiveVerdict.SHARED_UPSTREAM_UNVERIFIED,
                directive=f"HALT: Closure {closure.closure_id} incomplete, transitive exposure unknown.",
                manifest_digest=cls._hash(f"INCOMPLETE:{closure.closure_id}")
            )

        # Gate 3: Shared upstream with unverified integrity
        transitive_unverified = [
            p.package_id for p in closure.packages
            if not p.is_direct and not p.integrity_verified
        ]

        if transitive_unverified and policy.require_integrity_for_all_transitive:
            return TransitiveManifest(
                verdict=TransitiveVerdict.POISONING_SUSPECTED,
                directive=f"HALT: {len(transitive_unverified)} transitive packages unverified.",
                manifest_digest=cls._hash(f"UNVERIFIED:{closure.closure_id}")
            )

        # Gate 4: Shared exposure
        if closure.shared_with_other_agents and transitive_unverified:
            return TransitiveManifest(
                verdict=TransitiveVerdict.SHARED_UPSTREAM_UNVERIFIED,
                directive=f"FLAG: Shared upstream exposure to {len(closure.shared_with_other_agents)} agents with unverified transitive packages.",
                manifest_digest=cls._hash(f"SHARED:{closure.closure_id}")
            )

        # Gate 5: Verified
        digest = cls._hash(json.dumps({
            "closure": closure.closure_id,
            "packages": tuple(
                {"id": p.package_id, "version": p.version, "verified": p.integrity_verified}
                for p in closure.packages
            ),
            "shared": tuple(sorted(closure.shared_with_other_agents))
        }, sort_keys=True))
        
        return TransitiveManifest(
            verdict=TransitiveVerdict.TRANSITIVE_VERIFIED,
            directive=f"PROCEED: {len(closure.packages)} packages verified.",
            manifest_digest=digest
        )