Skip to content

[DWS] SHA-pin GitHub Actions, and replace Snyk and Gitleaks with GitHub secret scanning - #18

Open
nickwinder wants to merge 3 commits into
mainfrom
nick/dws/ci-sha-pin-secret-scanning
Open

[DWS] SHA-pin GitHub Actions, and replace Snyk and Gitleaks with GitHub secret scanning#18
nickwinder wants to merge 3 commits into
mainfrom
nick/dws/ci-sha-pin-secret-scanning

Conversation

@nickwinder

Copy link
Copy Markdown
Collaborator

Follow-up to #16, which was approved with a request to SHA-pin the GitHub Actions.

SHA pinning

All 20 uses: references across the four workflows now name a full commit SHA, with the release in a trailing comment so the version stays readable and Dependabot can still open update PRs.

Action Pin
actions/checkout 3d3c42e5… # v7.0.1
actions/setup-node 82076278… # v7.0.0
actions/upload-artifact 043fb46d… # v7.0.1
actions/github-script 3a2844b7… # v9.0.0

A tag is a mutable pointer. Anyone who can move it can run their code with this repository's workflow token and secrets. A commit SHA cannot be moved.

Removing two scans that never ran

Snyk. The step was gated behind if: env.SNYK_TOKEN != ''. No SNYK_TOKEN secret exists on the repository or the organization, so the guard was always false. continue-on-error: true meant it could not have failed the job even if it had run. npm audit --production --audit-level=moderate already runs two steps earlier in the same job, against the same dependency tree, and needs no token.

Gitleaks. The action requires a licence key for repositories owned by an organization. No GITLEAKS_LICENSE secret exists, so the step failed on every run, and continue-on-error: true hid that failure.

What replaces them

GitHub secret scanning and push protection are now enabled on the repository. Push protection rejects a commit containing a recognised credential before it reaches the remote, where Gitleaks could only report a key that was already published. CodeQL default setup is also enabled, covering actions and javascript-typescript.

The workflow keeps its own check for the Nutrient key formats (pdf_live_, its base64 form, sk_|pk_|nutr_sk_). Those are not GitHub partner patterns, so nothing else looks for them.

fetch-depth: 0 and the security-events: write permission went with the Gitleaks step. Only Gitleaks needed the full history and the SARIF upload.

Verification

Local, on fb2025a:

$ npm run typecheck && npm run lint && npm run test:unit
> tsc --noEmit && tsc --noEmit --project src/__tests__/tsconfig.json
> eslint src
Test Suites: 11 passed, 11 total
Tests:       315 passed, 315 total

Every workflow file still parses, and no unpinned reference remains:

$ python3 -c "import yaml,glob; [yaml.safe_load(open(f)) for f in glob.glob('.github/workflows/*.yml')]"
$ grep -rn "uses:" .github/workflows/ | grep -vE "@[0-9a-f]{40} # v"
(no output)

Each pinned SHA was resolved from the GitHub API and confirmed to match the release named in its comment, for example:

$ gh api repos/actions/checkout/commits/v7.0.1 --jq .sha
3d3c42e5aac5ba805825da76410c181273ba90b1

The checkout, setup-node, github-script and upload-artifact pins were exercised end to end by CI on #16 before the merge: 11 checks passed, including unit tests on Node 22 and 24 across ubuntu, windows and macos.

Repository settings, applied and read back:

$ gh api repos/:owner/:repo --jq .security_and_analysis
secret_scanning:                 enabled
secret_scanning_push_protection: enabled

$ gh api repos/:owner/:repo/code-scanning/default-setup --jq .state
configured

Tags are mutable. A maintainer, or an attacker with write access to an
action's repository, can move a tag to a different commit, and the next
workflow run then executes that code with our workflow token and repo
secrets. A full commit SHA is immutable and closes that window.

The trailing comment records the release each SHA resolves to, so the
version stays readable and Dependabot can still open update PRs.

snyk/actions/node moves off the `master` branch to the v1.0.0 release
SHA. A branch ref is mutable for the same reason a tag is, and master is
only three commits ahead of v1.0.0.
The step never ran. Its own guard, `if: env.SNYK_TOKEN != ''`, is false
because no SNYK_TOKEN secret exists on the repository or the
organization, and `continue-on-error: true` meant it could not fail the
job even if it had run.

`npm audit --production --audit-level=moderate` runs two steps earlier
in the same job, against the same dependency tree, and needs no token.
That is the coverage the Snyk step was supposed to add.
@nickwinder
nickwinder force-pushed the nick/dws/ci-sha-pin-secret-scanning branch from fb2025a to ee4f375 Compare August 11, 2026 08:39
Gitleaks needs a licence key for repositories owned by an organization.
No GITLEAKS_LICENSE secret exists here, so the step failed on every run,
and `continue-on-error: true` hid that failure.

GitHub secret scanning and push protection are now enabled on the
repository. They cover the same ground from a better position: push
protection rejects the commit before the secret reaches the remote,
where Gitleaks could only report a key that was already published.

The job keeps its own grep for the Nutrient key formats. Those are not
GitHub partner patterns, so nothing else looks for them.

`fetch-depth: 0` and the `security-events: write` permission went with
the step. Only Gitleaks needed the full history and the SARIF upload.
@nickwinder
nickwinder force-pushed the nick/dws/ci-sha-pin-secret-scanning branch from ee4f375 to 75af958 Compare August 11, 2026 08:46
@nickwinder
nickwinder marked this pull request as ready for review August 11, 2026 08:57
@nickwinder
nickwinder requested a review from HungKNguyen August 11, 2026 08:58
@nickwinder nickwinder self-assigned this Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant