* Here-string redirect (`<<< content`). The content becomes stdin — not * argv, not a path. Safe when content is a literal word, raw_string, or * string with no expansions. Reject when content contains $()/${}/$VAR — * those execute arbitrary code or inject runtime values. * * Reuses walkArgume
( node: Node, innerCommands: SimpleCommand[], varScope: Map<string, string>, )
| 1209 | * fires because it has no awareness of herestring vs argv context. |
| 1210 | */ |
| 1211 | function walkHerestringRedirect( |
| 1212 | node: Node, |
| 1213 | innerCommands: SimpleCommand[], |
| 1214 | varScope: Map<string, string>, |
| 1215 | ): ParseForSecurityResult | null { |
| 1216 | for (const child of node.children) { |
| 1217 | if (!child) continue |
| 1218 | if (child.type === '<<<') continue |
| 1219 | // Content node: reuse walkArgument. It returns a string on success |
| 1220 | // (which we discard — content is stdin, irrelevant to permissions) or |
| 1221 | // a too-complex result on failure (expansion found, unresolvable var). |
| 1222 | const content = walkArgument(child, innerCommands, varScope) |
| 1223 | if (typeof content !== 'string') return content |
| 1224 | // Herestring content is discarded (not in argv/envVars/redirects) but |
| 1225 | // remains in .text via raw node.text. Scan it here so checkSemantics's |
| 1226 | // NEWLINE_HASH invariant (bashPermissions.ts relies on it) still holds. |
| 1227 | if (NEWLINE_HASH_RE.test(content)) return tooComplex(child) |
| 1228 | } |
| 1229 | return null |
| 1230 | } |
| 1231 | |
| 1232 | /** |
| 1233 | * Walk a `command` node and extract argv. Children appear in order: |
no test coverage detected