fix(hooks): the args-heredoc guard checks a delimiter split by a backslash-newline, and its catch branch is tested

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-10-31 16:25:53 +01:00
commit 12bca8b953
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
3 changed files with 304 additions and 3 deletions

View file

@ -264,8 +264,10 @@ const commandView = commandPositionView(deobfuscated.replace(/\x1B\[[0-9;]*m/g,
// delimiter would close the heredoc there, and the shell would run every line
// after it as a command (fix round 4.5b, R6). This check reads the raw
// command: `normalized` and `commandView` collapse lines and drop heredoc
// bodies (hooks/lib/bash-view.mjs). A command that never names the delimiter
// skips the whole check.
// bodies (hooks/lib/bash-view.mjs). The check runs when the command names the
// delimiter once every backslash-newline is removed: the shell removes a
// continuation inside the opener's word, and an unquoted body with no closing
// line is still expanded (fix round 4.5b, part 4).
// Every opener in the command is read in two ways, and the command is blocked
// when either reading blocks it (the union):
// 1. A regex finds <<D, <<'D', <<"D" and <<\D, with an optional - and
@ -407,7 +409,7 @@ function argsShellOpeners(text) {
return openers;
}
if (command.includes(ARGS_DELIM)) {
if (command.replace(/\\\n/g, '').includes(ARGS_DELIM)) {
const argsOpeners = command.matchAll(new RegExp(`<<-?\\s*(['"\\\\]?)${ARGS_DELIM}(['"]?)`, 'g'));
const argsLines = command.split('\n');
const argsClosing = argsLines.flatMap((l, i) => (l.trim() === ARGS_DELIM ? [i] : []));