* Recursively validate an arithmetic_expansion node. Allows only literal * numeric expressions — no variables, no substitutions. Returns null if * safe, or a too-complex result if not. * * Variables are rejected because bash arithmetic recursively evaluates * variable values: if x='a[$(cmd)]' t
(node: Node)
| 1673 | * won't match any sensitive path/deny patterns. |
| 1674 | */ |
| 1675 | function walkArithmetic(node: Node): ParseForSecurityResult | null { |
| 1676 | for (const child of node.children) { |
| 1677 | if (!child) continue |
| 1678 | if (child.children.length === 0) { |
| 1679 | if (!ARITH_LEAF_RE.test(child.text)) { |
| 1680 | return { |
| 1681 | kind: 'too-complex', |
| 1682 | reason: `Arithmetic expansion references variable or non-literal: ${child.text}`, |
| 1683 | nodeType: 'arithmetic_expansion', |
| 1684 | } |
| 1685 | } |
| 1686 | continue |
| 1687 | } |
| 1688 | switch (child.type) { |
| 1689 | case 'binary_expression': |
| 1690 | case 'unary_expression': |
| 1691 | case 'ternary_expression': |
| 1692 | case 'parenthesized_expression': { |
| 1693 | const err = walkArithmetic(child) |
| 1694 | if (err) return err |
| 1695 | break |
| 1696 | } |
| 1697 | default: |
| 1698 | return tooComplex(child) |
| 1699 | } |
| 1700 | } |
| 1701 | return null |
| 1702 | } |
| 1703 | |
| 1704 | /** |
| 1705 | * Check if a command_substitution node is exactly `$(cat <<'DELIM'...DELIM)` |
no test coverage detected