Skip to content

Latest commit

 

History

History
188 lines (142 loc) · 5.13 KB

File metadata and controls

188 lines (142 loc) · 5.13 KB

Running scriptgate in CI

Three recipes, in increasing order of how much they ask of your team. Start with the first.

Whichever you pick, install first. scriptgate reads node_modules, so a job that scans before npm ci finds nothing and reports success.

1. The gate

name: supply chain

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  install-scripts:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm

      - run: npm ci
      - run: npx scriptgate check

Fails when a dependency runs code at install time that nobody approved, when an approval is pinned to a version that is no longer installed, or when a script scores high or above.

A note on npm ci under npm v12: it will not run unapproved install scripts either, so a job that passes without an allowlist may be building a project whose native addons were never compiled. That is the failure scriptgate check exists to make loud.

2. Code scanning alerts

Findings become alerts with a lifecycle: they appear on the pull request that introduced them, they can be dismissed with a stated reason, and they close themselves when fixed.

permissions:
  contents: read
  security-events: write

jobs:
  install-scripts:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm

      - run: npm ci

      - name: Analyse install scripts
        run: npx scriptgate scan --sarif > scriptgate.sarif
        continue-on-error: true

      - uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: scriptgate.sarif
          category: scriptgate

continue-on-error is deliberate here: scan exits 0 whether or not it finds anything, but the upload step should still run if a future version changes that.

Findings are anchored to package.json, because that is the only file in the repository you can edit to resolve them — the offending script lives in node_modules, which is not committed.

3. A comment on the pull request

What a reviewer looking at a dependency bump actually wants: the delta in install-time responsibility, with the settled decisions collapsed out of the way.

permissions:
  contents: read
  pull-requests: write

jobs:
  install-scripts:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm

      - run: npm ci

      - name: Build the report
        run: npx scriptgate scan --markdown > report.md

      - name: Comment
        uses: actions/github-script@v7
        with:
          script: |
            const body = require('node:fs').readFileSync('report.md', 'utf8');
            const { owner, repo } = context.repo;
            const issue_number = context.issue.number;

            const comments = await github.rest.issues.listComments({ owner, repo, issue_number });
            const mine = comments.data.find((c) =>
              c.user.type === 'Bot' && c.body.includes('Install-script review'));

            if (mine) {
              await github.rest.issues.updateComment({ owner, repo, comment_id: mine.id, body });
            } else {
              await github.rest.issues.createComment({ owner, repo, issue_number, body });
            }

Updating the existing comment rather than adding one keeps a long-running pull request readable.

Do not run this on pull_request_target. That trigger gives the workflow write permissions and repository secrets while checking out code from the fork — and the whole subject of this tool is that a dependency's install script is code you have not read. Use pull_request, which is what the recipe above does.

The job summary

--markdown also renders in a job summary, which costs no permissions at all:

- run: npx scriptgate scan --markdown >> "$GITHUB_STEP_SUMMARY"

Other CI systems

Nothing above is GitHub-specific except the SARIF upload and the comment.

GitLab

install-scripts:
  image: node:22
  script:
    - npm ci
    - npx scriptgate check

A pre-commit or pre-push hook — cheap, because the gate is a static read with no network:

npx scriptgate check --quiet

Exit codes

Code Meaning
0 clean
1 the gate failed, or explain found no such package
2 bad usage, or a project that could not be read

Code 2 is worth separating in a pipeline: it means the tool could not do its job — usually a missing node_modules — rather than that your dependencies are fine. Treating it as a pass is the mistake to avoid.

Keeping output out of the way

check writes its report to stdout and its verdict to stderr, so this produces a clean JSON file and still shows the reason in the log:

npx scriptgate check --json > scriptgate.json

Colour disables itself automatically when the output is not a terminal. NO_COLOR=1 forces it off; FORCE_COLOR=1 forces it on for a CI system that renders ANSI.