Skip to content

Latest commit

 

History

History
85 lines (59 loc) · 3.59 KB

File metadata and controls

85 lines (59 loc) · 3.59 KB

OA002: Floating tag

Severity: medium  ·  Action: replace (or suggest if no installed version)

What it catches

An override whose pin value is a floating tag or a non-semver string:

Pin value Why it's floating
"latest" Reinterpreted on every install
"next" Pre-release channel; cuts new versions arbitrarily
"*" Matches anything
"x" Same as * in npm semver
"" (empty) npm treats as *
Anything semver.validRange rejects e.g. typos like ^v1.0.0, registry shorthand, git URLs

It also fires on invalid semver ranges that semver.validRange cannot parse. They would either be coerced or rejected at install time, but they make the intent of the pin unreadable.

Example

{
  "overrides": {
    "@esbuild/linux-x64": "latest",  // <- floats with every install
    "postcss": "8.5.x",              // valid range, NOT flagged
    "react": "^18.0.0"               // valid range, NOT flagged
  }
}

Output:

MEDIUM (1)
----------
  OA002  @esbuild/linux-x64
    package.json/overrides/@esbuild~1linux-x64
    Override pinned to floating tag
    fix: applyable patch (1 op)

Why it matters

Overrides usually exist to fix a specific CVE or force a minimum-safe version. A floating tag undermines both jobs:

  • A future install could resolve latest to a version that's actually older than the one that was current when the override was written (rare, but possible during patch back-merges and reverts).
  • More commonly, latest resolves to a new major that breaks the consumer that depended on the lower major: exactly the failure mode an override was meant to prevent.

The pattern is especially insidious in security-driven pins, where the author intended latest to mean "always the newest, so always safe." In practice, >=X.Y.Z is the durable encoding of that intent.

Skipped pin formats

OA002 will not flag:

  • workspace:* / workspace:^ (pnpm workspace protocol)
  • file:../path (local file dependency)
  • link:../path (symlinked dependency)
  • npm:<alias>@<range> (registry aliases: the version selector lives after the alias, not in the pin string)
  • Nested-object override values (those are OA005's territory)

How to fix

cve-lite overrides --fix --rule OA002

--fix emits a single-op replace patch swapping the floating tag for >=<installed-version> when the package is installed under node_modules. If it isn't installed yet, the finding stays suggest-only (no patch attached): install dependencies first, then re-run --fix.

The proposed replacement is >=<installed-version>: a floor, not a ceiling. This:

  • Encodes "at least this version, for security" semantics.
  • Lets pnpm/npm pick the newest compatible version on install.
  • Doesn't lock the consumer out of legitimate patch and minor updates.

If node_modules/<package>/package.json is missing (project hasn't been installed yet), the finding is emitted without a fix patch and the message advises installing dependencies and re-running.

When you might want to keep "latest"

Almost never for production overrides. If your project is itself a starter kit or scaffold that should float with upstream, drive the override-floor convention elsewhere (e.g. a publish-time script that pins to the most recent version at publish).

References