fleetd #639: redact block scalars by line number
This commit is contained in:
+79
-22
@@ -154,18 +154,10 @@ done
|
||||
# story: a YAML block scalar (`|`, `|-`, `>`, `>-`, ...) puts the VALUE on the lines that follow
|
||||
# the key, each indented deeper than it. The key-name match above only ever sees the key line
|
||||
# itself, so those continuation lines used to flow straight through unredacted while the key line
|
||||
# right above them printed a reassuring "<redacted>" — an incomplete redactor that looks complete
|
||||
# is worse than one that visibly does nothing, because it stops a reviewer from looking further.
|
||||
# The fix is structural, not another name to match: once a key line is masked, every following
|
||||
# line indented STRICTLY DEEPER than that key is masked too, by indentation alone, until the
|
||||
# indentation returns to the key's own level or shallower. This needs no knowledge of the key's
|
||||
# name, so it covers a block scalar under any masked key — but ONLY while that key's own line is
|
||||
# itself inside the hunk being printed. `diff -u` prints just three lines of context, so a block
|
||||
# scalar's body often reaches this function with its key line left out; there is then nothing to
|
||||
# anchor to, `masked` is never set, and the body prints in full. A blank line inside a block
|
||||
# scalar loses the anchor the same way, because a blank diff line measures as indent 0. Both are
|
||||
# measured and filed as fleetd #639 — do not read this paragraph as a guarantee that a masked
|
||||
# key's value can never be printed.
|
||||
# right above them printed a reassuring "<redacted>". The redactor maps masked continuation lines
|
||||
# from each complete file before it reads the diff. It then masks a printed line when that file
|
||||
# line is inside a masked key's value. This covers block-scalar bodies even when the key line is
|
||||
# outside the printed hunk, and it keeps blank lines inside the value masked.
|
||||
#
|
||||
# `redact` is always fed `diff -u` output, and every line of a unified diff starts with exactly
|
||||
# one of ' ', '+', '-' (the three body markers; '@'/'-'/'+' for the three header-line kinds too).
|
||||
@@ -174,37 +166,102 @@ done
|
||||
# column shallower than it really is, and either wrongly escapes a continuation mask or wrongly
|
||||
# ends one early. Tabs are out of scope: YAML forbids them for indentation, and this is a bounded
|
||||
# fix, not a YAML parser.
|
||||
map_masked_lines() {
|
||||
local file="$1" side="$2" line content indent lead key line_number=0
|
||||
local masked=0 masked_indent=0
|
||||
|
||||
case "$side" in
|
||||
old) OLD_MASKED_LINES=() ;;
|
||||
new) NEW_MASKED_LINES=() ;;
|
||||
*) die "internal error: unknown redaction map side $side" ;;
|
||||
esac
|
||||
|
||||
while IFS= read -r line || [ -n "$line" ]; do
|
||||
line_number=$((line_number + 1))
|
||||
content="$line"
|
||||
indent=0
|
||||
while [ "${content:$indent:1}" = " " ]; do indent=$((indent + 1)); done
|
||||
|
||||
if [ "$masked" = 1 ]; then
|
||||
if [ -z "${content// /}" ] || [ "$indent" -gt "$masked_indent" ]; then
|
||||
case "$side" in
|
||||
old) OLD_MASKED_LINES[$line_number]=1 ;;
|
||||
new) NEW_MASKED_LINES[$line_number]=1 ;;
|
||||
esac
|
||||
continue
|
||||
fi
|
||||
masked=0
|
||||
fi
|
||||
|
||||
if [[ "$content" =~ ^([[:space:]]*)([A-Za-z0-9_.-]+:) ]]; then
|
||||
lead="${BASH_REMATCH[1]}"
|
||||
key="${BASH_REMATCH[2]}"
|
||||
if [[ "$key" =~ (TOKEN|SECRET|PASSWORD|PASSWD|PASSPHRASE|CREDENTIAL|URI|_KEY) ]]; then
|
||||
masked=1
|
||||
masked_indent="$indent"
|
||||
fi
|
||||
fi
|
||||
done < "$file"
|
||||
}
|
||||
|
||||
redact() {
|
||||
local line prefix content indent lead key
|
||||
local masked=0 masked_indent=0 saved_nocasematch=0
|
||||
local old_file="$1" new_file="$2"
|
||||
local line prefix content indent lead key old_line=0 new_line=0 in_hunk=0
|
||||
local old_masked new_masked saved_nocasematch=0
|
||||
shopt -q nocasematch && saved_nocasematch=1
|
||||
shopt -s nocasematch
|
||||
sed -E 's#://[^@]*@#://<redacted>@#g' | while IFS= read -r line || [ -n "$line" ]; do
|
||||
|
||||
map_masked_lines "$old_file" old
|
||||
map_masked_lines "$new_file" new
|
||||
|
||||
while IFS= read -r line || [ -n "$line" ]; do
|
||||
if [[ "$line" =~ ^@@\ -([0-9]+)(,([0-9]+))?\ \+([0-9]+)(,([0-9]+))?\ @@ ]]; then
|
||||
old_line="${BASH_REMATCH[1]}"
|
||||
new_line="${BASH_REMATCH[4]}"
|
||||
in_hunk=1
|
||||
printf '%s\n' "$line"
|
||||
continue
|
||||
fi
|
||||
|
||||
case "$line" in
|
||||
[\ +-]*) prefix="${line:0:1}"; content="${line:1}" ;;
|
||||
*) prefix=""; content="$line" ;;
|
||||
esac
|
||||
|
||||
old_masked=0
|
||||
new_masked=0
|
||||
if [ "$in_hunk" = 1 ]; then
|
||||
case "$prefix" in
|
||||
' ')
|
||||
[ "${OLD_MASKED_LINES[$old_line]:-}" = 1 ] && old_masked=1
|
||||
[ "${NEW_MASKED_LINES[$new_line]:-}" = 1 ] && new_masked=1
|
||||
old_line=$((old_line + 1)); new_line=$((new_line + 1)) ;;
|
||||
-)
|
||||
[ "${OLD_MASKED_LINES[$old_line]:-}" = 1 ] && old_masked=1
|
||||
old_line=$((old_line + 1)) ;;
|
||||
+)
|
||||
[ "${NEW_MASKED_LINES[$new_line]:-}" = 1 ] && new_masked=1
|
||||
new_line=$((new_line + 1)) ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
indent=0
|
||||
while [ "${content:$indent:1}" = " " ]; do indent=$((indent + 1)); done
|
||||
|
||||
if [ "$masked" = 1 ] && [ "$indent" -gt "$masked_indent" ]; then
|
||||
if [ "$old_masked" = 1 ] || [ "$new_masked" = 1 ]; then
|
||||
printf '%s%*s<redacted>\n' "$prefix" "$indent" ""
|
||||
continue
|
||||
fi
|
||||
masked=0
|
||||
|
||||
if [[ "$content" =~ ^([[:space:]]*)([A-Za-z0-9_.-]+:) ]]; then
|
||||
lead="${BASH_REMATCH[1]}"
|
||||
key="${BASH_REMATCH[2]}"
|
||||
if [[ "$key" =~ (TOKEN|SECRET|PASSWORD|PASSWD|PASSPHRASE|CREDENTIAL|URI|_KEY) ]]; then
|
||||
printf '%s%s%s <redacted>\n' "$prefix" "$lead" "$key"
|
||||
masked=1
|
||||
masked_indent="$indent"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
printf '%s\n' "$line"
|
||||
printf '%s\n' "$line" | sed -E 's#://[^@]*@#://<redacted>@#g'
|
||||
done
|
||||
[ "$saved_nocasematch" = 1 ] || shopt -u nocasematch
|
||||
}
|
||||
@@ -627,7 +684,7 @@ run_edit() {
|
||||
apply_mode "$cand" "$orig_mode"
|
||||
|
||||
say "change (redacted)"
|
||||
diff -u "$backup" "$cand" | redact || true
|
||||
diff -u "$backup" "$cand" | redact "$backup" "$cand" || true
|
||||
|
||||
say "install"
|
||||
install_candidate "$cand" "$CONFIG" \
|
||||
@@ -656,7 +713,7 @@ dry_run_diff() {
|
||||
die "candidate does not parse as valid YAML — this was a --dry-run, nothing would have been installed either"
|
||||
fi
|
||||
say "dry run — diff (redacted), nothing installed"
|
||||
diff -u "$CONFIG" "$cand" | redact || true
|
||||
diff -u "$CONFIG" "$cand" | redact "$CONFIG" "$cand" || true
|
||||
rm -f "$cand"; CAND=""
|
||||
return 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user