This release includes 3 security fixes for security teams reviewing exposed deployments.
Topics
Affected surfaces
ReleasePort's take
Moderate signalThe GSM backend now enforces read‑only access and will deny startup if write permissions are granted.
Why it matters: If your deployment grants write permissions to the GSM backend, it will be blocked on start; ensure configurations use only read‑only mode (severity 90).
Summary
AI summaryUpdates 0.8.0], 2026-07-18, Deprecated, and docs/adrs/ADR-0018-gcp-secret-manager-backend.md across a mixed release.
Changes in this release
| Type | Severity | Summary | CVE |
|---|---|---|---|
| Security | Critical |
GSM backend enforces read‑only; denies start if write permissions granted. GSM backend enforces read‑only; denies start if write permissions granted. Source: llm_adapter@2026-07-19 Confidence: high |
— |
| Security | High |
Notes host allowlist prevents confused‑deputy binding redirections on GCP. Notes host allowlist prevents confused‑deputy binding redirections on GCP. Source: llm_adapter@2026-07-19 Confidence: high |
— |
| Security | High |
Bare‑note shorthand now only matches valid hostnames, fixing silent binding breakage. Bare‑note shorthand now only matches valid hostnames, fixing silent binding breakage. Source: llm_adapter@2026-07-19 Confidence: high |
— |
| Feature | Medium |
Adds Google Secret Manager backend (`backend.type: gsm`). Adds Google Secret Manager backend (`backend.type: gsm`). Source: llm_adapter@2026-07-19 Confidence: high |
— |
| Feature | Medium |
Introduces Honeytoken tripwire and `honeytoken_triggered` audit event (audit contract v2 → v3). Introduces Honeytoken tripwire and `honeytoken_triggered` audit event (audit contract v2 → v3). Source: llm_adapter@2026-07-19 Confidence: high |
— |
| Feature | Medium |
Adds `/healthz` liveness/readiness endpoint for orchestrator traffic control. Adds `/healthz` liveness/readiness endpoint for orchestrator traffic control. Source: llm_adapter@2026-07-19 Confidence: high |
— |
| Feature | Medium |
Adds `avp-bindings` skill and Claude Code plugin packaging for AI‑assisted binding authoring. Adds `avp-bindings` skill and Claude Code plugin packaging for AI‑assisted binding authoring. Source: llm_adapter@2026-07-19 Confidence: high |
— |
| Deprecation | Low |
Deprecates `agent_vault_proxy.bws_notes` module; use `agent_vault_proxy.notes_binding`. Deprecates `agent_vault_proxy.bws_notes` module; use `agent_vault_proxy.notes_binding`. Source: llm_adapter@2026-07-19 Confidence: high |
— |
| Bugfix | Medium |
Fixes concurrency race in OAuth2 refresh write‑back path. Fixes concurrency race in OAuth2 refresh write‑back path. Source: llm_adapter@2026-07-19 Confidence: high |
— |
| Bugfix | Medium |
Fixes case‑normalization bug in notes host allowlist matching. Fixes case‑normalization bug in notes host allowlist matching. Source: llm_adapter@2026-07-19 Confidence: high |
— |
Full changelog
[0.8.0], 2026-07-18
Added
- Google Secret Manager backend (
backend.type: gsm, ADR-0018). Third built-in backend afterbwsandstatic. Reads secret values from GSM with one RESTaccesscall per cache miss (stdliburllibfor the call;google-auth— a single first-party dep in a newgsmextra — mints/refreshes access tokens only, no gRPC SDK). Keyless by design: ADC + service-account impersonation, Workload Identity Federation, or plain user ADC — there is deliberately no key-file config field, andreject_ambient_keyrefuses a downloaded service-account key surfaced via ADC. Secure-by-default is a boot invariant, not a doc note:self_check: denyrefuses to start under an over-broad identity (enumeration + project-levelversions.access+ write/admintestIamPermissionsprobes),avp gcp-setupgrants only per-secretsecretAccessorand hard-fails a project/folder/org bind, and_assert_in_scoperefuses to fetch any name outsidesecret_prefix. Host binding lives at the vault via theavp-bindingannotation (binding_source: gsm_notes, generalized tonotes), reusing the backend-agnostic notes parser. New operator CLIs:avp gcp-setupand read-onlyavp doctor --probe-gcp. Hardened across four cross-vendor security review passes. avp-bindingsskill + Claude Code plugin packaging. The repo ships an Anthropic-format skill atskills/avp-bindings/that walks an AI agent through authoring notes/annotation bindings (propose-only — it never sees or stores the secret). Packaged as a plugin via.claude-plugin/plugin.json+marketplace.json, so Claude Code users install it with/plugin marketplace add inflightsec/agent-vault-proxythen/plugin install avp@agent-vault-proxy; a manual~/.claude/skills/copy/symlink path is documented for other agents.- Honeytoken tripwire +
honeytoken_triggeredaudit event (audit contract v2 → v3, ADR-0019). Anysecrets:entry can sethoneytoken: trueto become a canary: plant its placeholder somewhere tempting and bind it to a trap host you control. Because the agent only ever holds the placeholder, the honeytoken's real value never enters its address space — but any use of that placeholder (injected, denied, scope-violated, or aimed at a non-bound host) makes the proxy emit a secondhoneytoken_triggeredrecord immediately after the normalinject_decision, under the same synchronousfsync(G6 ordering preserved per record). Shipped off-box, one trigger anywhere in a fleet means that machine is being walked for credentials — the signal fires on intent, before any real value moves. The event carries onlyrequest_id,binding_name,dest_host, andunderlying_reason— no secret material, header, body, or query string, per the §4.4 minimization contract. Emission is centralized inAuditWriter(keyed on the operator-flagged secret names, rebuilt per config reload), so it covers every decision path with no call site missed and is a no-op when no honeytoken is configured. BumpsAUDIT_CONTRACT_VERSIONto 3;docs/architecture.md§4.4 documents the event;bindings.example.yamlcarries a workedDECOY_AWS_PRODexample. 10 tests. /healthzliveness/readiness endpoint (roadmap: Observability). A plain-HTTP request through the proxy to the reserved sentinel hosthealthz.agent-vault-proxy.invalidat path/healthzis answered by the addon directly — never proxied upstream, emits no audit (health polling would otherwise flood the log). Returns200 {"status": "ok"}once config, backend client, and audit writer are all live;503 {"status": "starting"}before that, so an orchestrator holds traffic until AVP can actually broker. The body carries no version/build identifier — an unauthenticated endpoint should not hand out fingerprint/CVE-matching surface. The.invalidTLD (RFC 6761) can never collide with a real binding, and the gate is host and path — a real proxied request to/healthzon a bound host still injects normally. Intercepted before the destination allow-list so the probe isn't403'd as unmatched. The DockerHEALTHCHECKnow uses it (pure-Python, no curl/shell), replacing the bare-TCP probe that only proved the socket was open — not that AVP could broker. Prometheus metrics surface still pending (seedocs/ROADMAP.md).
Security
- Bare-note shorthand gated on hostname shape (live-incident fix). The ADR-0018 §4 Tier 0 bare-string shorthand (a note that is just
api.example.com) treated ANY string note as a host. Real vaults carry free-text human descriptions in the note field ("HackerOne API identifier", "GCP project ID"); underbinding_source: botheach description became a garbage host binding that shadowed the secret's realbindings.yamlhost — silently un-brokering the whole fleet (caught by the release smoke test on the first 0.8.0 deploy; the daemon was healthy but injecting nothing). The shorthand now fires only when the note is actually shaped like a hostname (≥2 dot-separated DNS labels, FQDN/IPv4/*.-wildcard); descriptive text returnsNoBinding— no binding, not a garbage one. Explicithost:mappings are untouched. Regression tests feed the parser real-world description notes and pin both directions. - File-side notes host allowlist — annotations may only narrow, never add a host (ADR-0024). Closes the confused-deputy ADR-0018's fourth cross-vendor pass surfaced: on GCP,
secretmanager.secrets.update(edit a secret'savp-bindingannotation) andversions.access(read its value) are independently grantable, so a principal who can write the annotation but not read the secret can point it at a host they control — AVP then reads with its own identity and injects there, exfiltrating a value the attacker could never read directly. New opt-in top-levelnotes_host_allowlistbounds where notes/annotation-sourced bindings may route. Absent (the default) is byte-identical to prior behaviour — the zero-config North Star is untouched, pinned by a no-allowlist regression test. When set andbinding_sourceisnotes/both, a notes-supplied host outside the list has its binding entry dropped fail-closed at config-build, and a later request carrying that placeholder toward the dropped host is denied under the distincthost_not_in_allowlistaudit reason (kept apart frominvalid_binding_metadata). Annotations may still narrowmethods/pathswithin an allowed host; file-secrets:hosts are trusted by definition and never rejected (includingboth-mode unions); multi-host fan-outs (ADR-0021) are judged per host; wildcard entries ride the existingallow_wildcard_hostsopt-in and a note can never broaden an exact entry. Source-agnostic — structural on GSM where the deputy exists, defense-in-depth on BWS.avp doctor --probe-gcpdowngrades itsannotation-trustWARN to OK once the allowlist is set. - ADR-0017 deferred hardening series closed (Amendment). All five items the v0.7.0 notes tracked as "hardening patch series remaining": (1) the SSRF guard's IP-literal short-circuit can no longer swallow its own block verdict — blocked literals raise without any DNS consultation; (2) token-exchange redirects are refused at the opener — any 3xx maps to
token_endpoint_status:<code>with no retry and the Location target is never contacted (replaces the post-callgeturl()check, which could only distrust a redirect after the redirected host had been visited); (3)refresh_token_rotated:pendingis fsynced before the vault PUT (crash-window ordering — Oracle F2); (4) write-back value-precondition: an operator's manual mid-flight rotation is detected and never clobbered (newBackendWriteConflictError→ outcomewrite_back_conflict; implements Oracle F3 as a stronger value variant, since the BWS SDK has no conditional PUT andrevisionDatefalse-fires on note edits); (5) per-bindingwrite_back_min_interval_secondsfloor (default 60 s,0disables) bounds vault PUT pressure under forced-rotation storms (outcomewrite_back_rate_limited— Oracle F4). Newrefresh_token_rotatedoutcome values only — no new event types, no audit contract bump. ~20 new tests including an on-the-wire proof that the opener refuses redirects. - Concurrency audit + cross-vendor review hardening. A concurrency review of the oauth2_refresh write-back path closed a read-modify-write race on the per-binding write-back rate-limit bookkeeping — now serialized under a
threading.Lock(_write_back_lock), so two in-flight rotations of the same binding can no longer both slip past thewrite_back_min_interval_secondsfloor. A cross-vendor review pass separately caught a case-normalization bug:notes_host_allowlistentries are now lowercased at config-load, so a mixed-case allowlist entry can no longer silently fail to match the already-lowercased note host and drop a legitimately-allowed binding. Newtests/test_concurrency_audit.py(five concurrency surfaces) andtests/test_addon_oauth2_concurrent.py(cold-start OAuth race). - Closed audit event-type set +
emit()choke-point guard (ADR-0023).AUDIT_EVENT_TYPESinaudit.pyenumerates every event type the audit stream may carry;AuditWriter.emit()refuses (raises) any unlisted type. A new event type now requires a conscious edit to the closed set — the review point where it must prove no-secret-material and gain stateful no-leak coverage. Type-level guard; the per-type field allowlist is pinned intests/test_audit_event_type_contract.py. - GSM read-only enforcement + annotation-trust advisory (ADR-0018, 4th cross-vendor pass).
self_checknow runs a thirdtestIamPermissionsprobe for write/admin permissions (secretmanager.secrets.update/versions.add/secrets.delete) and refuses to start (deny) when AVP's identity holds any — AVP is a read-only broker, so a write grant is over-privileged (a compromised proxy could tamper with the vault or rewrite the routing annotations). Separately,avp doctor --probe-gcpnow emits anannotation-trustWARN wheneverbinding_sourceisnotes/both: becausesecretmanager.secrets.update(editavp-binding) andversions.access(read the value) are independently grantable on GCP, a metadata-only writer can redirect an in-namespace secret to a host they control — a confused-deputy that abuses AVP's legitimate read access. Operators must lock annotation-write to the value-read trust tier. Both probes are best-effort (an inconclusivetestIamPermissionsnever hard-blocks). 5 tests.
Deprecated
agent_vault_proxy.bws_notesmodule path. The note/annotation parser was renamed toagent_vault_proxy.notes_binding— it is backend-agnostic (BWSnotesand GSMavp-bindingannotations), so thebws_prefix misled. The old path stays a re-export shim for back-compat but now emits aDeprecationWarning; import fromnotes_bindinginstead. Removal in a future release.
Breaking Changes
- Renamed note/annotation parser module from `agent_vault_proxy.bws_notes` to `agent_vault_parser.notes_binding`; old path deprecated and will be removed.
Security Fixes
- Bare‑note shorthand now requires hostname shape to prevent garbage host bindings.
- Notes host allowlist (`notes_host_allowlist`) prevents confused‑deputy redirection of secrets via annotation edits.
- GSM read‑only enforcement: `self_check` denies start if AVP identity has write/admin permissions on GCP Secret Manager.
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
About AVP
All releases →Related context
Related tools
Beta — feedback welcome: [email protected]