MCPcopy Create free account
hub / github.com/claude-code-best/claude-code / walkVariableAssignment

Function walkVariableAssignment

src/utils/bash/ast.ts:1777–1922  ·  view source on GitHub ↗
(
  node: Node,
  innerCommands: SimpleCommand[],
  varScope: Map<string, string>,
)

Source from the content-addressed store, hash-verified

1775}
1776
1777function walkVariableAssignment(
1778 node: Node,
1779 innerCommands: SimpleCommand[],
1780 varScope: Map<string, string>,
1781): { name: string; value: string; isAppend: boolean } | ParseForSecurityResult {
1782 let name: string | null = null
1783 let value = ''
1784 let isAppend = false
1785
1786 for (const child of node.children) {
1787 if (!child) continue
1788 if (child.type === 'variable_name') {
1789 name = child.text
1790 } else if (child.type === '=' || child.type === '+=') {
1791 // `PATH+=":/new"` — tree-sitter emits `+=` as a distinct operator
1792 // node. Without this case it falls through to walkArgument below
1793 // → tooComplex on unknown type `+=`.
1794 isAppend = child.type === '+='
1795 } else if (child.type === 'command_substitution') {
1796 // $() as the variable's value. The output becomes a STRING stored in
1797 // the variable — it's NOT a positional argument (no path/flag concern).
1798 // `VAR=$(date)` runs `date`, stores output. `VAR=$(rm -rf /)` runs
1799 // `rm` — the inner command IS checked against permission rules, so
1800 // `rm` must match a rule. The variable just holds whatever `rm` prints.
1801 const err = collectCommandSubstitution(child, innerCommands, varScope)
1802 if (err) return err
1803 value = CMDSUB_PLACEHOLDER
1804 } else if (child.type === 'simple_expansion') {
1805 // `VAR=$OTHER` — assignment RHS does NOT word-split or glob-expand
1806 // in bash (unlike command arguments). So `A="a b"; B=$A` sets B to
1807 // the literal "a b". Resolve as if inside a string (insideString=true)
1808 // so BARE_VAR_UNSAFE_RE doesn't over-reject. The resulting value may
1809 // contain spaces/globs — if B is later used as a bare arg, THAT use
1810 // will correctly reject via BARE_VAR_UNSAFE_RE.
1811 const v = resolveSimpleExpansion(child, varScope, true)
1812 if (typeof v !== 'string') return v
1813 // If v is VAR_PLACEHOLDER (OTHER holds unknown), store it — combined
1814 // with containsAnyPlaceholder in the caller to treat as unknown.
1815 value = v
1816 } else {
1817 const v = walkArgument(child, innerCommands, varScope)
1818 if (typeof v !== 'string') return v
1819 value = v
1820 }
1821 }
1822
1823 if (name === null) {
1824 return {
1825 kind: 'too-complex',
1826 reason: 'Variable assignment without name',
1827 nodeType: 'variable_assignment',
1828 }
1829 }
1830 // SECURITY: tree-sitter-bash accepts invalid var names (e.g. `1VAR=value`)
1831 // as variable_assignment. Bash only recognizes [A-Za-z_][A-Za-z0-9_]* —
1832 // anything else is run as a COMMAND. `1VAR=value` → bash tries to execute
1833 // `1VAR=value` from PATH. We must not treat it as an inert assignment.
1834 if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {

Callers 2

collectCommandsFunction · 0.85
walkCommandFunction · 0.85

Calls 4

resolveSimpleExpansionFunction · 0.85
walkArgumentFunction · 0.85
containsAnyPlaceholderFunction · 0.85

Tested by

no test coverage detected