Skip to content

secrets/hashivault: reject keyIDs that escape the engine/operation path - #3763

Merged
vangent merged 2 commits into
google:masterfrom
herdiyana256:fix-hashivault-keyid-path-traversal
Jul 31, 2026
Merged

secrets/hashivault: reject keyIDs that escape the engine/operation path#3763
vangent merged 2 commits into
google:masterfrom
herdiyana256:fix-hashivault-keyid-path-traversal

Conversation

@herdiyana256

Copy link
Copy Markdown
Contributor

keeper.Decrypt/keeper.Encrypt (secrets/hashivault/vault.go) build the literal Vault API request path with path.Join(opts.Engine+"/decrypt", keyID) / .../encrypt. path.Join cleans .. segments, so a keyID such as "../../sys/health" makes the actual HTTP request escape the intended engine and operation entirely — confirmed against a fake Vault server, where keyID = "a/../../../auth/token/create" produced a request to /v1/auth/token/create instead of /v1/transit/decrypt/....

This is the only secrets driver in this module that builds a raw path this way: awskms/gcpkms pass their key identifier as a structured field on the cloud SDK's request type (server-side authorization decides what's valid), and azurekeyvault validates keyID with a strict regex before use. hashivault's keyID is exactly the parameter OpenKeeper(client, keyID, opts) is documented to take directly, and Vault's own authorization model is commonly path-prefix ACLs (e.g. a policy scoped to transit/decrypt/tenant-a-* for a specific tenant/key namespace) — so a caller that only controls the key name, not the Vault client's credentials, can reach paths the deploying application never intended to expose.

Adds vaultPath, which joins the prefix and keyID and then verifies the result is still inside that prefix, returning an error instead of sending the request otherwise. tenant-a/../tenant-b (relative navigation that stays within <engine>/<op>/) still works; only escapes past that boundary are rejected.

Decrypt and Encrypt build the Vault API request path with
path.Join(opts.Engine+"/decrypt"|"/encrypt", keyID). path.Join cleans ".."
segments, so a keyID such as "../../sys/health" silently produces a request
outside the intended engine and operation entirely, unlike every other
secrets driver in this module (awskms/gcpkms hand key identification to the
cloud provider's structured API; azurekeyvault validates keyID with a strict
regex). Since Vault's own authorization model is commonly path-prefix ACLs
(e.g. a policy scoped to "transit/decrypt/tenant-a-*"), a caller that only
controls the key name -- the documented use of OpenKeeper's keyID parameter --
could reach a path the deploying application never intended to expose.

Add vaultPath, which joins and then verifies the result is still inside the
given prefix, rejecting the request client-side otherwise.
…r keyID rejection

Decrypt already had a regression test; add the symmetric case for Encrypt
(which goes through the same vaultPath check) and an end-to-end test through
OpenKeeper to confirm a keyID supplied this way is rejected before any
request reaches the server, not just when calling the driver.Keeper directly.
@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.41%. Comparing base (65c466d) to head (5b2a6e6).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3763      +/-   ##
==========================================
+ Coverage   75.38%   75.41%   +0.02%     
==========================================
  Files         104      104              
  Lines       14241    14246       +5     
==========================================
+ Hits        10736    10743       +7     
+ Misses       2768     2766       -2     
  Partials      737      737              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@vangent
vangent merged commit 1aebc0a into google:master Jul 31, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants