Skip to content

[Bug] parent-update suggests npm update <parent> that cannot refresh the child when the parent is exact-pinned #1286

Closed

Description

@alex-powell

Summary

In the suggested fix commands, the "High severity parent updates within range" section (parent-update) can suggest npm update <parent> when the parent is exact-pinned and its target version equals its current version. In that case npm update <parent> does not touch the parent's subtree, so the vulnerable child stays where it is.

Found in 1.37.0, still reproduces on current main.

Minimal repro

package.json:

{
  "name": "repro",
  "version": "1.0.0",
  "private": true,
  "dependencies": {
    "astro": "5.13.2"
  }
}

Build the lockfile and put the transitive devalue on a vulnerable version that is still inside astro's declared range (devalue: ^5.1.1):

npm install --package-lock-only --ignore-scripts
# then edit package-lock.json so node_modules/devalue is 5.9.2
# (version, resolved and integrity from `npm view devalue@5.9.2 dist`)

devalue 5.9.2 is affected by an advisory fixed in 5.9.4.

Run cve-lite . --json (or the default terminal output).

Actual

The parent-update:high section suggests:

{
  "kind": "parent-update",
  "command": "npm update astro",
  "targets": [
    {
      "package": "astro",
      "currentVersion": "5.13.2",
      "targetVersion": "5.13.2",
      "childPackage": "devalue",
      "childTargetVersion": "5.9.4",
      "reason": "astro@5.13.2 already allows devalue@5.9.4 within the current dependency range",
      "command": "npm update astro"
    }
  ]
}

Running it has no effect on the child:

npm update astro --package-lock-only   # node_modules/devalue stays at 5.9.2
npm update devalue --package-lock-only # node_modules/devalue moves to 5.9.4

The parent cannot move (5.13.2 -> 5.13.2), so the command is a no-op for the finding it is meant to fix.

Expected

When the parent's target equals its current version (for example because it is exact-pinned), the suggestion should be a command that actually refreshes the child. Since the child's fixed version is already allowed by the parent's declared range, npm update <child> (here npm update devalue) does that.

Suggested behaviour

  • For parent-update targets where targetVersion === currentVersion and childTargetVersion is within the parent's declared range for the child, emit npm update <child> instead of npm update <parent> (same for any --fix path that runs the same command).
  • Keep the display fields from fix(remediation): stop parent-update rendering a child's version as the parent's #1174 as they are (targetVersion belongs to the parent, the child is on childPackage / childTargetVersion).
  • If the child's fix is not within the parent's range, fall back to whatever the existing convention is for that case rather than a no-op parent update.

Related: #1152 / #1174, #989, #1113, and the exact-pinned-intermediate fixture from #661.

Activity

  1. sonukapoor commented on Oct 11, 2026

    @sonukapoor
    Collaborator

    Fixed on main in #1313, and the credit for both the report and the diagnosis is yours. Your table across both pin styles and two npm majors is the correct account of this: npm update only re-resolves the packages it is given, so naming the parent never refreshes the child, whether or not the parent itself moves. My own write-up had it narrower than that and was wrong.

    Closing this, with the remaining pieces of #1287 tracked there rather than here. Thank you for a report that was precise enough to act on without a reproduction step of my own.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions