feat(env-injection): value filters on resolved references (#138)#143
Merged
Conversation
Completes item 3 of the #115 epic. A credential reference may end with "| filter" to transform the value before injection: ${pass:api/token | base64} # base64 a token ${pass:api | basicauth} # base64("username:password") for Basic auth --set 'AUTH=api|basicauth' # same, on the mapping forms Filters: base64 (standard), base64url (URL-safe), and basicauth (combines a credential's own username + password). One filter per reference. Design: - Parse once in the shared SplitPath (envmap): peel an optional "| filter", trim only when a pipe is present (filterless refs unchanged byte-for-byte), validate the name fail-closed, reserve the first '|'. New 4-return signature propagated to ParseSetSpec, parseTemplate, ParseManifest, so every surface (--set, manifest, ${pass:...}) gets it at once. - Carry via a new Filter field on envmap.Mapping and TemplateRef. - Apply client-side in resolver.ResolveValuesFiltered, after the backend returns raw values — the agent's values-only protocol is untouched, so output is identical in direct and daemon modes. basicauth resolves username and password via the normal value path (batched) and base64s "user:pass". - Single-source filter registry in internal/envmap/filter.go: IsKnownFilter (parse-time validation) and ApplyValueFilter (apply-time) both derive from one map, so they can't drift. Filters are not applied to the positional <service> form (never parsed by SplitPath); on --set the '|' must be shell-quoted. Both documented. Tests: SplitPath filter parsing (spaces, colon form, empty/unknown/chaining/ multi-segment errors), filter registry, ResolveValuesFiltered (base64 + basicauth + unknown), and integration for inject (base64/basicauth/unknown), export (--set base64, filter-then-quote), and an agent-path case proving the filter is applied client-side while the daemon serves the raw value. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014kVLjbUL4F4CkoA7RbMYy8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #138. Completes item 3 of the #115 env-injection epic.
Summary
A credential reference may end with
| filterto transform the resolved value before injection. It's parsed in the one shared grammar, so it works everywhere a reference is accepted —${pass:...}templates (inject,exec --env-file),--set(exec/export), and.pass-cli.tomlmanifests.Filters (one per reference):
base64base64urlbasicauthbase64("username:password")from the credential (takes no field)Design
SplitPath(internal/envmap): peel an optional| filter, trim only when a pipe is present (filterless refs are byte-for-byte unchanged), validate the name fail-closed, reserve the first|. New 4-return signature propagated toParseSetSpec,parseTemplate,ParseManifest.Filterfield onenvmap.MappingandTemplateRef.resolver.ResolveValuesFiltered, after the backend returns raw values — so the agent's values-only protocol is untouched and output is identical in direct and daemon modes.basicauthresolves username + password through the normal (batched) value path and base64suser:pass.internal/envmap/filter.go):IsKnownFilter(parse-time) andApplyValueFilter(apply-time) both derive from one map, so they can't drift.Intentional limitations (documented)
<service>form is never parsed bySplitPath, so filters apply only to--set,--frommanifests, and${pass:...}.--setthe|must be shell-quoted.--set/manifests this is caught before the vault is opened.Test
SplitPathfilter parsing (spaces, legacy colon, empty/unknown/chaining/multi-segment errors, basicauth-no-field); the filter registry;ResolveValuesFiltered(base64 + basicauth + unknown propagation).inject(base64, basicauth, unknown-fails-closed),export --set(base64, proving filter-then-shell-quote order), and an agent-path case proving the filter is applied client-side while the daemon serves the raw value.go build✓ ·go vet ./...✓ ·go test ./...✓ ·go test -tags=integration(filter set) ✓ ·golangci-lint run ./...→ 0 issues ✓Out of scope (future)
Filter chaining (
| trim | base64),base64url-nopad, a general two-reference join operator, and more filters (hex, upper/lower, trim).🤖 Generated with Claude Code