MCPcopy Index your code
hub / github.com/codeaashu/claude-code / walkString

Function walkString

src/utils/bash/ast.ts:1508–1652  ·  view source on GitHub ↗

* Extract literal content from a double-quoted string node. A `string` node's * children are `"` delimiters, `string_content` literals, and possibly * expansion nodes. * * tree-sitter quirk: literal newlines inside double quotes are NOT included * in `string_content` node text. bash preserves t

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

Source from the content-addressed store, hash-verified

1506 * match what bash actually sees.
1507 */
1508function walkString(
1509 node: Node,
1510 innerCommands: SimpleCommand[],
1511 varScope: Map<string, string>,
1512): string | ParseForSecurityResult {
1513 let result = ''
1514 let cursor = -1
1515 // SECURITY: Track whether the string contains a runtime-unknown
1516 // placeholder ($() output or unknown-value tracked var) vs any literal
1517 // content. A string that is ONLY a placeholder (`"$(cmd)"`, `"$VAR"`
1518 // where VAR holds an unknown sentinel) produces an argv element that IS
1519 // the placeholder — which downstream path validation resolves as a
1520 // relative filename within cwd, bypassing the check. `cd "$(echo /etc)"`
1521 // would pass validation but runtime-cd into /etc. We reject
1522 // solo-placeholder strings; placeholders mixed with literal content
1523 // (`"prefix: $(cmd)"`) are safe — runtime value can't equal a bare path.
1524 let sawDynamicPlaceholder = false
1525 let sawLiteralContent = false
1526 for (const child of node.children) {
1527 if (!child) continue
1528 // Index gap between this child and the previous one = dropped newline(s).
1529 // Ignore the gap before the first non-delimiter child (cursor === -1).
1530 // Skip gap-fill for `"` delimiters: a gap before the closing `"` is the
1531 // tree-sitter whitespace-only-string quirk (space/tab, not newline) — let
1532 // the Fix C check below catch it as too-complex instead of mis-filling
1533 // with `\n` and diverging from bash.
1534 if (cursor !== -1 && child.startIndex > cursor && child.type !== '"') {
1535 result += '\n'.repeat(child.startIndex - cursor)
1536 sawLiteralContent = true
1537 }
1538 cursor = child.endIndex
1539 switch (child.type) {
1540 case '"':
1541 // Reset cursor after opening quote so the gap between `"` and the
1542 // first content child is captured.
1543 cursor = child.endIndex
1544 break
1545 case 'string_content':
1546 // Bash double-quote escape rules (NOT the generic /\\(.)/g used for
1547 // unquoted words in walkArgument): inside "...", a backslash only
1548 // escapes $ ` " \ — other sequences like \n stay literal. So
1549 // `"fix \"bug\""` → `fix "bug"`, but `"a\nb"` → `a\nb` (backslash
1550 // kept). tree-sitter preserves the raw escapes in .text; we resolve
1551 // them here so argv matches what bash actually passes.
1552 result += child.text.replace(/\\([$`"\\])/g, '$1')
1553 sawLiteralContent = true
1554 break
1555 case DOLLAR:
1556 // A bare dollar sign before closing quote or a non-name char is
1557 // literal in bash. tree-sitter emits it as a standalone node.
1558 result += DOLLAR
1559 sawLiteralContent = true
1560 break
1561 case 'command_substitution': {
1562 // Carve-out: `$(cat <<'EOF' ... EOF)` is safe. The quoted-delimiter
1563 // heredoc body is literal (no expansion), and `cat` just prints it.
1564 // The substitution result is therefore a known static string. This
1565 // pattern is the idiomatic way to pass multi-line content to tools

Callers 2

walkFileRedirectFunction · 0.85
walkArgumentFunction · 0.85

Calls 5

extractSafeCatHeredocFunction · 0.85
tooComplexFunction · 0.85
resolveSimpleExpansionFunction · 0.85
walkArithmeticFunction · 0.85

Tested by

no test coverage detected