Skip to content

Add supply chain queries for npm publish token usage and missing provenance#21621

Open
david-wiggs wants to merge 1 commit intogithub:mainfrom
david-wiggs:main
Open

Add supply chain queries for npm publish token usage and missing provenance#21621
david-wiggs wants to merge 1 commit intogithub:mainfrom
david-wiggs:main

Conversation

@david-wiggs
Copy link
Copy Markdown

Description

Adds two new CodeQL queries for GitHub Actions workflows that detect npm supply chain security risks:

New Queries

  1. actions/npm-token-in-publish (CWE-798, error, severity 9.0)
    Detects publish steps (npm publish, yarn publish, JS-DevTools/npm-publish) that set NODE_AUTH_TOKEN or NPM_TOKEN from a repository secret. Long-lived npm tokens can be stolen and used to publish malicious package versions from outside the CI/CD pipeline (e.g. the axios@1.14.1 attack). The recommended remediation is to use npm Trusted Publishing (OIDC).

  2. actions/missing-provenance-flag (CWE-353, warning, severity 5.0)
    Detects npm publish commands that do not include the --provenance flag. Provenance attestation cryptographically links the published package to a specific source commit and workflow run.

Files Added

  • actions/ql/src/Security/CWE-798/NpmTokenInPublish.ql + .md
  • actions/ql/src/Security/CWE-353/MissingProvenanceFlag.ql + .md
  • Test workflows, .qlref, and .expected files for both queries

@david-wiggs david-wiggs requested a review from a team as a code owner March 31, 2026 14:00
Copilot AI review requested due to automatic review settings March 31, 2026 14:00
@github-actions github-actions bot added documentation Actions Analysis of GitHub Actions labels Mar 31, 2026
command.regexpMatch("(?i).*\\bnpm\\s+publish\\b.*") and
not command.regexpMatch("(?i).*\\bnpm\\s+publish\\b.*--provenance\\b.*")
select run,
"npm publish command does not include '--provenance'. Add '--provenance' to cryptographically link the package to this source commit and workflow run."
secretExpr = env.getEnvVarExpr(envVarName) and
isSecretsReference(secretExpr.getExpression())
select secretExpr,
"Long-lived npm token '$@' is used in a publish step. Use npm Trusted Publishing (OIDC) instead.",
isSecretsReference(secretExpr.getExpression())
select secretExpr,
"Long-lived npm token '$@' is used in a publish step. Use npm Trusted Publishing (OIDC) instead.",
secretExpr, envVarName
@@ -0,0 +1 @@
Security/CWE-353/MissingProvenanceFlag.ql
@@ -0,0 +1 @@
Security/CWE-798/NpmTokenInPublish.ql
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds two new CodeQL queries to the GitHub Actions pack to detect npm supply-chain risks in workflows: (1) use of long-lived npm publish tokens sourced from secrets, and (2) npm publish commands missing --provenance, along with query help and query-tests.

Changes:

  • Added actions/npm-token-in-publish (CWE-798) to flag publish steps that use NODE_AUTH_TOKEN/NPM_TOKEN from secrets.*.
  • Added actions/missing-provenance-flag (CWE-353) to flag npm publish commands missing --provenance.
  • Added query-tests (workflows + .qlref + .expected) for both queries.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
actions/ql/src/Security/CWE-798/NpmTokenInPublish.ql New query to detect long-lived npm tokens used during publish steps.
actions/ql/src/Security/CWE-798/NpmTokenInPublish.md Query help documenting risk + OIDC Trusted Publishing remediation guidance.
actions/ql/src/Security/CWE-353/MissingProvenanceFlag.ql New query to detect npm publish without --provenance.
actions/ql/src/Security/CWE-353/MissingProvenanceFlag.md Query help documenting provenance attestation and recommended workflow permissions.
actions/ql/test/query-tests/Security/CWE-798/NpmTokenInPublish.qlref Registers the CWE-798 query for query-tests.
actions/ql/test/query-tests/Security/CWE-798/NpmTokenInPublish.expected Expected results for npm-token-in-publish query-tests.
actions/ql/test/query-tests/Security/CWE-798/.github/workflows/npm-token-publish.yml Positive test workflow cases for token-in-publish detections.
actions/ql/test/query-tests/Security/CWE-798/.github/workflows/npm-token-publish-safe.yml Negative test workflow cases for token-in-publish.
actions/ql/test/query-tests/Security/CWE-353/MissingProvenanceFlag.qlref Registers the CWE-353 query for query-tests.
actions/ql/test/query-tests/Security/CWE-353/MissingProvenanceFlag.expected Expected results for missing-provenance-flag query-tests.
actions/ql/test/query-tests/Security/CWE-353/.github/workflows/npm-publish-with-provenance.yml Negative test workflow cases (has --provenance).
actions/ql/test/query-tests/Security/CWE-353/.github/workflows/npm-publish-no-provenance.yml Positive test workflow cases (missing --provenance).
Comments suppressed due to low confidence (1)

actions/ql/src/Security/CWE-798/NpmTokenInPublish.md:7

  • The recommendation only mentions removing NODE_AUTH_TOKEN, but the query (and overview) also covers NPM_TOKEN. Consider updating the recommendation to cover both env vars / long-lived npm tokens in general.
Remove `NODE_AUTH_TOKEN` from the publish step. Configure npm Trusted Publishing (OIDC) on npmjs.com, pointing to this repository and workflow. This eliminates the need for long-lived tokens entirely.

@@ -0,0 +1,54 @@
/**
* @name Long-lived npm token used in publish step
* @description The publish step sets NODE_AUTH_TOKEN or NPM_TOKEN from a repository secret.
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The query help text says the token is sourced from a "repository secret", but this query matches any secrets.* reference (which may be repository, environment, or organization). Consider rewording to "a GitHub Actions secret" (or similar) to avoid being misleading.

Suggested change
* @description The publish step sets NODE_AUTH_TOKEN or NPM_TOKEN from a repository secret.
* @description The publish step sets NODE_AUTH_TOKEN or NPM_TOKEN from a GitHub Actions secret.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,35 @@
## Overview

The publish step sets `NODE_AUTH_TOKEN` (or `NPM_TOKEN`) from a repository secret. This is a long-lived credential that can be stolen and used to publish malicious versions from outside the CI/CD pipeline, as demonstrated by the axios@1.14.1 supply chain attack.
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This overview states the token comes from a "repository secret", but the secret context also includes environment/org secrets. Consider using more accurate wording like "a GitHub Actions secret (secrets.*)".

This issue also appears on line 7 of the same file.

Suggested change
The publish step sets `NODE_AUTH_TOKEN` (or `NPM_TOKEN`) from a repository secret. This is a long-lived credential that can be stolen and used to publish malicious versions from outside the CI/CD pipeline, as demonstrated by the axios@1.14.1 supply chain attack.
The publish step sets `NODE_AUTH_TOKEN` (or `NPM_TOKEN`) from a GitHub Actions secret (`secrets.*`). This is a long-lived credential that can be stolen and used to publish malicious versions from outside the CI/CD pipeline, as demonstrated by the axios@1.14.1 supply chain attack.

Copilot uses AI. Check for mistakes.
@david-wiggs
Copy link
Copy Markdown
Author

@copilot apply changes based on the comments in this thread

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Actions Analysis of GitHub Actions documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants