Context
v1.7.1 fixed a jq-injection / token-exfiltration vector in bb logs: step_index was interpolated raw into a jq program (.values[<step_index>].uuid), so a crafted index like 0].uuid,$ENV.BB_TOKEN,.values[0 could make jq emit $ENV.BB_TOKEN. The fix added _require_step_index (a numeric guard) that runs before the interpolation.
v1.7.2 added tests/bash/test_security_guards.sh, run under the digest-pinned bash:3.2 image in the bash32-floor CI job. It asserts each _require_* guard rejects its injection vector by matching the guard's specific error message. That harness is offline by design: every malicious case is rejected before any network call, so it needs no live workspace and no jq / curl / git.
The gap
Because the harness is offline, it proves the guard is present (delete the _require_step_index call and the harness fails), but it cannot prove the end-to-end property that even if a value reached the jq interpolation, the token could not leak. Input only reaches jq if the guard is already broken, and the guard-deletion case is the realistic regression the shipped harness already catches. This is the more exotic remaining scenario: a guard replaced by something that still lets input through to jq.
Proposed work
Add a mock-server variant that drives the injection all the way to the jq step and asserts the token sentinel never appears in output:
- A small local HTTP stub with method/path routing (a ~30-50 line
python3 http.server handler, or a bats + nc fixture) that returns a canned pipelines list + steps JSON so cmd_logs proceeds past the fetches to the jq interpolation. python3 -m http.server alone will not do path/method routing.
- Point
BB_API_BASE at http://127.0.0.1:<port>.
- Assert: with the guard present, the malicious
step_index is rejected; and as a defense-in-depth check, the BB_TOKEN sentinel is absent from output on the injection path.
Constraints / notes
- This variant cannot run in the minimal
bash:3.2 CI image the current harness uses: reaching the jq step requires jq + curl to fetch and parse the mock response. It needs a fuller environment, so it would be a separate CI job on the ubuntu runner (which has python3, jq, curl), not part of bash32-floor.
- Mock servers carry port-binding and process-cleanup considerations; the fixture must bind an ephemeral port and clean up reliably.
Priority
Low. The security property is already protected by three layers: the guard itself (primary), the CLAUDE.md "destructive/injection-prone values are validated at the boundary" convention (prevents new gaps), and the shipped bash32-floor harness (catches the realistic guard-deletion regression). This variant closes a theoretical corner case at a meaningfully higher infrastructure cost, so it is best done opportunistically if/when a broader bash integration-test harness is stood up, rather than as a standalone piece.
Context
v1.7.1 fixed a jq-injection / token-exfiltration vector in
bb logs:step_indexwas interpolated raw into a jq program (.values[<step_index>].uuid), so a crafted index like0].uuid,$ENV.BB_TOKEN,.values[0could make jq emit$ENV.BB_TOKEN. The fix added_require_step_index(a numeric guard) that runs before the interpolation.v1.7.2 added
tests/bash/test_security_guards.sh, run under the digest-pinnedbash:3.2image in thebash32-floorCI job. It asserts each_require_*guard rejects its injection vector by matching the guard's specific error message. That harness is offline by design: every malicious case is rejected before any network call, so it needs no live workspace and nojq/curl/git.The gap
Because the harness is offline, it proves the guard is present (delete the
_require_step_indexcall and the harness fails), but it cannot prove the end-to-end property that even if a value reached the jq interpolation, the token could not leak. Input only reaches jq if the guard is already broken, and the guard-deletion case is the realistic regression the shipped harness already catches. This is the more exotic remaining scenario: a guard replaced by something that still lets input through to jq.Proposed work
Add a mock-server variant that drives the injection all the way to the jq step and asserts the token sentinel never appears in output:
python3http.serverhandler, or abats+ncfixture) that returns a canned pipelines list + steps JSON socmd_logsproceeds past the fetches to the jq interpolation.python3 -m http.serveralone will not do path/method routing.BB_API_BASEathttp://127.0.0.1:<port>.step_indexis rejected; and as a defense-in-depth check, theBB_TOKENsentinel is absent from output on the injection path.Constraints / notes
bash:3.2CI image the current harness uses: reaching the jq step requiresjq+curlto fetch and parse the mock response. It needs a fuller environment, so it would be a separate CI job on the ubuntu runner (which haspython3,jq,curl), not part ofbash32-floor.Priority
Low. The security property is already protected by three layers: the guard itself (primary), the
CLAUDE.md"destructive/injection-prone values are validated at the boundary" convention (prevents new gaps), and the shippedbash32-floorharness (catches the realistic guard-deletion regression). This variant closes a theoretical corner case at a meaningfully higher infrastructure cost, so it is best done opportunistically if/when a broader bash integration-test harness is stood up, rather than as a standalone piece.