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

Function walkHeredocRedirect

src/utils/bash/ast.ts:1143–1193  ·  view source on GitHub ↗

* Heredoc redirect. Only quoted-delimiter heredocs (<<'EOF') are safe — * their bodies are literal text. Unquoted-delimiter heredocs (<<EOF) * undergo full parameter/command/arithmetic expansion in the body. * * SECURITY: tree-sitter-bash has a grammar gap — backticks (`...`) inside * an unquot

(node: Node)

Source from the content-addressed store, hash-verified

1141 * <<'EOF' to get a literal body, which the model already prefers.
1142 */
1143function walkHeredocRedirect(node: Node): ParseForSecurityResult | null {
1144 let startText: string | null = null
1145 let body: Node | null = null
1146
1147 for (const child of node.children) {
1148 if (!child) continue
1149 if (child.type === 'heredoc_start') startText = child.text
1150 else if (child.type === 'heredoc_body') body = child
1151 else if (
1152 child.type === '<<' ||
1153 child.type === '<<-' ||
1154 child.type === 'heredoc_end' ||
1155 child.type === 'file_descriptor'
1156 ) {
1157 // expected structural tokens — safe to skip. file_descriptor
1158 // covers fd-prefixed heredocs (`cat 3<<'EOF'`) — walkFileRedirect
1159 // already treats it as a benign structural token.
1160 } else {
1161 // SECURITY: tree-sitter places pipeline / command / file_redirect /
1162 // && / etc. as children of heredoc_redirect when they follow the
1163 // delimiter on the same line (e.g. `ls <<'EOF' | rm x`). Previously
1164 // these were silently skipped, hiding the piped command from
1165 // permission checks. Fail closed like every other walker.
1166 return tooComplex(child)
1167 }
1168 }
1169
1170 const isQuoted =
1171 startText !== null &&
1172 ((startText.startsWith("'") && startText.endsWith("'")) ||
1173 (startText.startsWith('"') && startText.endsWith('"')) ||
1174 startText.startsWith('\\'))
1175
1176 if (!isQuoted) {
1177 return {
1178 kind: 'too-complex',
1179 reason: 'Heredoc with unquoted delimiter undergoes shell expansion',
1180 nodeType: 'heredoc_redirect',
1181 }
1182 }
1183
1184 if (body) {
1185 for (const child of body.children) {
1186 if (!child) continue
1187 if (child.type !== 'heredoc_content') {
1188 return tooComplex(child)
1189 }
1190 }
1191 }
1192 return null
1193}
1194
1195/**
1196 * Here-string redirect (`<<< content`). The content becomes stdin — not

Callers 2

walkRedirectedStatementFunction · 0.85
extractSafeCatHeredocFunction · 0.85

Calls 1

tooComplexFunction · 0.85

Tested by

no test coverage detected