Skip to content

Latest commit

 

History

History
136 lines (97 loc) · 5.68 KB

File metadata and controls

136 lines (97 loc) · 5.68 KB

OA005: Nested ineffective override

Severity: low to critical (sub-code dependent)  ·  Action: sub-code dependent

This is the core detector that the override-audit work exists for. A common pattern in hand-rolled override readers is to call typeof value === 'string' and silently skip the nested-object override shape entirely, so a class of override-hygiene bugs goes invisible. OA005 surfaces them.

What "nested override" means

npm (only; pnpm does not honour this) supports a parent-scoped override syntax:

{
  "overrides": {
    "@esbuild-kit/core-utils": {
      "esbuild": "^0.25.0"      // only when esbuild is installed via @esbuild-kit/core-utils
    }
  }
}

The pin only applies along the install path through the outer parent. It is more surgical than a top-level "esbuild": "^0.25.0" (which would force esbuild everywhere), but it is also fragile: several separate conditions have to all hold for the pin to actually do anything.

OA005 has five sub-codes, one per failure mode. The detector evaluates them in priority order and emits the most-specific match. Each sub-code is reported as ruleId: "OA005" with a subRuleId of OA005.a ... OA005.e.

The five sub-codes

OA005.a (critical)  ·  remove

The project uses pnpm (or any non-npm PM), but a nested-object override is present. pnpm silently ignores the nested form entirely.

// pnpm-lock.yaml present - npm syntax has no effect
{
  "pnpm": {
    "overrides": {
      "@a/parent": { "inner": "1.0.0" }   // <- entirely ignored by pnpm
    }
  }
}

Critical severity because the pin looks load-bearing but is functionally inert.

OA005.b (high)  ·  remove

The outer parent is not in the resolved tree. The nested pin has no install path to apply to.

{
  "overrides": {
    "@gone/parent": {          // <- @gone/parent isn't in any dep chain
      "inner": "1.0.0"
    }
  }
}

OA005.b wins over OA001 for the same outer key (more specific framing; see "Dedup" below).

OA005.c (high)  ·  remove

The outer parent is in the tree, but the inner key is not in the parent's dependencies / optionalDependencies / peerDependencies. The pin has nothing to bind to.

{
  "overrides": {
    "real-parent": {
      "missing-dep": "1.0.0"   // <- real-parent doesn't declare missing-dep
    }
  }
}

Often caused by the parent dropping a dependency in a minor update without the override being cleaned up.

OA005.d (medium)  ·  suggest (no auto-fix)

The inner dep is installed by another path in the tree at a version that does not satisfy the nested pin. The pin succeeds along the parent path, but the un-pinned copy still installs elsewhere.

{
  "overrides": {
    "parent": {
      "inner": "^2.0.0"        // works for parent -> inner
    }
  }
}
// node_modules/inner@1.5.0   // other path: pin doesn't apply

Severity is medium because the intent of the pin (force inner to >=2.0.0) is not actually being achieved everywhere. The right answer is usually to flatten to a top-level "inner": "^2.0.0" so the pin applies project-wide. This is a judgement call about scope, so OA005.d is suggest-only: it carries no auto-fix patch and --fix will not touch it.

OA005.e (low)  ·  suggest (no auto-fix)

The nested form is valid and effective: no orphan, no leak. But a top-level override would be more durable and easier to reason about.

{
  "overrides": {
    "parent": {
      "inner": "^1.0.0"
    }
  }
}
// Could be flattened to:
{ "overrides": { "inner": "^1.0.0" } }

This is purely a stylistic suggestion. It is emitted at low severity with no auto-fix patch; flattening is a manual choice.

Priority order

The detector evaluates sub-codes top-down and emits the first match per inner key. Once .a fires, .b through .e are not evaluated for that entry; once .b fires, .c through .e are not evaluated; and so on. This guarantees one finding per inner key (not five).

Dedup with OA001

When OA001 (orphan outer) and OA005.b (orphan outer in a nested context) would both fire on the same package, OA005 wins. OA005 is the more specific framing: it tells the user "your nested override has an orphan outer", which is more actionable than the bare "orphan target". The dedup happens in the composite pass (see src/overrides/composite.ts).

Why this is the headline rule

Two long-open pnpm issues (#9852 and #5949) ask for exactly this kind of analysis in pnpm audit. They're still open. Meanwhile hand-rolled override readers that test typeof value === 'string' skip the nested entry entirely, making nested overrides invisible to the security scanner. The OA005.a through OA005.e sub-codes encode every nested-override failure mode seen in practice.

How to fix

cve-lite overrides --fix --rule OA005

--fix applies the remove patch for .a, .b, and .c findings. .d and .e are suggest-only and carry no patch; resolve those by flattening the override yourself if the scope change is what you want.

References