Repository navigation
Description
Controlled audit follow-up to #424: parameterized workflows still prepend human parameter rows to machine-readable stdout.
Observed with clean source 971ebedf5c747a580195383e4f17f07e09f31b9c and a locally rebuilt normal CLI (SHA-256 5c1d629e1ef465f2747ea48926a3ca8f1dca78ac44459e69ce8eb61a448a5427). The profile, files, and loopback provider were synthetic; stdin was closed, and no --quiet workaround was used.
A controlled invocation of:
biorouter run --workflow biorouter-self-test.yaml \
--params test_phases=folder_access --params test_depth=quick \
--params workspace_dir=<synthetic-workspace> \
--params parallel_tests=false --params cleanup_after=false \
--output-format json </dev/nullexited 0 but stdout began with parameter rows such as cleanup_after: false and test_phases: folder_access, followed by the JSON object. Parsing the entire stdout failed with JSONDecodeError. The workflow-loading heading and parameter-summary heading were already on stderr. The actual original workflow made zero tool calls with this deterministic fixture, so its security phase is recorded as INCONCLUSIVE rather than passed. Separate direct and no-parameter derived-workflow controls produced actual structured authorization_required refusals and parseable JSON/NDJSON.
Cause: print_workflow_info in crates/biorouter-cli/src/workflows/print_workflow.rs calls the shared print_parameters_with_values, which prints rows to stdout. That helper is also used by human explanation output, where stdout is intentional.
Recommendations:
- Send runtime workflow parameter rows to stderr with their existing headings, preserving the same readable information for terminal users.
- Keep the intentional human
--explainoutput intact. - Extend the real-binary, closed-stdin regression to a parameterized workflow in all three formats: whole JSON object, every NDJSON line, and ordinary text with its session banner.
- Preserve the actual structured authorization refusal and synthetic canaries; this output correction must not change folder policy or grant access.
This is an automation/output correctness defect discovered during defensive security acceptance, not an observed authorization bypass. The original raw failure and later corrective results will remain separately attributable in the audit supplement.
Activity
fix(cli): keep workflow parameter summaries on stderr
Broccolito commentedon Oct 24, 2026
Issue #456: Keep parameterized workflow machine stdout parseable
Issue · Host PR #454 · merged main source
Historical desktop/browser/CLI observations used f227 artifacts, accepted via the reviewed production-equivalence comparison; exact-main CI/build/smoke is attributed separately below.
Problem. Runtime parameter diagnostics contaminated JSON stdout, breaking automation despite exit 0.
Fix and UX. Workflow loading diagnostics now go to stderr. JSON remains one machine-readable value and stream-JSON remains parseable per line; useful terminal diagnostics and intentional human
--explainoutput remain. Folder permissions do not change, and scripts need no--quietworkaround.Validation. The actual-binary regression passed one Rust test with direct/parameterized input × JSON/NDJSON/text: real provider-delta/output/canary assertions, closed stdin and parameter stderr. Final CLI05 independently passed six actual headless cases plus five normal PTY calls: 11 matched pairs total = three approved reads/eight refusals, nine executions exit 0, no orphan/unanswered pair and unchanged canaries/source. Machine output parses and parameter diagnostics are correctly attributed to stderr. Interactive once/Deny/session/list/revoke/reprompt remain usable. See current Rust CI and the local Supplementary Data S1 package for separate software/runtime attribution.
Limits. Six format subcases are not six independent security attacks. The older 971 original 0-call JSON failure is preserved. A separate selected selftest now parsed but produced 0 tool calls; its security phase remains inconclusive, so no whole-workflow security PASS is inferred. This is an output/automation correction, not a new authorization capability.
Recommendation. Keep runtime diagnostics on stderr and add machine-output regression coverage when workflow parameters change, preserving intentional human explanation output.
Merged-main verification. PR454 is merged at
0580110; main Rust CI, frontend CI and related checks finished 20 successes/one skipped (17 push successes plus three landing/deployment checks; these are not security-test counts). Normal main build manifest SHA256:38cc2979c7da104cb623975bac7f6922a085e834a1be5c18e961d7f26ea811fc; shipping receipt:e6e1d8957d8d0b22a9aae814011a1e028c5d279848ffabbf4c6d0c4edf3c0112. Actual main01 smoke separately recorded four pairs—three reads/one typed Deny—and three unchanged canaries; it does not rerun/recount the historical 81-pair cohort.Submission record. Manuscript v1.1 local Supplementary Data S1: Supplementary Data S1 - BioRouter Security Audit - 2026-10-24.zip; manifest SHA256 4f0c0c110ca0b07a29d5281870227f7b36c744a0f37ccb499b8ef1283dea116e. This is a user/submission package, not a claim of public upload. Public PR/source/CI links above provide the public implementation proof.