Skip to content

Releases: inflightsec/agent-vault-proxy

v0.4.3

02 Jun 06:24

Choose a tag to compare

Single-line follow-up to v0.4.2. No proxy code changes — v0.4.2's proxy runtime was already byte-for-byte identical to v0.4.1 (all v0.4.2 diffs were in tests/, pyproject.toml, README.md, and CHANGELOG.md).

Fixed

  • src/agent_vault_proxy/__init__.py __version__ now agrees with pyproject.toml. The v0.4.2 release bumped pyproject.toml to 0.4.2 but left __version__ = "0.4.1" in src/agent_vault_proxy/__init__.py. The published v0.4.2 wheel installed correctly via pip (PyPI metadata = 0.4.2) but reported agent_vault_proxy.__version__ == "0.4.1" at runtime. The wheel-smoke job in .github/workflows/test.yml is designed to catch exactly this skew (its comment lists it as the canonical failure mode) and went red on main after the v0.4.2 push.
  • scripts/smoke-test-wheel.sh entry-point check switched from python -m agent_vault_proxy --help to an import-only check matching .github/workflows/test.yml's wheel-smoke job. mitmdump's argparse behavior on --help with a -s addon flag is fragile across versions and returns exit 1 in some environments, masking real wheel issues as entry-point failures. The CI job already learned this lesson and switched to importing agent_vault_proxy, agent_vault_proxy.__main__.main, agent_vault_proxy.addon, agent_vault_proxy.config, and agent_vault_proxy.backends.BACKEND_REGISTRY; the local pre-release tool now does the same. Without this, scripts/pre-release.sh step 11 (which invokes smoke-test-wheel.sh) couldn't go green.
  • Release-tooling fixes from v0.4.2 are preserved unchanged in v0.4.3 (tests/pypi-smoke/run.sh NEGATIVE assertion accepting "type":"deny", tests/pypi-smoke/docker-compose.yml empty-default for TEST_SECRET).
  • Release handoff now gates on scripts/pre-release.sh between commit and tag — that script's section 3 (Version constants agree?) does the exact pyproject vs __init__.py comparison that would have caught the v0.4.2 skew. Order matters: pre-release.sh's first check is git status --porcelain for a clean working tree, so it runs AFTER git commit, not before. This is a process fix, not a code fix; recorded here for traceability.

Changed

  • Version pointers in README.md, tests/pypi-smoke/README.md, tests/pypi-smoke/run.sh usage examples, docker-compose.yml image tag, and scripts/smoke-test-wheel.sh usage examples bumped to 0.4.3. The historical reference to "v0.4.2 changelog" in tests/pypi-smoke/run.sh:324 is preserved — the harness fix is documented in the v0.4.2 entry.

v0.4.2

01 Jun 22:12

Choose a tag to compare

Release-tooling-only patch on top of v0.4.1. No proxy code changes — v0.4.1's substitution and audit guarantees are unchanged. v0.4.1 was correctly published to PyPI and the core wire-format behavior was verified by the smoke harness's positive test on the published wheel. The release.yml pypi-install-smoke gate did its job: it caught a real signal mismatch and held back the GitHub Release until investigation was complete. The investigation found the proxy was behaving correctly; the harness's negative-assertion grep needed updating.

Fixed (release tooling)

  • tests/pypi-smoke/run.sh NEGATIVE assertion now accepts the "type":"deny" audit event in addition to the "decision":"denied" / "decision":"forwarded_unmodified" shapes. The addon has two distinct deny paths for an unbound destination: (1) the unmatched_destination_policy: deny policy gate fires BEFORE placeholder analysis when the request's host is in no binding at all and emits {"type":"deny","reason":"unmatched_destination",...}; (2) the destination_not_in_binding check inside the inject path fires when a placeholder IS present but the matched secret's bindings don't cover the request host and emits {"type":"inject_decision","decision":"denied",...}. The pypi-smoke negative test aims at example.invalid (in no binding at all), so the policy gate fires first. The previous grep only knew path (2) and the smoke went red despite the proxy correctly returning 403 + auditing the deny. tests/docker-e2e/run.sh:236 already greps the "type":"deny" shape, so this was a pypi-smoke-only regression introduced when the pypi-smoke harness was first added in v0.4.1.
  • tests/pypi-smoke/docker-compose.yml uses ${TEST_SECRET:-} instead of the strict ${TEST_SECRET:?...} for the avp-init env reference. The strict form blocked docker compose down -v teardown when TEST_SECRET wasn't exported, because compose interpolates all referenced vars at parse time even for down. The empty default unblocks teardown without weakening the test: run.sh still exports the real value before compose up, and a manual compose up without TEST_SECRET produces a broken secrets.yml that fails the positive assertion at run time rather than at compose time.

Changed

  • README.md and tests/pypi-smoke/README.md version pointers bumped from 0.4.1 to 0.4.2. The "Status" prose now leads with the v0.4.2 framing (release-tooling patch only; v0.4.1 guarantees unchanged) before recapping v0.4.1 and v0.4.0.

v0.4.0

29 May 23:43

Choose a tag to compare

First public release.

The adapter refactor (originally proposed for v0.3.0 in docs/adapter-architecture.md) is bundled with composite secrets and shipped together as v0.4.0.

Added

  • Composite secret bindings (inject.template + compose:). Bindings can now assemble a credential from 1-4 atomic BWS values at fetch time, instead of requiring the operator to pre-concatenate values in BWS (which made single-key rotation silently stale). Templates use Jinja2 syntax evaluated through ImmutableSandboxedEnvironment with a strict filter/function whitelist (b64encode, b64decode, sha256, urlencode, hmac_sha256, hmac_sha512). An AST-level deny-by-default validator at config-load rejects every Jinja2 construct outside the small set the proxy supports, class-walk escapes, control flow, attribute traversal, subscript, arithmetic are all structurally impossible. See bindings.example.yaml for a Jira / Atlassian Cloud example and docs/architecture.md §4.2 for the reference table and architectural sketch.
  • CachingSecretsClient.composite_fetch(names): atomically fetches multiple secrets under a single generation snapshot; empty BWS values raise BackendUnavailableError (never compose partial credentials); flush during assembly raises _StaleAfterFlushError so the caller restarts.
  • Same-UUID heuristic: when two distinctly-named compose entries resolve to the same value (suggesting an operator typo pointing both names at one BWS secret), the addon logs a one-shot WARNING to its own logger. The warning never includes the actual secret value.
  • jinja2 >= 3.1 is now a declared direct dependency (previously transitive via mitmproxy).
  • SecretsBackend protocol + adapter architecture. bindings.yaml uses a discriminated backend: {type: bws, config: {...}} block. agent_vault_proxy.backends.bws.BitwardenBackend is the reference implementation; new backends (1Password Service Accounts, HashiCorp Vault, Doppler, etc.) plug in by registering a type discriminator. agent_vault_proxy.caching.CachingSecretsClient provides generic TTL+jitter+LRU+singleflight caching on top of any backend. Protocol design in docs/adapter-architecture.md.
  • mypy in CI + pre-commit. Strict-ish config (warn_return_any, warn_unused_ignores, no_implicit_optional, check_untyped_defs); third-party libs without stubs (mitmproxy, bitwarden_sdk, yaml) explicitly silenced.
  • Ruff C90 cyclomatic-complexity gate (max-complexity = 10). Three pre-existing functions carry # noqa: C901 with one-line provenance justifications.
  • Hash-pinned dev dependencies. requirements-dev.lock is now hash-pinned alongside requirements.lock. New helper scripts: scripts/check-lockfile-hashes.py (zero-dep structural check - every pinned package must carry a --hash=sha256: continuation) and scripts/check-lockfile-drift.sh (re-runs uv pip compile with the 7-day cooldown and diffs against committed). Both wired into pre-commit; the CI verify-lockfile job runs the same hash check before the drift diff.

Changed

  • Config: InjectSpec.format is now Optional[str] and a peer of the new InjectSpec.template: Optional[str]. Exactly one of the two must be set per binding (validator-enforced). Existing inject.format bindings continue to work unchanged.
  • Addon: request handlers now capture (config, client, audit) at handler entry, preventing a mid-request configure() reload from producing a torn state (Silas F3).
  • Dependency advances captured by the 7-day-cooldown regen: bitwarden-sdk 2.0.0 → 2.1.0, certifi 2026.4.22 → 2026.5.20, click 8.4.0 → 8.4.1, ruff 0.15.13 → 0.15.14.
  • Security CI stack refactored to drop GitHub Advanced Security dependency. CodeQL (Python SAST) replaced with Bandit + Semgrep (community rulesets: p/security-audit + p/python + p/secrets). Gitleaks (which required a paid license for org-owned repos as of Aug 2022) replaced with TruffleHog (AGPL-3, free for everyone). OSV-Scanner switched from the action wrapper to direct binary install (the wrapper passes scan-args as a single positional arg, breaking multi-flag invocation). All security jobs now gate the merge via job-fail instead of SARIF-upload to the Security tab, workflows portable to any git host, no GHAS settings to babysit. The same three Docker-based scanners (TruffleHog, OSV-Scanner, Semgrep) also run in pre-commit so most commits hit the same gates locally before push.

Removed

  • agent_vault_proxy.bws module (the BwsClient facade) and the top-level bws: config block deprecation shim in config.py. v0.4.0 is the first public release; there are no public v0.2.0 users to migrate. New bindings.yaml files must use the backend: {type: bws, config: {...}} form (shown in bindings.example.yaml).
  • CodeQL job from .github/workflows/security.yml (replaced by Bandit + Semgrep: see above).
  • Gitleaks pre-commit hook + CI job (replaced by TruffleHog - see above).