fleetd #635 follow-up: redact() masks block-scalar continuations + passphrase; --set failures stop echoing the value (defects 7 and 8, criteria 15a/15b/16)
CI / shell-tests (pull_request) Failing after 7s
CI / contract (pull_request) Successful in 1m42s
CI / build (pull_request) Failing after 1m50s

Defect 7 (comment 17670): redact() only masked a line that itself started with a
secret-looking key, so a YAML block scalar's value leaked on the lines that
followed the key while the key line right above it printed a reassuring
"<redacted>". Fixed by tracking the masked key's own indentation and masking
every following line indented deeper than it, stopping once indentation returns
to the key's level or shallower; the diff's leading +/-/space marker is stripped
before indentation is measured, per the comment's own pitfall. "passphrase" is
now also in the key-name backstop.

Defect 8 (comment 17673): apply_set_pairs echoed the operator's full
"path=value" input, unredacted, in both of its yq-failure die messages — a
failing --set with a secret-looking value printed that value right back. Fixed
to print only the path; deliberately not routed through redact, which would
pass a non-"key: value"-shaped string straight through.

Adds acceptance criteria 15a (block-scalar continuation), 15b (passphrase key),
and 16 (failing --set never echoes its value) to scripts/test-config-edit.sh,
each with a positive control proving the relevant line really was in the
printed output before asserting the secret is absent. All three confirmed RED
against the pre-fix code and GREEN after, in isolation, before being folded
into the full suite (16 criteria + 3 extras, exit 0).

Also updates PR #636's description per comment 17671: the redact() sentence now
names the continuation-masking rule and says plainly that the key-name list is
a backstop, never a complete list.
This commit is contained in:
Dai Ha
2026-10-01 18:47:00 +02:00
parent 096f08c866
commit d7f94cafa2
2 changed files with 182 additions and 13 deletions
+124
View File
@@ -382,6 +382,124 @@ test_restore_message_names_the_searched_directory() {
|| fail "--restore with a real backup present must put the file back byte for byte"
}
# ------------------------- acceptance criterion 15a: block-scalar continuation lines are redacted
# fleetd #635 follow-up (ticket comment 17670, defect 7) — redact() used to look only AT the key
# line. A YAML block scalar (`|`) puts its value on the lines that FOLLOW the key, each indented
# deeper than it, so the real secret flowed through untouched while the key line right above it
# printed a reassuring "<redacted>" — worse than no redaction, because the marker stops a reader
# from looking further. The edited key here ("retries") sits directly next to the block scalar,
# well inside diff -u's default 3-line context window, so the printed hunk is GUARANTEED to
# include the secret's lines — placing the edit further away would let this pass today even
# without the fix, proving nothing (the ticket comment's own warning, from the lead's first
# reproduction attempt). The positive control runs FIRST: without it, "the secret never entered
# the diff at all" would pass identically to "it entered and was correctly redacted".
new_fixture_block_scalar() {
local dir
dir="$(mktemp -d "$TMP/fixture.XXXXXX")"
cat > "$dir/fleetd.yaml" <<'YAML'
bind:
host: 127.0.0.1
port: 19999
broker:
uri: amqp://user:hunter2@host/vhost
auth:
token: |
FAKELEAK-BLOCK-SCALAR
retries: 1
profiles:
sonnet:
weight: 3
maxLoad: 5
YAML
: > "$dir/fleetd.out"
printf '%s' "$dir"
}
test_block_scalar_continuation_is_redacted() {
local dir
dir="$(new_fixture_block_scalar)"
start_run "$dir" 5 --set '.auth.retries=2'
sleep 1
printf 'config reloaded\n' >> "$dir/fleetd.out"
collect_run "$dir"
assert_equals 0 "$RUN_RC" "block-scalar case reload exit code"
# Positive control FIRST: the key's own (masked) line must really be in the printed diff, or the
# negative assertion right after proves nothing — see the comment above this test.
assert_contains "token:" "$RUN_OUTPUT" "block-scalar case: the key's line must be in the printed diff"
assert_contains "<redacted>" "$RUN_OUTPUT" "block-scalar case: redaction must be proven to have run on real content"
assert_not_contains "FAKELEAK-BLOCK-SCALAR" "$RUN_OUTPUT" "block-scalar case: the block scalar's VALUE must never leak"
}
# ------------------------------------- acceptance criterion 15b: "passphrase" is also recognised
# "passphrase" was in none of TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|URI|_KEY (ticket comment
# 17670). This is a plain key:value line, not a block scalar — kept in its OWN fixture and OWN
# function, separate from criterion 15a, so that a failure in one case can never mask a failure in
# the other (a single combined test would abort under `set -e` at its first failing assertion,
# and the second case would then never even run).
new_fixture_passphrase() {
local dir
dir="$(mktemp -d "$TMP/fixture.XXXXXX")"
cat > "$dir/fleetd.yaml" <<'YAML'
bind:
host: 127.0.0.1
port: 19999
broker:
uri: amqp://user:hunter2@host/vhost
auth:
passphrase: FAKELEAK-PASSPHRASE
retries: 1
profiles:
sonnet:
weight: 3
maxLoad: 5
YAML
: > "$dir/fleetd.out"
printf '%s' "$dir"
}
test_passphrase_key_is_redacted() {
local dir
dir="$(new_fixture_passphrase)"
start_run "$dir" 5 --set '.auth.retries=2'
sleep 1
printf 'config reloaded\n' >> "$dir/fleetd.out"
collect_run "$dir"
assert_equals 0 "$RUN_RC" "passphrase case reload exit code"
assert_contains "passphrase:" "$RUN_OUTPUT" "passphrase case: the key's line must be in the printed diff"
assert_contains "<redacted>" "$RUN_OUTPUT" "passphrase case: redaction must be proven to have run on real content"
assert_not_contains "FAKELEAK-PASSPHRASE" "$RUN_OUTPUT" "passphrase case: the passphrase VALUE must never leak"
}
# ----------------------------------- acceptance criterion 16: a failing --set must not echo value
# fleetd #635 follow-up (ticket comment 17673, defect 8) — apply_set_pairs used to echo the FULL
# "$kv" (path=value, exactly as typed) in its yq-failure messages, so a broken --set with a
# secret-looking value printed that value right back out. The path alone is what the positive
# control proves is still there — it is what the operator needs to fix their command — and the
# negative assertion proves the value itself never appears. Kept to exactly this one failure
# shape (an invalid yq path/expression), matching the ticket's own reproduction.
test_failing_set_does_not_echo_its_value() {
local dir rc=0
dir="$(new_fixture)"
cp "$dir/fleetd.yaml" "$dir/pre-edit.yaml"
"$EDIT" --dry-run --set '.broker.["bad=FAKELEAK-SETVALUE' \
--config "$dir/fleetd.yaml" --log "$dir/fleetd.out" --wait-seconds 2 \
> "$dir/stdout.log" 2>&1 || rc=$?
RUN_OUTPUT="$(cat "$dir/stdout.log")"
[ "$rc" -ne 0 ] || fail "a --set with an invalid yq expression must exit non-zero, got 0"
cmp -s "$dir/fleetd.yaml" "$dir/pre-edit.yaml" \
|| fail "a failing --set must install nothing — the live fixture changed"
# Positive control FIRST: the path must still be in the message, or the negative assertion right
# after proves nothing (the message could simply have disappeared entirely).
assert_contains '.broker.["bad' "$RUN_OUTPUT" "the failure message must still name the PATH"
assert_not_contains "FAKELEAK-SETVALUE" "$RUN_OUTPUT" "the failure message must NEVER echo the VALUE"
}
# dry-run must never touch the live file and must still redact.
test_dry_run_never_installs_and_redacts() {
local dir before
@@ -451,6 +569,12 @@ echo "== acceptance criterion 12: the file mode survives an edit and a restore =
test_file_mode_survives_edit_and_restore
echo "== acceptance criterion 14: the restore message names the directory actually searched =="
test_restore_message_names_the_searched_directory
echo "== acceptance criterion 15a: a block scalar's continuation lines are redacted =="
test_block_scalar_continuation_is_redacted
echo "== acceptance criterion 15b: a passphrase key is also recognised =="
test_passphrase_key_is_redacted
echo "== acceptance criterion 16: a failing --set must not echo its value =="
test_failing_set_does_not_echo_its_value
echo "== extra: dry-run never installs, and redacts =="
test_dry_run_never_installs_and_redacts
echo "== extra: --check is read-only and always exits 0 =="