PREQ-4481: Diagnose which secret path causes 403 Forbidden failures#71
PREQ-4481: Diagnose which secret path causes 403 Forbidden failures#71
Conversation
When vault-action fails with a 403 Forbidden, automatically diagnose which specific secret path(s) are denied by checking per-path capabilities via Vault's sys/capabilities-self endpoint. This avoids generating dynamic secrets as a side effect and only runs on failure, so there is zero overhead on successful runs.
There was a problem hiding this comment.
Pull request overview
This PR improves debuggability of Vault secret retrieval failures (PREQ-4481) in this composite action by adding a conditional diagnostic step to identify which specific secret paths are denied when hashicorp/vault-action fails with a 403.
Changes:
- Marks the Vault secrets step as
continue-on-errorand adds a follow-up diagnostic step that authenticates via OIDC and checks per-path capabilities. - Emits OK/DENIED status per secret path and fails the action with a clearer error summary when secrets retrieval fails.
- Documents the new 403 (Forbidden) diagnostics behavior in the README.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
action.yaml |
Adds conditional diagnostics on Vault secrets failure using sys/capabilities-self to report denied paths. |
README.md |
Adds FAQ documentation for 403 Forbidden errors and shows example diagnostic output. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The diagnostic step was failing with "invalid audience (aud) claim" because we passed vaultUrl as the audience. hashicorp/vault-action uses getIDToken() without an audience when jwtGithubAudience is not set. Use the same to match Vault's expected bound_audiences.
- Vault API expects paths (array) not path (string) for sys/capabilities-self - Batch all secret paths in a single request to reduce Vault load - Parse response: data[path] for each path, fallback to data.capabilities for single-path
tomverin
left a comment
There was a problem hiding this comment.
Review feedback addressed
-
pathsvspath— Applied. The Vault API expectspaths(array) for/sys/capabilities-self. Updated tobody: JSON.stringify({ paths: secretPaths })and adjusted response parsing to usedata[secretPath] ?? data.capabilities ?? []. -
Batching — Applied. Replaced the per-path loop with a single batched request. All
secretPathsare sent in one call, reducing Vault load and speeding up diagnostics when many secrets are requested. -
Integration test — Addressed via sonar-dummy. The failure path is covered by sonar-dummy PR #563, which runs a workflow that intentionally requests an unauthorized secret (
operations/team/re/kv/data/digicert) and asserts the diagnostic step emits OK/DENIED output. See the Test plan section in the PR description for links to the workflow run.
Summary
Resolves PREQ-4481 — when
hashicorp/vault-actionfails with a 403 Forbidden, the logs show all requested secrets but don't indicate which one caused the failure. This forces users to debug by trial-and-error.This PR adds a diagnostic step that runs only when the Vault secrets step fails. It:
sys/capabilities-self(no side effects — does not read actual secrets or generate dynamic credentials)DENIEDvsOKExample output
Design decisions
continue-on-error: true+if: steps.secrets.outcome == 'failure')sys/capabilities-selfinstead of direct reads: Avoids generating dynamic secrets (Artifactory tokens, GitHub tokens, etc.) as a side effect. Only checks permissions.actions/github-script(already used in the replace step) and Node.js built-infetchRelated
Test plan
What we tested
OKwith their capability (e.g.read)DENIEDVault secrets retrieval failed — 1 path(s) denied: operations/team/re/kv/data/digicert)How we tested
development/kv/data/slack— sonar-dummy has accessoperations/team/re/kv/data/digicert— RE team only, sonar-dummy has no access.github/workflows/test-vault-403-diagnostics.yml— requests both secrets to trigger a 403, then runs the diagnostic stepTest workflow run
sonar-dummy PR #563 — draft PR with the test workflow
Workflow run 22661709735 — validates batched
sys/capabilities-selfAPI and OIDC fix:Checklist
core.setFailed())continue-on-error: true)outputs.vaultis still available on success