fleetd #500: honest refusal when the policy parse fails, not just when it's empty #516
Reference in New Issue
Block a user
Delete Branch "worker/500-9e52c9-3"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes fleetd #500.
scripts/probe-member-credentials.shusedmapfile -t _FIELDS < <(producer)to parse the fetched policy. That construct hides a producer failure three separate ways:mapfileis bash 4+ and missing on macOS's/bin/bash3.2; a process substitution's exit status is never propagated back tomapfile; and every downstream read of_FIELDSuses a:-default or a slice, neither of which firesset -uon a short or unset array. All three converge on the same0 known namesrefusal at the bottom of the script, which blames the POLICY for a failure that is actually the INTERPRETER or the PARSER.Three distinct, independently-measured guards, each closing one cause with its own message:
BASH_VERSINFOgate near the top refuses outright on bash < 4, naming the running version (exit 3).mapfile < <(...), so a non-zerojq/python3exit is caught at the call, while the fact still exists, beforemapfileever sees it (exit 4).The pre-existing "0 known names" guard is now honest: by the time it fires, the three causes above are already ruled out, so it really does mean the policy legitimately reports 0 known names. Its message was tightened to drop the "or the response could not be parsed" hedge, which is no longer a real possibility at that point.
set -o pipefailis kept, with a comment correcting an earlier guess on the ticket: it is not what catches today's single-stagejq/python3pipe (jq is the last element, so the pipeline's status is already jq's status) — it is insurance for if a post-processing stage is ever appended after the parser.Noted per the ticket's own open caveat:
mapfile ... <<<materialises the captured string in memory instead of streaming it the way< <(...)did. Irrelevant for this policy response's size; flagged rather than assumed away.Testing
bash -npasses under both/bin/bash(3.2.57) andenv bash(5.3.9, homebrew) on this host.Measured all three causes, plus controls, on both interpreters using throwaway fixture
curl/jq/python3stand-ins (no real policy URL or network contacted):Note on the pre-emption rows: on bash 3.2 the interpreter gate is checked first and unconditionally, before the script ever fetches or parses anything — so a cause-(b)/(c) fixture on that interpreter still produces the cause-(a) message. That is the intended behaviour of putting the gate "at the one place a guard cannot be defeated by its call site," not a gap in coverage; I ran it explicitly to confirm the gate really does supersede the later guards rather than assuming it.
Scope: only
scripts/probe-member-credentials.shchanged. No real policy URL or external service was contacted; all fixtures were throwaway local scripts. No environment variable value was printed by any test.probe-member-credentials.sh used mapfile < <(producer) to parse the fetched policy. That hides a producer failure three ways: mapfile is bash 4+ and missing on macOS's /bin/bash 3.2, a process substitution's exit status is never propagated to mapfile, and the downstream reads (":-" defaults and a slice) never fire set -u on a short or unset array. All three converge on the same "0 known names" refusal, which blames the policy for a failure that is actually the interpreter or the parser. Three distinct guards, each closing one cause with its own message: - a BASH_VERSINFO gate at the top refuses outright on bash < 4 (exit 3) - the parser's output is captured via command substitution instead of mapfile < <(...), so a non-zero jq/python3 exit is caught at the call while the fact still exists (exit 4) - an arity check before the field slice refuses a parse that exits 0 but returns fewer than 5 fields (exit 5) The existing "0 known names" guard is now honest: by the time it fires, the three causes above are already ruled out, so it really does mean the policy has 0 known names.