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

Function walkFileRedirect

src/utils/bash/ast.ts:1071–1128  ·  view source on GitHub ↗

* Extract operator + target from a `file_redirect` node. The target must be * a static word or string.

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

Source from the content-addressed store, hash-verified

1069 * a static word or string.
1070 */
1071function walkFileRedirect(
1072 node: Node,
1073 innerCommands: SimpleCommand[],
1074 varScope: Map<string, string>,
1075): Redirect | ParseForSecurityResult {
1076 let op: Redirect['op'] | null = null
1077 let target: string | null = null
1078 let fd: number | undefined
1079
1080 for (const child of node.children) {
1081 if (!child) continue
1082 if (child.type === 'file_descriptor') {
1083 fd = Number(child.text)
1084 } else if (child.type in REDIRECT_OPS) {
1085 op = REDIRECT_OPS[child.type] ?? null
1086 } else if (child.type === 'word' || child.type === 'number') {
1087 // SECURITY: `number` nodes can contain expansion children via the
1088 // `NN#<expansion>` arithmetic-base grammar quirk — same issue as
1089 // walkArgument's number case. `> 10#$(cmd)` runs cmd at runtime.
1090 // Plain word/number nodes have zero children.
1091 if (child.children.length > 0) return tooComplex(child)
1092 // Symmetry with walkArgument (~608): `echo foo > {a,b}` is an
1093 // ambiguous redirect in bash. tree-sitter actually emits a
1094 // `concatenation` node for brace targets (caught by the default
1095 // branch below), but check `word` text too for defense-in-depth.
1096 if (BRACE_EXPANSION_RE.test(child.text)) return tooComplex(child)
1097 // Unescape backslash sequences — same as walkArgument. Bash quote
1098 // removal turns `\X` → `X`. Without this, `cat < /proc/self/\environ`
1099 // stores target `/proc/self/\environ` which evades PROC_ENVIRON_RE,
1100 // but bash reads /proc/self/environ.
1101 target = child.text.replace(/\\(.)/g, '$1')
1102 } else if (child.type === 'raw_string') {
1103 target = stripRawString(child.text)
1104 } else if (child.type === 'string') {
1105 const s = walkString(child, innerCommands, varScope)
1106 if (typeof s !== 'string') return s
1107 target = s
1108 } else if (child.type === 'concatenation') {
1109 // `echo > "foo"bar` — tree-sitter produces a concatenation of string +
1110 // word children. walkArgument already validates concatenation (rejects
1111 // expansions, checks brace syntax) and returns the joined text.
1112 const s = walkArgument(child, innerCommands, varScope)
1113 if (typeof s !== 'string') return s
1114 target = s
1115 } else {
1116 return tooComplex(child)
1117 }
1118 }
1119
1120 if (!op || target === null) {
1121 return {
1122 kind: 'too-complex',
1123 reason: 'Unrecognized redirect shape',
1124 nodeType: node.type,
1125 }
1126 }
1127 return { op, target, fd }
1128}

Callers 2

walkRedirectedStatementFunction · 0.85
walkCommandFunction · 0.85

Calls 4

tooComplexFunction · 0.85
stripRawStringFunction · 0.85
walkStringFunction · 0.85
walkArgumentFunction · 0.85

Tested by

no test coverage detected