Skip to content

Security: npm install used instead of npm ci in 5 CI workflows #405

@pratikchaskar

Description

@pratikchaskar

Severity: Medium

Summary

Five CI workflows use npm install instead of npm ci for installing dependencies. npm install resolves semver ranges at install time and can pull in newer dependency versions than what package-lock.json specifies, creating a supply chain attack vector and non-deterministic builds.

Affected Files

Workflow File Line Current Command
.github/workflows/storybook-tests.yml 16 npm install
.github/workflows/storybook-deployment.yml 36 npm install (passed as install_command)
.github/workflows/chromatic.yml 22 npm install && npm run build
.github/workflows/tag-release.yml 29 npm install && npm run release
.github/workflows/code-analysis.yml 37 npm install

Already using npm ci (correct): publish.yml (line 23), publish-public-build.yml (line 22)

Why This Matters

Behavior npm install npm ci
Respects lockfile exactly No — may update it Yes
Can install newer dependency versions Yes (semver range resolution) No (fails if out of sync)
Removes node_modules first No Yes (clean slate)
Reproducible builds No Yes

Real-world precedent: Supply chain attacks like the event-stream incident (2018) and the ua-parser-js hijacking (2021) exploited exactly this pattern — malicious patch versions published within semver ranges were automatically pulled in by npm install in CI environments.

Impact

  • If any dependency within a semver range publishes a compromised patch version, npm install will pull it in automatically.
  • Two CI runs of the same commit could install different dependency trees if a transitive dependency publishes a new version between runs.
  • npm install can silently modify package-lock.json in the CI environment, meaning the tested dependency tree may not match what is committed.

Recommended Fix

Replace npm install with npm ci in all five workflows:

# storybook-tests.yml, chromatic.yml, code-analysis.yml
- name: Install dependencies
  run: npm ci

# tag-release.yml
- name: Build the release
  run: npm ci && npm run release

# storybook-deployment.yml (bitovi action config)
install_command: npm ci

The storybook-deployment.yml even has a comment acknowledging this: install_command: npm install # default: npm ci

Context

Identified during a modular security audit. The inconsistency (2 workflows correct, 5 incorrect) suggests the team is aware of the best practice but has not applied it uniformly.


Found by automated security audit — VULN-07

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions