fix: vault-import.sh: pipe-separator in ops_data/paths_to_write silently truncates secret values containing | (#898)
Replace the `|`-delimited string accumulators with bash associative and
indexed arrays so any byte may appear in a secret value.
Two sites used `|` as a delimiter over data that includes user secrets:
1. ops_data["path:key"]="value|status" — extraction via `${data%%|*}`
truncated values at the first `|` (silently corrupting writes).
2. paths_to_write["path"]="k1=v1|k2=v2|..." — split back via
`IFS='|' read -ra` at write time, so a value containing `|` was
shattered across kv pairs (silently misrouting writes).
Fix:
- Split ops_data into two assoc arrays (`ops_value`, `ops_status`) keyed
on "vault_path:vault_key" — value and status are stored independently
with no in-band delimiter. (`:` is safe because both vault_path and
vault_key are identifier-safe.)
- Track distinct paths in `path_seen` and, for each path, collect its
kv pairs into a fresh indexed `pairs_array` by filtering ops_value.
`_kv_put_secret` already splits each entry on the first `=` only, so
`=` and `|` inside values are both preserved.
Added a bats regression that imports values like `abc|xyz`, `p1|p2|p3`,
and `admin|with|pipes` and asserts they round-trip through Vault
unmodified. Values are single-quoted in the .env so they survive
`source` — the accumulator is what this test exercises.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
391aaa99a5
commit
27baf496db
2 changed files with 74 additions and 37 deletions
|
|
@ -199,6 +199,46 @@ setup() {
|
|||
echo "$output" | jq -e '.data.data.token == "MODIFIED-LLAMA-TOKEN"'
|
||||
}
|
||||
|
||||
# --- Delimiter-in-value regression (#898) ────────────────────────────────────
|
||||
|
||||
@test "preserves secret values that contain a pipe character" {
|
||||
# Regression: previous accumulator packed values into "value|status" and
|
||||
# joined per-path kv pairs with '|', so any value containing '|' was
|
||||
# silently truncated or misrouted.
|
||||
local piped_env="${BATS_TEST_TMPDIR}/dot-env-piped"
|
||||
cp "$FIXTURES_DIR/dot-env-complete" "$piped_env"
|
||||
|
||||
# Swap in values that contain the old delimiter. Exercise both:
|
||||
# - a paired bot path (token + pass on same vault path, hitting the
|
||||
# per-path kv-pair join)
|
||||
# - a single-key path (admin token)
|
||||
# Values are single-quoted so they survive `source` of the .env file;
|
||||
# `|` is a shell metachar and unquoted would start a pipeline. That is
|
||||
# orthogonal to the accumulator bug under test — users are expected to
|
||||
# quote such values in .env, and the accumulator must then preserve them.
|
||||
sed -i "s#^FORGE_REVIEW_TOKEN=.*#FORGE_REVIEW_TOKEN='abc|xyz'#" "$piped_env"
|
||||
sed -i "s#^FORGE_REVIEW_PASS=.*#FORGE_REVIEW_PASS='p1|p2|p3'#" "$piped_env"
|
||||
sed -i "s#^FORGE_ADMIN_TOKEN=.*#FORGE_ADMIN_TOKEN='admin|with|pipes'#" "$piped_env"
|
||||
|
||||
run "$IMPORT_SCRIPT" \
|
||||
--env "$piped_env" \
|
||||
--sops "$FIXTURES_DIR/.env.vault.enc" \
|
||||
--age-key "$FIXTURES_DIR/age-keys.txt"
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# Verify each value round-trips intact.
|
||||
run curl -sf -H "X-Vault-Token: ${VAULT_TOKEN}" \
|
||||
"${VAULT_ADDR}/v1/secret/data/disinto/bots/review"
|
||||
[ "$status" -eq 0 ]
|
||||
echo "$output" | jq -e '.data.data.token == "abc|xyz"'
|
||||
echo "$output" | jq -e '.data.data.pass == "p1|p2|p3"'
|
||||
|
||||
run curl -sf -H "X-Vault-Token: ${VAULT_TOKEN}" \
|
||||
"${VAULT_ADDR}/v1/secret/data/disinto/shared/forge"
|
||||
[ "$status" -eq 0 ]
|
||||
echo "$output" | jq -e '.data.data.admin_token == "admin|with|pipes"'
|
||||
}
|
||||
|
||||
# --- Incomplete fixture ───────────────────────────────────────────────────────
|
||||
|
||||
@test "handles incomplete fixture gracefully" {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue