Short answer: scan both package.json and package-lock.json. But when you need the most accurate vulnerability results for an installed npm project, give more weight to package-lock.json.

The reason is simple. package.json tells npm which dependency ranges your project accepts. package-lock.json records the exact dependency versions resolved for the project, including transitive dependencies. For security scanning, that difference matters.

What package.json tells you

package.json is the manifest of a Node.js project. It lists direct dependencies, dev dependencies, scripts, package metadata, and version ranges such as ^1.2.0 or ~2.4.1.

This file is useful for understanding what your project intends to use. It can show risky direct packages, outdated version ranges, and packages that should not be in production. But it does not always show the exact version that will be installed.

What package-lock.json tells you

package-lock.json records the resolved dependency tree. It includes the exact package versions npm selected during installation. It also includes nested dependencies that your direct dependencies bring into the project.

That makes the lockfile especially important for vulnerability scanning. A vulnerability may exist in a transitive package that never appears in package.json. If your scanner only checks the manifest, it may miss that vulnerable package.

So which file should you scan?

For the most accurate result, scan package-lock.json. It reflects the dependency tree npm actually resolved for the project.

Still, do not ignore package.json. It helps answer a different question: why is this dependency allowed in the first place? The lockfile shows what is installed. The manifest shows what your project permits.

A practical scanning rule

Use this approach in CI and code review:

  • Scan package-lock.json to find vulnerabilities in the resolved dependency tree.
  • Review package.json to understand direct dependencies and version ranges.
  • Fail builds on high and critical vulnerabilities unless there is an approved exception.
  • Keep the lockfile committed so scans are reproducible across local, CI, and production environments.
  • Regenerate the lockfile when dependency ranges change.

Common mistake: scanning only package.json

Scanning only package.json can produce incomplete results. Many vulnerabilities live inside transitive dependencies. These are packages your application uses indirectly through another package.

For example, your project may depend on a framework, and that framework may depend on a vulnerable utility package. The vulnerable utility package may not appear in package.json, but it can appear in package-lock.json.

Common mistake: trusting a stale lockfile

A lockfile is only useful when it matches the current project. If developers edit package.json without updating package-lock.json, scan results can become misleading.

Use npm ci in CI when possible. It expects the manifest and lockfile to stay in sync, which helps avoid accidental dependency drift.

Recommended workflow

  1. Commit both package.json and package-lock.json.
  2. Run vulnerability scans on pull requests.
  3. Prioritize results from the resolved dependency tree.
  4. Review direct dependency ranges before accepting automated fixes.
  5. Use lockfile updates as a review point, not as noise.

Final answer

Scan package-lock.json for the most accurate vulnerability picture, because it shows the resolved dependency tree. Scan package.json as well to review direct dependencies, allowed version ranges, and dependency intent.

In short: package-lock.json tells you what you are actually installing. package.json tells you what your project is allowed to install. A good vulnerability scanning workflow needs both.