Fix: bash node $nodeId.output silently corrupts large multi-KB inputs#1719
Fix: bash node $nodeId.output silently corrupts large multi-KB inputs#1719Wirasm wants to merge 3 commits into
Conversation
…#1717) When a bash node references $nodeId.output from an upstream LLM node whose output is large (~40KB+), the value was embedded inline as a single-quoted shell literal in the script passed to `bash -c`. For large or adversarial inputs this inline substitution can mishandle the value, causing the bash subprocess to receive corrupted or truncated content. This extends the shellSafe env-var pattern (PR #1651) to cover whole-output $nodeId.output refs in bash nodes and until_bash loop conditions. Field-access refs ($nodeId.output.field) remain inline because they are small scalars. Changes: - Add extractNodeOutputEnvVars helper that rewrites $nodeId.output to "\${_ARCHON_NODE_<ID>_OUTPUT}" expansions and returns the env-var map - Use the helper in executeBashNode so the value travels via subprocessEnv - Use the helper in the until_bash loop condition check - Add unit tests covering 50KB+ outputs, special shell characters, hyphenated node IDs, mixed whole/field refs, repeated refs, and unknown node refs Fixes #1717
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Comprehensive PR Review — 5 AgentsPR: #1719 (draft) — Fix: bash node $nodeId.output silently corrupts large multi-KB inputs Verdict: REQUEST_CHANGESThe core fix is technically sound. `extractNodeOutputEnvVars()` correctly partitions whole-output refs from field-access refs, injects via `ARCHON_NODE*` env vars, avoids the OS argument-size boundary, and is well-tested at the unit level. The issues below are documentation accuracy (one HIGH), comment style (CLAUDE.md), and integration test coverage.
🟠 HIGH —
|
| Issue | Location | Note |
|---|---|---|
| Multi-line JSDoc (7 lines; CLAUDE.md says 1 max) | dag-executor.ts:236-244 |
Pre-existing debt on substituteNodeOutputRefs makes this a grey area; trimming recommended |
| Regex comment explains WHAT not WHY | dag-executor.ts:251 |
Remove; regex is readable without it |
escapedForBash — field refs only remain trailing note is fragile |
dag-executor.ts:2187 |
Trim to true // escapedForBash or remove |
until_bash env-var path has zero integration-level coverage |
dag-executor.ts:2181-2203 |
Add minimal spy test (criticality 5/10) |
Pre-existing: until_bash EACCES/ETIMEDOUT errors are silent to user |
dag-executor.ts:2206-2222 |
Follow-up issue; not introduced here — executeBashNode handles these correctly |
✅ What's Good
- Correct regex:
(?!\.[a-zA-Z_])precisely mirrors the field-name start pattern insubstituteNodeOutputRefs— clean partitioning, no overlap. - Well-namespaced env vars:
_ARCHON_NODE_*_OUTPUT— user collision negligibly unlikely. - Correct call-site ordering:
extractNodeOutputEnvVars→substituteNodeOutputRefs— field refs see unmodified text. - Thorough unit tests: 8 cases covering large inputs, field exclusion, mixed refs, hyphen normalization, unknown nodes, duplicate refs, and negative-lookahead edge case.
- Both call sites fixed:
executeBashNodeanduntil_bashboth receive the fix without creating new asymmetry. - Correct env spread order:
{ ...nodeOutputEnvVars, ...(envVars ?? {}) }lets userenvVarsoverride_ARCHON_*vars correctly. - No silent failures introduced: Pure function; unknown node IDs intentionally deferred to
substituteNodeOutputRefswarn path. .catchchains are complete: Every fire-and-forgetcreateWorkflowEventcall logserr,workflowRunId, andeventType.- Env-var approach vs PR C1718 temp-file approach: No threshold edge case, no temp file lifecycle — env-var injection is the cleaner fix.
Suggested Follow-up Issues
| Title | Priority |
|---|---|
until_bash system errors (EACCES, ETIMEDOUT) are silent to user |
P2 |
Add integration test for until_bash env-var injection path |
P3 |
Reviewed by Archon comprehensive-pr-review workflow · 5 agents · Artifacts: ~/.archon/workspaces/coleam00/Archon/artifacts/runs/ccfd331ff8a6b094408c7e34771a89ef/review/
- Remove issue number refs (#1717, #1651) from comments per CLAUDE.md - Trim multi-line JSDoc on extractNodeOutputEnvVars to 2-line comment - Remove regex comment that explained WHAT not WHY - Trim escapedForBash trailing comment - Add collision guard warn log when two node IDs map to the same env var - Spread config.envVars into until_bash subprocess for parity with executeBashNode - Update variables.md: replace stale "auto shell-quoted" description with env-var mechanism - Update script-nodes.md: fix stale contrast with bash node auto-quoting - Add CHANGELOG entry for #1717 fix - Add integration tests: executeBashNode and until_bash env var wiring via spy - Add empty-string output unit test for extractNodeOutputEnvVars
⚡ Self-Fix Report (Aggressive)Status: COMPLETE Fixes Applied (11 total)
View all fixes
Tests Added
Skipped(none — all findings addressed) Suggested Follow-up Issues
Validation✅ Type check | ✅ Lint | ✅ Tests (243+ passed across all packages) Self-fix by Archon · aggressive mode · fixes pushed to |
|
Closing in favor of #1718 (merged). Quick comparison of the two approaches for #1717: #1718 (temp-file) — above a 32KB threshold, writes the value to #1719 (env-var, this PR) — Trade-offs:
For the actual #1717 symptom (42KB synth output), both fix it. Since #1718 already merged and covers a strictly broader set of cases, this PR is no longer needed. Thanks @wirasm-archon for the env-var experiment — the test scaffolding here was a useful sanity check while reviewing #1718. |
Summary
$nodeId.outputfrom an upstream LLM node inline-embed the value viashellQuote()into the bash script string passed tobash -c. For outputs ≥42KB this silently corrupts the value through OS argument-size boundaries and bash parser edge cases.maintainer-standup'spersistnode) fails on every run with no user-visible error — the upstream output is stored correctly but the bash node receives a truncated or corrupted copy.extractNodeOutputEnvVars()helper that replaces$nodeId.output(whole-output) refs with"${_ARCHON_NODE_<ID>_OUTPUT}"bash expansions and injects the actual values viasubprocessEnv. Applied at both call sites:executeBashNodeanduntil_bashloop condition. Field-access refs ($nodeId.output.field) remain inline as before — they are small scalars.substituteNodeOutputRefsitself is unchanged. Script nodes, prompt/LLM nodes, and field-access ref handling are all out of scope.UX Journey
Before
After
Architecture Diagram
Before
After
Same fix applied at
until_bashloop condition call site.Connection inventory:
executeBashNodeextractNodeOutputEnvVarssubstituteNodeOutputRefsexecuteBashNodesubstituteNodeOutputRefsexecuteBashNodeexecFileAsyncsubprocessEnvnow includes node output env varsuntil_bash(loop)extractNodeOutputEnvVarsuntil_bash(loop)execFileAsyncenvnow includes node output env varsLabel Snapshot
risk: lowsize: Sworkflowsworkflows:dag-executorChange Metadata
bugworkflowsLinked Issue
Validation Evidence (required)
All checks passed:
bun run validatepass documented invalidation.mdartifactSecurity Impact (required)
Compatibility / Migration
$nodeId.outputin bash scripts continue to work; the env var injection is transparent to the script authorHuman Verification (required)
extractNodeOutputEnvVarshelper with 8 cases including 50KB+ outputs with special shell characters ($, backticks, double/single quotes, globs), hyphenated node IDs, field-access refs left untouched, multiple refs to same node, unknown node refs left unchanged$nodeId.output.field; hyphens in node IDs are normalized to underscores in env var names; unknown node IDs are left unchanged sosubstituteNodeOutputRefscan emit its existing warningmaintainer-standupwith a live ≥42KB synthesis output (no running Archon instance available in this session)Side Effects / Blast Radius (required)
until_bashloop conditions in any workflow that references$nodeId.output$nodeId.output.field) are explicitly excluded by the negative lookahead and still go through the existing inline path_ARCHON_NODE_<ID>_OUTPUTis private/namespaced; unlikely to collide with user env varsRollback Plan (required)
dag-executor.ts(remove theextractNodeOutputEnvVarscall and spread, revertsubstituteNodeOutputRefsto receivesubstitutedScriptdirectly)persistnode inmaintainer-standupreceiving corrupted inputRisks and Mitigations
my-nodeandmy_node)ARG_MAX(256KB) could fail even with env var injection