Security basics
9 min
A vulnerable package can turn a well-built feature into an account takeover, data exposure or production outage. If you need to check npm dependencies for vulnerabilities, do more than run one command and clear every warning. You need to know what code is actually deployed, whether the issue is reachable, and whether the proposed fix creates a new problem.
For startup teams moving quickly with JavaScript, Next.js, React, Node.js and AI-generated code, dependency risk is easy to miss. A package may arrive through a template, an authentication library, a payment integration or a small utility added during a late-night build. It may be several layers below the package you chose. Your application still inherits the risk.
Why npm dependency findings deserve attention
npm applications rarely contain only the packages listed in package.json. Each direct dependency can install many transitive dependencies, which are packages required by other packages. A small application can therefore ship hundreds or thousands of packages through its lockfile.
That is not automatically a security failure. Most findings do not mean an attacker can immediately compromise your application. Severity ratings describe the potential impact of a known flaw, not the exact exposure in your environment. A high-severity vulnerability in a development-only tool used locally has a different operational risk from a medium-severity flaw in a production package that parses attacker-controlled uploads.
The mistake is treating both cases the same. Ignoring the whole report leaves genuine exposure unresolved. Applying every automatic upgrade without review can break authentication, payments, builds or database behaviour immediately before a release.
A useful review asks four questions: Is the vulnerable version in the deployed lockfile? Is the affected package loaded in production? Can an attacker reach the vulnerable code path? Is there a safe upgrade, configuration change or removal available?
How to check npm dependencies for vulnerabilities
Start from the repository and environment that actually builds your live application. package.json declares your intended dependencies, but package-lock.json records the versions installed. The lockfile is usually the more useful security artefact because it shows the exact dependency tree your build uses.
Run the following from the project root:
This checks installed package versions against npm's vulnerability advisories and reports affected packages, severity and available remediations. For a production-focused view, use:
This matters when you want to assess what reaches a deployed Node.js service. It does not mean development dependencies are irrelevant. A compromised build tool, test runner or code-generation package can affect your build pipeline, steal environment variables or alter the artefacts you deploy. It simply helps you separate immediate runtime exposure from development and supply-chain risk.
Next, inspect why a package exists:
Replace package-name with the affected dependency. The output shows whether it is direct or transitive and which package introduced it. That detail changes the fix. If your own package.json directly requests an outdated package, you can usually upgrade or replace it. If the package sits four levels down the tree, you may need to update the parent dependency, apply an npm override or wait for a maintained upstream release.
Do not review the audit output in isolation. Check the deployed branch, your CI build settings and any separate frontend and backend repositories. It is common to fix a lockfile locally while a deployment platform builds another branch, uses cached dependencies or deploys a different service altogether.
Check the lockfile is committed and reproducible
A committed lockfile makes dependency review repeatable. Without one, two installations can resolve different versions and produce different results. In CI, use npm ci rather than npm install for predictable installs based on the lockfile.
If npm install changes your lockfile during a security fix, read the diff. A seemingly small upgrade can alter dozens of transitive packages. That may be acceptable, but it should be intentional and tested.
Triage findings by real application risk
An npm audit report gives you a starting point, not a remediation plan. Prioritise findings based on how your product handles data and who can trigger the affected code.
A critical issue in a package used to process public file uploads, render user-controlled markdown or parse request bodies should be investigated quickly. The same applies to dependencies involved in session handling, JWT validation, cryptography, authorisation or server-side requests. These are paths where a package flaw may become customer data exposure or account compromise.
A finding in a front-end-only package can still matter. Client bundles are visible to users, and vulnerabilities such as cross-site scripting can expose sessions or trigger actions as a signed-in user. The practical question is whether the affected version and function are actually included in the browser bundle.
Development tooling needs a different lens. A low-severity advisory in a local linting tool may not block a release. A package with install scripts that runs in CI and has access to deployment secrets deserves more scrutiny, even if it is not included in your production image.
When reviewing each result, record the affected version, dependency path, environment, exploit condition, recommended fixed version and owner. If a finding is not actionable, write down why: for example, the vulnerable function is not imported, the package is excluded from production, or a compensating control prevents the attack path. That decision should be revisited when the application changes.
Fix safely rather than blindly running auto-fix
npm audit fix can apply compatible updates automatically. It is useful for straightforward patch and minor updates, particularly in a branch with a test suite behind it. But it is not a substitute for review.
npm audit fix --force may install major versions outside your declared compatibility range. That can remove a reported advisory while introducing a broken API, altered security defaults or an outage. Do not run it against a production branch and assume the green audit result means the application is safe.
A safer path is to update the direct dependency deliberately, regenerate the lockfile, run tests, build the production artefact and exercise the affected feature. For a web application, test sign-up, sign-in, password reset, role-restricted API calls, checkout and any upload or parsing flow touched by the dependency. If the change touches your database client, verify Supabase row-level security still behaves as expected for both authorised and unauthorised users.
Where the issue is transitive, npm overrides can pin a safe version across the tree. This can be an effective short-term control, but only when the newer transitive package remains compatible with its parent. Treat overrides as a tracked exception, not a permanent fix. Remove them once the upstream maintainer releases a compatible update.
If no fix exists, the answer depends on reachability. You may be able to remove the feature, disable a risky parser, reject the dangerous input type, isolate the service or replace the dependency. Document the temporary control and set a date to reassess it. "No fix available" is a reason to make an engineering decision, not a reason to close the finding.
Build dependency checks into the release process
Dependency security works best as a routine release control rather than a cleanup task after launch. Run audits when dependencies change, on pull requests and on a scheduled basis. Newly disclosed vulnerabilities can affect a lockfile that has not changed in months.
Automated checks should fail or alert based on rules your team can maintain. Blocking every low-severity development finding often leads to alert fatigue and ignored reports. A more useful policy is to require review for high and critical issues, flag medium findings for triage, and create explicit exceptions with an owner and expiry date.
Also check what scanning cannot prove. npm audit will not tell you whether an API key is committed in a repository, whether an endpoint has an IDOR flaw, whether JWTs are validated correctly, or whether your security headers allow risky browser behaviour. Dependency scanning is one layer of an application review, alongside code, configuration, authentication, database permissions and the live attack surface.
AI-assisted development makes this broader check especially worthwhile. Generated code often assembles familiar packages correctly enough to work, but may add outdated examples, unnecessary libraries, unsafe defaults or server-side packages into client-facing paths. A clean build is not evidence that those choices are safe.
What a useful dependency finding looks like
A finding should lead to a decision someone can implement. "High severity package vulnerability" is not enough. A useful record identifies the repository, branch, file path, package chain, deployed service, realistic impact and exact next step.
For example, a report might state that package-lock.json resolves a vulnerable parser through a direct upload library, that the upload endpoint accepts unauthenticated files, and that upgrading the parent library to a specified version removes the vulnerable path. It should also say which upload tests to run and whether the fix needs a release before the next customer import.
That level of detail keeps a founder from guessing whether the issue is theoretical or urgent. It also gives an engineer a bounded task rather than an open-ended security investigation.
If your team has inherited a fast-moving repository, start with the deployed lockfile, identify the findings that touch public input and authentication paths, and make each remediation testable. When the dependency report is unclear or sits alongside exposed credentials, weak RLS policies or broken access controls, an independent review can turn a noisy scan into a short, prioritised repair plan before a small issue becomes a customer incident.
Back to blog
