diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8bf85ab..eca9c8b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,8 +63,18 @@ jobs: with: python-version: "3.12" - - name: Install dependencies - run: python -m pip install --upgrade pip setuptools && pip install -e ".[dev]" "agent-governance-toolkit[full]>=4.1" + - name: Install dependencies + run: python -m pip install --upgrade pip setuptools && pip install -e ".[dev]" + + # AGT 4.1 currently constrains cryptography below 49, while the runtime + # requires 50+ for published security fixes. Keep the scanner isolated + # so its tooling constraints cannot downgrade the package under test, + # then override that stale upper bound inside the ephemeral scanner too. + - name: Install governance tooling in an isolated environment + run: | + python -m venv .agt-venv + .agt-venv/bin/pip install "agent-governance-toolkit[full]>=4.1" + .agt-venv/bin/pip install --upgrade --no-deps "cryptography>=50.0" - name: Generate evidence file run: python scripts/gen_agt_evidence.py @@ -72,11 +82,11 @@ jobs: # Blocking gate: the [full] toolkit provides the ASI coverage modules # (10/10) and the evidence declares cA2A's governed capabilities, so strict # verify is COMPLETE. A regression that drops coverage or governance fails CI. - - name: AGT governance verify (strict) - run: agt verify --evidence agt-evidence.json + - name: AGT governance verify (strict) + run: .agt-venv/bin/agt verify --evidence agt-evidence.json - - name: Save attestation JSON - run: agt --json verify --evidence agt-evidence.json > agt-attestation.json + - name: Save attestation JSON + run: .agt-venv/bin/agt --json verify --evidence agt-evidence.json > agt-attestation.json - name: Upload governance artifacts uses: actions/upload-artifact@v7 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2615854..b41e8a3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -77,17 +77,27 @@ jobs: with: python-version: "3.12" - - name: Install package and AGT - run: python -m pip install --upgrade pip && pip install -e "." "agent-governance-toolkit[full]>=4.1" + - name: Install package + run: python -m pip install --upgrade pip && pip install -e "." + + # Isolate AGT's tooling dependencies from the release artifact. AGT 4.1 + # constrains cryptography below 49; cA2A requires 50+ security fixes. The + # ephemeral scanner is upgraded too so the release gate runs no known- + # vulnerable cryptography build while that upstream bound is stale. + - name: Install governance tooling in an isolated environment + run: | + python -m venv .agt-venv + .agt-venv/bin/pip install "agent-governance-toolkit[full]>=4.1" + .agt-venv/bin/pip install --upgrade --no-deps "cryptography>=50.0" - name: Generate evidence file run: python scripts/gen_agt_evidence.py - name: AGT governance verify (strict) - run: agt verify --evidence agt-evidence.json + run: .agt-venv/bin/agt verify --evidence agt-evidence.json - name: Save attestation JSON - run: agt --json verify --evidence agt-evidence.json > agt-attestation.json + run: .agt-venv/bin/agt --json verify --evidence agt-evidence.json > agt-attestation.json - name: Attach evidence to release if: github.event_name == 'release' diff --git a/CHANGELOG.md b/CHANGELOG.md index 58f4d04..ce83cba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 version and `latest` tags, keyless signing, and provenance attestation. All third-party container actions are pinned to immutable commits. +- Raised dependency floors past newly disclosed vulnerable releases: + `cryptography>=50.0` (PYSEC-2026-3552/3553/3554), `aiohttp>=3.14.3` + (PYSEC-2026-3545/3546/3547) for the A2A SDK extra, and + `pymdown-extensions>=11.0.1` (PYSEC-2026-3654) for documentation builds. +- Isolated the AGT governance CLI in its own virtual environment. AGT 4.1 + constrains `cryptography<49`, so installing it alongside cA2A could either + downgrade the runtime below the security floor or make releases unresolvable. + The ephemeral AGT environment also overrides that stale upper bound with + `cryptography>=50.0`; the strict verification path is tested against it. + - **A delegation chain was a bearer credential: any party holding a copy was granted the leaf's authority.** The inbound path verified signatures, continuity, attenuation, depth and replay, then granted, without ever requiring the caller to demonstrate a relationship to the chain it presented. `PeerRequest` had no field that could carry such a proof, and `subject` — an Ed25519 public key — was only ever compared as a string for continuity, never used as a key. Chains are published deliberately: handed to auditors for offline verification, embedded in provenance DAGs, and shipped in `examples/`. So the credential intended for publication was the credential that granted authority. A chain lifted from any of those and replayed verbatim was accepted, and the provenance record emitted afterwards named the legitimate subject, so the audit trail attributed the call to the wrong party. Nothing was forged, so nothing failed a check and nothing anomalous reached a log; verbatim replay leaves no tamper evidence to find. `CREDENTIAL_REPLAY` does not cover it, catching only a duplicate `credential_id` inside one chain rather than replay of a whole valid chain by a different party. diff --git a/pyproject.toml b/pyproject.toml index d06baa2..f754c14 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ ] requires-python = ">=3.11" dependencies = [ - "cryptography>=42.0", + "cryptography>=50.0", "pyyaml>=6.0", "cedarpy==4.8.7", # TRACE record models + RFC 8785 signing, reused rather than reimplemented @@ -48,6 +48,7 @@ tpm = [ # the base install stays dependency-light. a2a-sdk = [ "a2a-sdk>=1.1,<2", + "aiohttp>=3.14.3", ] dev = [ "pytest>=8.0", @@ -63,6 +64,7 @@ dev = [ # So tests/unit/test_a2a_sdk_bridge.py runs in CI rather than skipping. A # bridge whose tests only ever skip is a bridge nobody has exercised. "a2a-sdk>=1.1,<2", + "aiohttp>=3.14.3", ] [project.scripts] diff --git a/requirements-docs.txt b/requirements-docs.txt index a46009b..c413f1f 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -2,6 +2,6 @@ mkdocs>=1.6.1 mkdocs-material>=9.7.7 mkdocs-minify-plugin>=0.8 mkdocs-llmstxt>=0.5,<1.0 -pymdown-extensions>=10.21.3 +pymdown-extensions>=11.0.1 mkdocstrings[python]>=1.0.6 griffe>=2.1.0 diff --git a/tests/unit/test_dependency_security_floors.py b/tests/unit/test_dependency_security_floors.py new file mode 100644 index 0000000..27094fa --- /dev/null +++ b/tests/unit/test_dependency_security_floors.py @@ -0,0 +1,36 @@ +"""Keep dependency floors above versions with known release-blocking advisories.""" + +from __future__ import annotations + +import tomllib +from pathlib import Path + + +def _project() -> dict: + with Path("pyproject.toml").open("rb") as stream: + return tomllib.load(stream)["project"] + + +def test_runtime_cryptography_floor_includes_2026_security_fixes() -> None: + assert "cryptography>=50.0" in _project()["dependencies"] + + +def test_a2a_sdk_extra_cannot_resolve_vulnerable_aiohttp() -> None: + extras = _project()["optional-dependencies"] + assert "aiohttp>=3.14.3" in extras["a2a-sdk"] + assert "aiohttp>=3.14.3" in extras["dev"] + + +def test_docs_floor_excludes_vulnerable_pymdown_extensions() -> None: + requirements = Path("requirements-docs.txt").read_text(encoding="utf-8").splitlines() + assert "pymdown-extensions>=11.0.1" in requirements + + +def test_governance_tooling_cannot_downgrade_runtime_dependencies() -> None: + for workflow in ("ci.yml", "release.yml"): + contents = Path(".github/workflows", workflow).read_text(encoding="utf-8") + assert "python -m venv .agt-venv" in contents + assert '.agt-venv/bin/pip install "agent-governance-toolkit[full]>=4.1"' in contents + assert '.agt-venv/bin/pip install --upgrade --no-deps "cryptography>=50.0"' in contents + assert 'pip install -e ".[dev]" "agent-governance-toolkit' not in contents + assert 'pip install -e "." "agent-governance-toolkit' not in contents