Repository navigation
Description
Problem
The Security Review — Permissions & Secrets job in .github/workflows/squad-repo-health.yml fails with a JavaScript syntax error whenever a finding message contains a backtick.
Observed on PR #1754, run 32392990636:
SyntaxError: Unexpected identifier 'git'
at new AsyncFunction (<anonymous>)
at callAsyncFunction (.../github-script/dist/index.js:64949:16)
Root cause
scripts/security-review.mjs:209 produces messages that embed the offending code in backticks:
message: `Unsafe git operation: \`${label}\` — this can stage unintended files or force-push shared branches.`,The workflow then passes the JSON report into an actions/github-script step by interpolating it into a template literal:
script: |
const { run } = await import(...);
await run({
output: `{ "findings": [ ... "message": "Unsafe git operation: `git push --force-with-lease` ..." ] }`,
...
});The backticks around `git push --force-with-lease` terminate the template literal early. Everything after is parsed as JavaScript, producing Unexpected identifier 'git'.
This means the security gate cannot report any finding whose message quotes code — which is most of them. The job fails, but with a confusing syntax error instead of the actual finding, and the finding text is never posted as a comment.
Impact
- Severity is low for correctness (the job still fails, so the gate is not bypassed) but high for usability: the failure mode is opaque and the actual finding is invisible in the PR.
- It is latent for every category in
security-review.mjsthat formats a label into the message, not justunsafe-git.
Suggested fix
Do not interpolate the report into the script body. Pass it through the step environment instead, so no JS parsing of the payload occurs:
- uses: actions/github-script@...
env:
REPORT: ${{ steps.<scan-step>.outputs.report }}
with:
script: |
const { run } = await import(`${process.env.GITHUB_WORKSPACE}/scripts/repo-health-comment.mjs`);
await run({ github, context, output: process.env.REPORT, job: 'security' });Writing the report to a file and reading it with fs.readFileSync is equally acceptable. The requirement is that the untrusted-shaped payload never becomes part of the parsed script text.
Verification
Add a regression case that runs the reporter against a synthetic finding whose message contains a backtick and a ${ sequence, and assert the step completes and the comment body contains the finding verbatim.
Notes
- Pre-existing; not introduced by chore(squad): merge decision inbox and summarize agent histories #1754, docs(gh-aw): consolidate slash command reference into a single table #1755, or Prevent dispatch_workflow schema probes #1766. It only became visible because chore(squad): merge decision inbox and summarize agent histories #1754 restored an agent history archive containing the phrase in prose.
- The false-positive that surfaced it is tracked separately —
history-archive-*.mdis missing fromUNSAFE_GIT_EXCLUDED_PATHSatscripts/security-review.mjs:185-189.
Activity
bradygaster commentedon Aug 20, 2026
Flight sequencing note — revised 2026-08-20.
Wave: 1.
Depends on: nothing in this stream.
Blocks: nothing directly. It is CI/reporting reliability, not a feature dependency.
Safe to run in parallel with: all included issues. It touches
.github/workflows/squad-repo-health.ymlandscripts/security-review.mjs/tests, notworkflows/squad.mdorworkflows/squad-implement-worker.md.Why / evidence: the scanner creates a finding message with backtick-wrapped labels at
scripts/security-review.mjslines 195-213. The repo-health workflow then interpolates the report into a JavaScript template literal at.github/workflows/squad-repo-health.ymllines 176-187; line 185 passes the security step output directly inside the script body. A backtick in the report therefore becomes parsed script text. This should be fixed early because the job can fail opaquely on any future finding, but no current feature issue depends on it.test: reword fixture comment that tripped the unsafe-git detector
test: use a non-denylisted git command in the transport fixture
fix(ci): force LF on *.mjs so shebanged scripts stay loadable by vite…
fix(ci): stop splicing repo-health report content into github-script …