mirror of
https://github.com/affaan-m/ECC.git
synced 2026-11-10 10:06:00 +00:00
fix(hooks): also read a group's writes joined in the order the shell receives them
The commands in a group write one stream into the pipe, so a bypass split across them, as in { printf 'git pu'; printf 'sh --no-verify'; } | sh, was read as two harmless fragments. groupSources() now adds the outputs joined in order next to the separate ones. The group handling moves out of pipelineSources() to keep it under the complexity limit.
This commit is contained in:
2 files changed
+16
-7
No files matched your search
@@ -815,18 +815,25 @@ function commandRole(words, budget) {
|
||||
return { kind: 'opaque', stdin: true };
|
||||
}
|
||||
|
||||
// A brace group or subshell writes what its own commands print into the pipe:
|
||||
// `{ echo 'git push --no-verify'; } | sh` feeds sh like `echo ... | sh`. The
|
||||
// writes form one stream, so they are also read joined in order:
|
||||
// `{ printf 'git pu'; printf 'sh --no-verify'; } | sh`.
|
||||
function groupSources(scope, budget) {
|
||||
const written = [];
|
||||
for (const inner of scanShell(scope.body, budget).commands) {
|
||||
if (!inner.pipeTo) written.push(...pipelineSources(inner, budget));
|
||||
}
|
||||
if (written.length > 1) written.push({ text: written.map(source => source.text).join(''), opaque: written.some(source => source.opaque) });
|
||||
return written;
|
||||
}
|
||||
|
||||
// Literal producers only. Unmodeled transformations remain conservative rather
|
||||
// than executing a formatter, interpreter, shell or user-supplied command.
|
||||
function pipelineSources(command, budget) {
|
||||
const sources = [];
|
||||
for (let current = command; current; current = current.pipeFrom) {
|
||||
// A brace group or subshell writes what its own commands print into the
|
||||
// pipe: `{ echo 'git push --no-verify'; } | sh` feeds sh like `echo ... | sh`.
|
||||
if (current.closedScope) {
|
||||
for (const inner of scanShell(current.closedScope.body, budget).commands) {
|
||||
if (!inner.pipeTo) sources.push(...pipelineSources(inner, budget));
|
||||
}
|
||||
}
|
||||
if (current.closedScope) sources.push(...groupSources(current.closedScope, budget));
|
||||
budget.spend(current.words.length + 1);
|
||||
const { words } = executableWords(current.words, budget);
|
||||
for (const word of words) budget.spend(word.value.length + 3);
|
||||
|
||||
@@ -239,6 +239,8 @@ const executingPayloads = [
|
||||
['pipe subshell data to bash', "(echo 'git push --no-verify'; printf x) | bash"],
|
||||
['pipe nested group data to sh', "{ (printf '%s\\n' 'git commit -n -m x'); } | sh"],
|
||||
['pipe group pipeline data to sh', "{ echo 'git push --no-verify' | grep git; } | sh"],
|
||||
['pipe consecutive group writes to sh', "{ printf 'git pu'; printf 'sh --no-verify'; } | sh"],
|
||||
['pipe echo -n and echo group writes to bash', "{ echo -n 'git commit '; echo '--no-verify -m x'; } | bash"],
|
||||
['execute data through xargs and bash -c', "printf '%s\\n' 'git commit -n -m x' | xargs -I CMD bash -c CMD"],
|
||||
['execute command substitution text through bash', "echo '$(git commit -n -m x)' | bash"],
|
||||
['execute bash here-string', "bash <<< 'git commit -n -m x'"],
|
||||
|
||||
Reference in new issue
Block a user