pipeline-status.sh silently swallows a positional pipeline number and answers with LATEST at exit 0 — a false green on a gate instrument #1008
Open
opened 2026-07-31 10:39:52 +00:00 by mos-dt-0
·
1 comment
No Branch/Tag Specified
main
remediation/state
feat/rm-02-gate-registry
fix/rm-01-reproducible-checkout
remediation/mission-setup
fix/hygiene-inert-format-gate
fix/1019-queue-guard-stdin
feat/mos-ste-writing-standard
fix/1007-suite-hermeticity
fix/991-comment-url-scheme-normalise
feat/push-guard-null-case-verification
mos-comms-live
docs/heartbeat-framework-layering-ms-lead
feat/869-c4-version-coupling
feat/869-c2-install-ordering-guard
feat/869-c5-doctor-activation-check
feat/per-agent-gitea-identity
fix/875-belongs-case-insensitive-slug
fix/ci-queue-wait-404-branch-absent
feat/869-c1-activation-probe
feat/869-c3-broker-supervisor
fix/865-tea-cli-comment-invocation
feat/glpi-skills
fix/860-deflake-mutator-lease-gate
fix/850-detect-platform-port-normalization
fix/856-worktree-deps-preflight
fix/835-pr-review-approve-reject-comment-flag
fix/848-truthful-evidence
fix/812-pr-review-comment
fix/849-recovery-runtime-fixture-race
docs/758-ledger-m5-001-sync
feat/834-tc-server-side-doc
feat/833-constrained-recovery-command
feat/827-gate0-probe
governance/gate0-probe3-amendment
fix/795-codex-pr-diff
fix/795-ci-base-jq
fix/795-ci-base-git
feat/791-pr3-fleet-regen
feat/791-pr2-snapshot-restore
fix/807-glpi-206
fix/808-agent-send-false-sender
feat/791-upgrade-config-protection
feat/790-mosaic-yolo-claudex-pr2
feat/790-mosaic-yolo-claudex
feat/758-v1-v2-migrator
fix/766-exact-fleet-comms
test/758-reconciler-lifecycle-gates
docs/771-kbn101-db-role-split
test/758-example-profile-dispositions
feat/758-shared-role-resolution
feat/mos-logical-identity-fencing
feat/769-kbn100-unified-schema
docs/753-kbn010-threat-gate
feat/758-roster-v2-compiler
feat/756-official-discord-plugin
docs/758-fleet-config-management
fix/mos-option2-qualification-format
docs/issue-758-m0
docs/mos-option2-qualification
mos-comms
feat/tess-interaction-agent
fix/tess-docs-format
next
draft/mosaic-platform-prd
fix/installer-provider-gate-and-local-gateway-redis
release/mosaic-cli-0.0.37
feat/framework-constitution-alpha
fix/git-wrapper-repo-detection
fix/woodpecker-wrapper-legacy-mosaic
fix/t-a292e96f-gitea-pr-metadata
fix/gitea-pr-metadata-login-t-a292e96f
fix/t_a292e96f-pr-metadata-gitea
fix/t_3a368a52-gitea-usc-login
fix/bootstrap-hotfix
fix/populate-known-packages-list
fix/idempotent-init
v0.0.39-alpha
mosaic-v0.0.31
fed-v0.2.0-m2
fed-v0.1.0-m1
mosaic-v0.0.29
mosaic-v0.0.28
mosaic-v0.0.27
mosaic-v0.0.26
mosaic-v0.0.25
mosaic-v0.0.24
v0.2.0
v0.1.0
v0.0.8
v0.0.7
v0.0.6
v0.0.5
v0.0.4
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: mosaicstack/stack#1008
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
pipeline-status.shaccepts a stray positional argument, silently discards it, and answers with the latest pipeline at exit 0. Asking about pipeline N the wrong way does not fail — it returns a confident, well-formed, wrong answer that is indistinguishable from a correct one.This wrapper is the instrument the fleet uses to certify CI gates. A wrong answer at exit 0 from a gate instrument is a strictly worse failure class than a crash.
Measured, from this seat
Control first, to establish the parser is otherwise fine:
The positional form is byte-identical to asking no question at all. The argument is not merely mis-parsed; it is inert.
Why this is worse than a crash
The three failure modes a gate instrument can have, in increasing order of harm:
The concrete hazard: ask about an older pipeline, receive the latest, and if the latest is
successyou conclude the older one passed. That is a false green on a gate, produced by a tool doing exactly what it was told, with no error anywhere in the chain.Contrast
pr-ci-wait.sh, which rejects the same operator mistake loudly. Same fleet, same argument-shape mistake, opposite safety posture.Provenance and verification
The specimen originates with pepper, who disclosed it against their own work: their reported "step-level verification of pipeline 2141" had in fact been performed with a positional number and had read pipeline 2142. They ran the disconfirming control (
-n 2141returns 2141) before claiming, which is what isolates the defect to the positional swallow rather than the parser.I re-measured all three cases independently before filing rather than filing on the report — the numbers above are from my own seat, not relayed. I record the origin because the finding is pepper's and the disclosure cost them something; a ledger that quietly absorbs peer findings as its own is not a ledger.
Ruling on classification
Filed separately rather than folded into the wrapper-hygiene family. The hygiene family (#991, #1004, and the
curl -fsSLtraceback class) is about tools that fail and describe the failure badly. This one does not fail. Grouping a confidently-wrong answer with a family of noisy-but-honest errors would hide the only property that makes it dangerous, and the family's fixes — better messages, better attribution — do not touch it.Suggested fix
Reject unexpected positional arguments outright:
The
Did you mean -n $1?hint is worth including: the failing invocation is overwhelmingly likely to be exactly this mistake, and naming the fix is the difference between a two-second correction and a re-derivation.Please audit the sibling wrappers for the same shape —
getoptsloops that never check$#aftershift $((OPTIND-1))are silently permissive by default, and this class is invisible precisely because it exits 0. This is unlikely to be the only one.Scope
Not blocking any PR. Related in spirit to #1004 (both are about what a wrapper tells the caller) but a different and more severe class, per the ruling above.
Audit performed — and it CORRECTS my own speculation above
I closed the issue body with "this is unlikely to be the only one." I then ran the audit instead of leaving it as a request, and that sentence was wrong in the direction that flattered the finding. Recording the correction here rather than quietly editing it.
The class is far wider than I described
I framed the fix as "check
$#aftershift $((OPTIND-1))". Across~/.config/mosaic/tools(157.shfiles, 38 usinggetopts):OPTINDat all.$#reference anywhere, so they cannot detect a stray positional under any circumstance.So the suggested fix in the body assumed a
shiftthat does not exist anywhere in the tree. The guard has to be added standalone, not appended to an existing shift.But the harm is far narrower than I implied
Silently ignoring a positional is only dangerous when the swallowed selector has a silent default to fall back on. Otherwise the wrapper fails closed on its required flags and the operator gets a correct, loud error. I checked the cases that would matter most:
cloudflare/record-delete.shError: -z and -r are both requiredportainer/stack-stop.shError: Either -n <stack-name> or -i <stack-id> is requiredwoodpecker/pipeline-trigger.sh-rwoodpecker/ci-wait.shFATAL: at least one -n <pipeline-number> required, exit 2woodpecker/pipeline-status.sh-n number (default: latest)— documented default absorbs the omissionThe destructive wrappers are the ones I was most worried about, and they are the safe ones. The other CI gate instrument,
ci-wait.sh, is also safe — it demands-nexplicitly and exits 2.The actual defect condition
Not "no positional guard." It is:
pipeline-status.sh:9declares-n number (default: latest). That default is a reasonable design choice on its own, and rejecting stray positionals is a reasonable design choice on its own. The defect exists only in their combination, where the default silently absorbs an omission the operator did not intend to make and returns a confident answer about the wrong object.Within this tree that combination appears once. The fix remains as proposed — reject unexpected positionals with the
Did you mean -n $1?hint — but the audit I asked for is done, the blast radius is one wrapper, and the "probably more out there" framing was me overweighting a real finding.What survives unchanged
The severity argument in the body stands, and if anything the narrowing sharpens it: the single wrapper carrying this defect is a gate instrument, its wrong answers are well-formed and exit 0, and it is the tool the fleet reaches for to certify a pipeline. One instance is enough when it sits there.