Skip to content

Fix raw path handoffs to re-parsing callers - #12

Merged
wayneeseguin merged 5 commits into
developfrom
fix-raw-path-handoffs
Jul 29, 2026
Merged

Fix raw path handoffs to re-parsing callers#12
wayneeseguin merged 5 commits into
developfrom
fix-raw-path-handoffs

Conversation

@wayneeseguin

Copy link
Copy Markdown
Contributor

Completes the path-vocabulary work started in #10. ParsePath both splits
and unescapes, so everything it returns is a raw Vault path. Six call
sites then handed that raw path to something that parses its argument a
second time, splitting it at the wrong colon.

Consumer Wants Why
Read, Write, deleteEntireSecret mini-language each calls ParsePath
Versions, client.Destroy* raw vaultkv takes literal paths
ConstructSecrets raw the tree walk stores literal paths

Measured behavior

Fixture is a single secret at the literal Vault path secret/we:ird,
which the mini-language writes as secret/we\:ird. Vault 1.13.2, KV v2.

Command Before After
rm 'secret/we\:ird:alpha' exit 0, key not deleted key deleted, sibling kept
mv --deep 'secret/we\:ird' dst exit 0, source left intact source destroyed
cp 'secret/src:k' 'secret/de\:st:kk' cannot write to paths in /path:key notation copies
revert 'secret/we\:ird' 1 no secret exists at path reverts
gen 'secret/we\:ird:pw' cannot write to paths… generates
uuid 'secret/we\:ird:id' cannot write to paths… generates

The first two failed silently: rm reported success while the key
remained readable, and mv --deep reported success while every version of
the source survived with destroyed:false. A colon-free control confirms
mv --deep destroys the source correctly, so the colon is the variable.

Verification

  • make check green; every commit builds and tests green in isolation.
  • Integration suite, Vault 1.13.2: 1725 passing, 0 failing — unchanged
    from develop.
  • All six operations were re-run against a real Vault, each paired with a
    colon-free control to show the fix is specific and does not disturb the
    ordinary path.

Notes for reviewers

cmdRevert had a third handoff on the same line of reasoning — Write
also received the raw path — which is fixed here. It is reachable only on
KV v2, where a revert to an older version actually writes; the KV v1 test
fake short-circuits before it. It is covered by the live v2 run above
rather than by a unit test.

Reverting the fix makes safe gen panic on a nil dereference inside
Secret.Password, because cmdGen treats a key-not-found from the
truncated path as "secret absent" and continues with a nil secret. These
changes make that branch unreachable from gen/uuid, but the underlying
nil deref is untouched and still latent for other callers. Not fixed here:
it is a separate defect and outside this change's scope.

deleteSpecificKey handed the unescaped path ParsePath returned to
Read, Write, and deleteEntireSecret, all of which parse their
argument again. A colon inside the secret path was split a second
time, so the delete landed on a truncated path and left the key in
place. Re-encode the path before each of those calls.
Copy parsed the destination and then handed the unescaped result to
Read and Write, which parse again. A colon in the destination path
was read as a key separator, so the copy failed outright with
"cannot write to paths in /path:key notation". Re-encode the
destination for those two calls; the tree walk and SecretEntry.Copy
keep the literal path they need.
A deep move finished by destroying the source, but passed the path in
the escaped syntax the caller wrote. That call goes straight to Vault,
which takes literal paths, so a source path holding a colon was never
matched and the secret stayed behind while the move reported success.
Unescape the path before destroying it.
revert looked the versions up with the raw argument the user typed,
but the version list is fetched by literal path and never unescaped.
A path holding an escaped colon was reported as a missing secret. Use
the parsed path there and in the message that follows it, and encode
it again for the write, which does parse its argument.
gen and uuid split the path:key argument themselves, which unescapes
the path, then passed the result to Read and Write, which split it
again. A colon in the path made both commands fail with "cannot write
to paths in /path:key notation". Encode the path back before those
calls, so it stays in the same syntax as the branch that takes the
path and key as separate arguments.
@wayneeseguin
wayneeseguin merged commit e4c8264 into develop Jul 29, 2026
@wayneeseguin
wayneeseguin deleted the fix-raw-path-handoffs branch July 29, 2026 16:39
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.

1 participant