# Dependency version specifiers do not allow unintended breaking changes

- **Pattern:** `ab-000679` (`code-maintainability.dependency-management.version-specifiers`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000679
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000679)

## 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:

```json
// 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.json` for production dependencies. Look for:
  - Bare `*` or `latest` — installs whatever is newest, including major breaking changes
  - `>= X.Y.Z` with no upper bound — allows any version including future majors
  - `^X.Y.Z` on 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 `>= X` as violations.
- **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 `*` or `latest` or 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.0` appears without an upper bound — this permits arbitrary future major versions.
- **Skip (N/A) when:** The project has no `package.json`. Signal: no `package.json` present.
- **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`. `*` or `latest` means an npm install six months from now could install a breaking change:

  ```json
  // Before (dangerous):
  "dependencies": {
    "some-library": "latest",
    "another-lib": "*"
  }

  // After (safe):
  "dependencies": {
    "some-library": "^2.4.1",
    "another-lib": "^1.0.0"
  }
  ```

  Run `npm install` with pinned versions, then commit the updated `package.json` and lock file together.

---

## External references

- cwe CWE-1357
- ssdf PS.3

Taxons: supply-chain

HTML version: https://auditbuffet.com/patterns/ab-000679
