Skip to content

Resolve bao:/kv: secret refs embedded in header/env values - #13

Merged
selic merged 1 commit into
mainfrom
fix/embedded-secret-refs
Jul 25, 2026
Merged

Resolve bao:/kv: secret refs embedded in header/env values#13
selic merged 1 commit into
mainfrom
fix/embedded-secret-refs

Conversation

@selic

@selic selic commented Jul 25, 2026

Copy link
Copy Markdown
Member

Problem

Upstream header/env injection resolved a secret ref only when the entire value was the ref (isSecretRef checks value.startsWith("bao:")). A value like:

Authorization: Bearer bao:upstreams/publora#token

— exactly what the admin UI placeholder suggests (Authorization: Bearer bao:upstreams/itglue#token) — starts with Bearer , so it was sent to the upstream verbatim. The upstream received the literal string bao:upstreams/publora#token as the credential instead of the resolved secret.

This surfaced when adding an HTTP upstream (Publora) via the manual "Add server" form: the server rejected the key as malformed because it got the ref text, not the token. Presets work only because their Bearer {{token}} markers are expanded at install time.

Fix

resolveInjectionValue now resolves refs (and ${VAR}) embedded anywhere in the value, in a single left-to-right pass so resolved secret contents are never re-scanned (a secret that happens to contain ${...} or bao:... is not re-processed).

  • Whole-value refs keep the previous behaviour: the secret is returned verbatim, and a malformed whole-value ref (e.g. bao:missing-hash) still throws.
  • Embedded ref tokens use a conservative path/field charset (A-Za-z0-9_./-) so a ref ends cleanly at surrounding punctuation (…#token; more).
  • ${VAR} substitution and the no-store / scheme-mismatch errors are unchanged.

Now Authorization: Bearer bao:upstreams/publora#token resolves to Authorization: Bearer <secret>, matching the UI placeholder's promise — and the token stays in OpenBao (no Bearer prefix baked into the stored secret).

Tests

Added cases for: embedded ref, multiple embedded tokens (ref + ${VAR}), embedded ref with no store configured, and whole-value secret returned verbatim without re-scanning. Full suite: 201 passed, tsc build clean.

🤖 Generated with Claude Code

Header and env injection values only resolved a secret ref when the
*entire* value was the ref. A value like "Authorization: Bearer
bao:upstreams/x#token" — exactly what the admin UI placeholder suggests —
was sent to the upstream verbatim, so the upstream received the literal
"bao:..." string instead of the secret.

resolveInjectionValue now resolves refs (and ${VAR}) embedded anywhere in
the value, in a single left-to-right pass so resolved secret contents are
never re-scanned. Whole-value refs keep the previous behaviour: the secret
is returned verbatim and a malformed whole-value ref still errors.

Ref tokens use a conservative path/field charset (A-Za-z0-9_./-) so a ref
ends cleanly at surrounding punctuation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@selic
selic merged commit 7b94069 into main Jul 25, 2026
1 check passed
@selic
selic deleted the fix/embedded-secret-refs branch July 25, 2026 04:37
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