MCPcopy Create free account
hub / github.com/AnukarOP/claude-code-leaked / collectCommands

Function collectCommands

source code/utils/bash/ast.ts:484–957  ·  view source on GitHub ↗

* Recursively collect leaf `command` nodes from a structural wrapper node. * Returns an error result on any disallowed node type, or null on success.

(
  node: Node,
  commands: SimpleCommand[],
  varScope: Map<string, string>,
)

Source from the content-addressed store, hash-verified

482 * Returns an error result on any disallowed node type, or null on success.
483 */
484function collectCommands(
485 node: Node,
486 commands: SimpleCommand[],
487 varScope: Map<string, string>,
488): ParseForSecurityResult | null {
489 if (node.type === 'command') {
490 // Pass `commands` as the innerCommands accumulator — any $() extracted
491 // during walkCommand gets appended alongside the outer command.
492 const result = walkCommand(node, [], commands, varScope)
493 if (result.kind !== 'simple') return result
494 commands.push(...result.commands)
495 return null
496 }
497
498 if (node.type === 'redirected_statement') {
499 return walkRedirectedStatement(node, commands, varScope)
500 }
501
502 if (node.type === 'comment') {
503 return null
504 }
505
506 if (STRUCTURAL_TYPES.has(node.type)) {
507 // SECURITY: `||`, `|`, `|&`, `&` must NOT carry varScope linearly. In bash:
508 // `||` RHS runs conditionally → vars set there MAY not be set
509 // `|`/`|&` stages run in subshells → vars set there are NEVER visible after
510 // `&` LHS runs in a background subshell → same as above
511 // Flag-omission attack: `true || FLAG=--dry-run && cmd $FLAG` — bash skips
512 // the `||` RHS (FLAG unset → $FLAG empty), runs `cmd` WITHOUT --dry-run.
513 // With linear scope, our argv has ['cmd','--dry-run'] → looks SAFE → bypass.
514 //
515 // Fix: snapshot incoming scope at entry. After these separators, reset to
516 // the snapshot — vars set in clauses between separators don't leak. `scope`
517 // for clauses BETWEEN `&&`/`;` chains shares state (common `VAR=x && cmd
518 // $VAR`). `scope` crosses `||`/`|`/`&` as the pre-structure snapshot only.
519 //
520 // `&&` and `;` DO carry scope: `VAR=x && cmd $VAR` is sequential, VAR is set.
521 //
522 // NOTE: `scope` and `varScope` diverge after the first `||`/`|`/`&`. The
523 // caller's varScope is only mutated for the `&&`/`;` prefix — this is
524 // conservative (vars set in `A && B | C && D` leak A+B into caller, not
525 // C+D) but safe.
526 //
527 // Efficiency: snapshot is only needed if we hit `||`/`|`/`|&`/`&`. For
528 // the dominant case (`ls`, `git status` — no such separators), skip the
529 // Map alloc via a cheap pre-scan. For `pipeline`, node.type already tells
530 // us stages are subshells — copy once at entry, no snapshot needed (each
531 // reset uses the entry copy pattern via varScope, which is untouched).
532 const isPipeline = node.type === 'pipeline'
533 let needsSnapshot = false
534 if (!isPipeline) {
535 for (const c of node.children) {
536 if (c && (c.type === '||' || c.type === '&')) {
537 needsSnapshot = true
538 break
539 }
540 }
541 }

Callers 3

walkProgramFunction · 0.85
walkRedirectedStatementFunction · 0.85

Calls 13

walkCommandFunction · 0.85
walkRedirectedStatementFunction · 0.85
walkArgumentFunction · 0.85
walkVariableAssignmentFunction · 0.85
applyVarToScopeFunction · 0.85
tooComplexFunction · 0.85
containsAnyPlaceholderFunction · 0.85
walkTestExprFunction · 0.85
setMethod · 0.80
deleteMethod · 0.80
hasMethod · 0.45

Tested by

no test coverage detected