chore(scripts): scan the shell command template, and flag a multi-line env override (#2146)

Copilot, ninth and tenth reviews:

- A custom `shell:` is a COMMAND TEMPLATE that Actions runs around every
  script (`{0}` is the script path), so `shell: npm run local:gate && bash {0}`
  invoked the gate while the `run:` value beside it looked harmless. Step
  `shell`, and `defaults.run.shell` at both workflow and job level, are
  command regions now. An ordinary `bash`/`pwsh`/`cmd` matches no rule, so
  naming them costs nothing.

- GitHub's multiline environment-file syntax (`SMOKE_BROWSER<<EOF`, value,
  delimiter) bypassed the assignment patterns. It is reported as an override
  whose value the guard cannot READ rather than parsed: the value can arrive
  through any number of shapes — a brace group redirected to $GITHUB_ENV, a
  heredoc nested in another — and a parser handling some of them would be a
  coverage claim that holds only for the shapes someone thought of. Nobody
  spells `chromium` that way, and if they did, `SMOKE_BROWSER=chromium` is the
  fix.

Signed-off-by: Cliff Hall <cliff@futurescale.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-27 02:32:23 -04:00
commit 2ead041572

View file

@ -300,6 +300,70 @@ describe("findWorkflowViolations", () => {
),
rules: [],
},
{
// Copilot, ninth review: a custom `shell:` is a COMMAND TEMPLATE Actions
// runs around every script, so the invocation hides beside a `run:` value
// that looks harmless.
name: "flags a command in a step's shell template",
text: [
"on: push",
"jobs:",
" build:",
" steps:",
" - shell: npm run local:gate && bash {0}",
" run: echo hello",
].join("\n"),
rules: [VIOLATION.LOCAL_SCRIPT],
},
{
name: "flags one in a job's defaults.run.shell",
text: [
"on: push",
"jobs:",
" build:",
" defaults:",
" run:",
" shell: npm run smoke:web:firefox && bash {0}",
" steps:",
" - run: echo hello",
].join("\n"),
rules: [VIOLATION.ENGINE_SCRIPT],
},
{
name: "flags one in the workflow-level defaults.run.shell",
text: [
"on: push",
"defaults:",
" run:",
" shell: npm run local:gate && bash {0}",
"jobs:",
" build:",
" steps:",
" - run: echo hello",
].join("\n"),
rules: [VIOLATION.LOCAL_SCRIPT],
},
{
name: "leaves an ordinary shell name alone",
text: workflow(" - shell: pwsh\n run: npm run smoke"),
rules: [],
},
{
// Copilot, tenth review: GitHub's multiline environment-file syntax.
// Reported as unreadable rather than parsed — see the comment on the
// pattern for why parsing it would be a coverage claim that only holds
// for the shapes someone thought of.
name: "flags a multi-line $GITHUB_ENV override",
text: workflow(
[
" - run: |",
' echo "SMOKE_BROWSER<<EOF" >> $GITHUB_ENV',
' echo "firefox" >> $GITHUB_ENV',
' echo "EOF" >> $GITHUB_ENV',
].join("\n"),
),
rules: [VIOLATION.BROWSER_ENV],
},
{
name: "flags a workflow-level env override",
text: [