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
Severity: Medium
Summary
Five CI workflows use
npm installinstead ofnpm cifor installing dependencies.npm installresolves semver ranges at install time and can pull in newer dependency versions than whatpackage-lock.jsonspecifies, creating a supply chain attack vector and non-deterministic builds.Affected Files
.github/workflows/storybook-tests.ymlnpm install.github/workflows/storybook-deployment.ymlnpm install(passed asinstall_command).github/workflows/chromatic.ymlnpm install && npm run build.github/workflows/tag-release.ymlnpm install && npm run release.github/workflows/code-analysis.ymlnpm installAlready using
npm ci(correct):publish.yml(line 23),publish-public-build.yml(line 22)Why This Matters
npm installnpm cinode_modulesfirstReal-world precedent: Supply chain attacks like the
event-streamincident (2018) and theua-parser-jshijacking (2021) exploited exactly this pattern — malicious patch versions published within semver ranges were automatically pulled in bynpm installin CI environments.Impact
npm installwill pull it in automatically.npm installcan silently modifypackage-lock.jsonin the CI environment, meaning the tested dependency tree may not match what is committed.Recommended Fix
Replace
npm installwithnpm ciin all five workflows:The
storybook-deployment.ymleven has a comment acknowledging this:install_command: npm install # default: npm ciContext
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