Dependency version specifiers do not allow unintended breaking changes
Why it matters
A package.json with "some-library": "latest" or "another-lib": "*" means a fresh npm install six months from now could pull a breaking major version — silently. CWE-1357 applies: the project depends on whatever happens to be newest, not on a version it was tested against. SSDF PS.3 requires version-pinning as a supply chain control. This failure mode is especially dangerous in CI/CD pipelines that run npm install without a lockfile — a new release of a dependency can break production deployments without any code change.
Severity rationale
Medium because wildcard or unbounded version specifiers allow untested breaking changes to be silently installed on any fresh `npm install`, turning a routine deploy into a potential breakage.
Remediation
Replace every *, latest, and unbounded >= specifier with a pinned or caret range:
// Before (dangerous):
"dependencies": {
"some-library": "latest",
"another-lib": "*"
}
// After (safe):
"dependencies": {
"some-library": "^2.4.1",
"another-lib": "^1.0.0"
}
Run npm install after updating specifiers to regenerate the lockfile, then commit both package.json and package-lock.json together. Caret ranges (^X.Y.Z) allow minor and patch updates but block major version jumps.
Detection
-
ID:
version-specifiers -
Severity:
medium -
What to look for: Examine the version specifiers in
package.jsonfor production dependencies. Look for:- Bare
*orlatest— installs whatever is newest, including major breaking changes >= X.Y.Zwith no upper bound — allows any version including future majors^X.Y.Zon packages with poor semver hygiene (i.e., packages that ship breaking changes in minor versions)
Note:
^X.Y.Z(caret) is generally acceptable for well-behaved packages as it allows minor/patch updates but not major.~X.Y.Z(tilde, patch only) is more conservative. Flag*,latest, and>= Xas violations. - Bare
-
Pass criteria: Enumerate all version specifiers in production dependencies. All production dependencies use specific versions (
X.Y.Z), caret ranges (^X.Y.Z), or tilde ranges (~X.Y.Z). No more than 0 bare*orlatestor unbounded>=specifiers. Report the count: "X of Y production dependencies use safe version specifiers." -
Fail criteria: One or more production dependencies use
*,latest, or an unbounded>=version specifier. Do NOT pass when a specifier like>=1.0.0appears without an upper bound — this permits arbitrary future major versions. -
Skip (N/A) when: The project has no
package.json. Signal: nopackage.jsonpresent. -
Detail on fail:
"2 production dependencies use 'latest' specifier: 'some-package': 'latest'. This means npm install after 6 months could install a breaking major version."or"'react': '*' — wildcard specifier allows any version including hypothetical future React 20+ with breaking changes." -
Remediation: Version specifiers determine what gets installed on a fresh
npm install.*orlatestmeans an npm install six months from now could install a breaking change:// Before (dangerous): "dependencies": { "some-library": "latest", "another-lib": "*" } // After (safe): "dependencies": { "some-library": "^2.4.1", "another-lib": "^1.0.0" }Run
npm installwith pinned versions, then commit the updatedpackage.jsonand lock file together.
External references
- cwe · CWE-1357 — Reliance on Insufficiently Trustworthy Component
- ssdf:800-218 · PS.3 — Archive and Protect Each Software Release
Taxons
History
- 2026-04-18·v1.0.0·Initial import from code-maintainability·automated