Skip to content

security-review reporter crashes on any finding containing a backtick #1770

Closed

Description

@bradygaster

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.mjs that formats a label into the message, not just unsafe-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

Activity

  1. bradygaster commented on Aug 20, 2026

    @bradygaster
    CollaboratorAuthor

    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.yml and scripts/security-review.mjs/tests, not workflows/squad.md or workflows/squad-implement-worker.md.

    Why / evidence: the scanner creates a finding message with backtick-wrapped labels at scripts/security-review.mjs lines 195-213. The repo-health workflow then interpolates the report into a JavaScript template literal at .github/workflows/squad-repo-health.yml lines 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.

  2. added a commit that references this issue on Aug 22, 2026
    51b8399
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions