Skip to content

feat: add npm upgrade step in publish workflow#217

Merged
yujeong-jeon merged 1 commit into
mainfrom
fix/release-ci
Apr 14, 2026
Merged

feat: add npm upgrade step in publish workflow#217
yujeong-jeon merged 1 commit into
mainfrom
fix/release-ci

Conversation

@yujeong-jeon

Copy link
Copy Markdown
Contributor

Related Issue

Describe your changes

Request

@npayfebot

Copy link
Copy Markdown
Collaborator

🔍 No files have been changed

Latest commit: b43f551

Please check your commit.

powered by: naverpay size-action

- name: Check and upgrade npm
run: |
echo "Current npm version:"
npm --version

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 The new "Check and upgrade npm" step uses npm install -g npm@latest, which is unpinned — every workflow run may install a different npm version, making builds non-deterministic. More critically, in a publish workflow with registry credentials, a compromised or broken version briefly tagged @latest on the npm registry would be automatically adopted; pin to a specific version like npm@10.9.2 instead.

Extended reasoning...

What the bug is and how it manifests

The newly added step runs npm install -g npm@latest without pinning to a specific version. The @latest dist-tag on the npm registry is a floating pointer that changes whenever a new npm release is published. This means the version of npm installed in the workflow is determined at runtime by whatever is currently tagged @latest, not by anything recorded in source control.

The specific code path that triggers it

Line 57 of .github/workflows/publish.yaml (the new step named "Check and upgrade npm") runs:

npm install -g npm@latest

This executes on every trigger of the publish job — both on push to main (the release path) and on canary/RC issue_comment triggers.

Why existing code doesn't prevent it

There is no version lock or checksum verification anywhere in the workflow for this global npm install. GitHub Actions runners do cache some tooling, but a fresh npm install -g bypasses any such caching and fetches directly from the registry. Nothing in the repository enforces which npm version is installed.

What the impact would be

Two distinct risks apply:

  1. Non-determinism: Different workflow runs (e.g., a hotfix publish vs. a regular release one week later) may use different npm versions. If a newer npm changes behavior around package resolution, lockfile handling, or publish semantics, the discrepancy would be silent and hard to debug.
  2. Supply chain risk: This is a publish workflow that pushes packages to the npm registry using pnpm release (backed by changeset-actions). Downstream consumers of these packages trust that the publishing toolchain was not tampered with. If a malicious or broken npm release is briefly tagged @latest on the registry — a known attack category — the workflow would install it and use it for the publish operation before anyone can react.

How to fix it

Replace the floating tag with a pinned version:

- name: Check and upgrade npm
  run: |
      echo "Current npm version:"
      npm --version
      npm install -g npm@10.9.2
      echo "Upgraded npm version:"
      npm --version

The pinned version should be deliberately updated in a PR when an upgrade is intentional, making the change explicit and reviewable.

Step-by-step proof

  1. Workflow run A executes today: npm@latest resolves to 10.9.2. Packages are published successfully.
  2. npm Inc. releases npm@11.0.0 tomorrow and tags it @latest.
  3. Workflow run B executes on the next push to main: npm install -g npm@latest now installs 11.0.0.
  4. npm@11.0.0 has a breaking change in its publish behavior (or, in the attack scenario, is a compromised build).
  5. pnpm release (or changeset-actions) calls npm under the hood; packages are published with the new/compromised npm binary without any human review of the npm upgrade.
  6. Downstream consumers install the affected package versions.

@2-one-week 2-one-week left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

감삼다

@yujeong-jeon yujeong-jeon merged commit bea8e79 into main Apr 14, 2026
8 of 9 checks passed
@yujeong-jeon yujeong-jeon deleted the fix/release-ci branch April 14, 2026 05:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants