This is part 7 of the series "Protect Your Own Service Yourself — Self-Defense Security in the Age of AI." Do the checks you can yourself; when it's hard, EarthLink Network can run the assessment for you, and fix the gaps you can't fix on your own.
Series contents
- Anyone can attack now, with AI — check your own site first
- What attackers now hold: when AI runs the attack itself
- Before you start: the whole picture of checking, and how to test without breaking production
- Testing from the outside (1): setup and the first run
- Testing from the outside (2): attack tools and legitimate scanners find different things
- Checking the code you wrote: static analysis
- Checking the parts you depend on: libraries and packages (this article)
- Checking your infrastructure settings for gaps (coming soon)
- Collect every result in one place and decide what to fix first (coming soon)
- How to fix what you found: three ways (coming soon)
- When you cannot fix it yourself: a maintenance contract (coming soon)
- Keep checking automatically every day — from building to protecting (coming soon)
The short version
Of the 69 known flaws npm audit found in the libraries our main app depends on (counted as distinct advisories), 50 were published on or after September 1, 2026. Sixteen were published in the seven days before we ran the check. (Publication dates are the dates in GitHub's advisory database.) We hadn't changed a line of code. The flaws arrived anyway.
That is the nature of dependency checking, and it is why it produces more findings than any other kind of security check. We checked a copy of the latest code of our own service, promptflow, with two free tools. The main app's lockfile lists 1,931 packages. npm audit reported 50 vulnerable packages, counting packages that are flagged only because they contain a vulnerable one: 4 critical, 39 high, 7 moderate. Trivy reported 62 findings in the same lockfile, 5 of them critical. On the same day, our daily automated scan found 2 issues in the code we wrote ourselves and 2 possible secrets left in the repository. The dependency numbers are in a different league.
The five critical findings include a flaw in Next.js described as "unauthenticated remote code execution": under certain conditions, such as a server running on Windows, someone who is not logged in could run commands on the server. A flaw like this is the top priority: confirm whether your setup is affected, and move to a fixed version. We give the counts and the names of the flaws here without hiding them, but we don't describe how to exploit them.
This post covers three things, with the real commands and real screens: how to find these flaws (three ways), how to count them (why the tools disagree), and what to look at first. How to fix them is the topic of Part 10.
Your service is mostly other people's code
Almost every modern service is assembled from ready-made libraries: one for rendering pages, one for talking to the database, one for login. Each is written and published by someone else, somewhere in the world.
Those libraries depend on more libraries. We picked over a hundred directly, but counting everything they pull in, the main app's lockfile (package-lock.json) lists 1,931 packages. Of those, 770 run in production and 928 are used only during development (npm's categories overlap, so they don't add up to the total). The phone app has its own lockfile with another 1,111.
The bugs in code you wrote live in the lines you wrote. The flaws in your dependencies can be anywhere in 1,931 packages you have never read. That is why this check is the one with the biggest numbers.
Look at what you depend on, with the same eyes
Dependency flaws have two properties that your own bugs don't.
First, they show up later. People and researchers around the world keep examining popular libraries, and every flaw they find gets published. A package that is safe today can be dangerous tomorrow, without you touching anything. The critical flaw in the Sentry SDK (the error-reporting library) that showed up in this check was published just before the check. A day earlier, it would not have appeared. That is why a dependency check is never a one-time job.
Second, protecting yourself is not enough. The people who publish libraries can be attacked, and the library itself can be turned into the attack. This is a supply chain attack.
In September 2025, CISA warned that a self-replicating worm had compromised more than 500 npm packages. Environments that installed them were searched for credentials, which were then stolen. However carefully you write your own code, if the author of one of your libraries is compromised, that is a way in.
Libraries are also shared. In this check, the Next.js flaw showed up in both our admin app and our user-facing app (see packages/admin-app and packages/user-app in the npm audit screen below). One flaw in a shared library lands in every app that uses it.
So the check cannot stop at your own code. It has to reach what you depend on. No person can review 1,931 packages by hand, though. This is work for tools. This post covers the packages listed in lockfiles; this copy of the code has no Python lockfile.
Method 1: npm audit
npm audit ships with npm, so there is nothing extra to install, and it is free.
npm audit only checks JavaScript packages. Every lockfile in promptflow is a JavaScript one. We checked the npm lockfiles with npm audit and the pnpm one with Trivy (next section). A service written in PHP, Python, or another language needs a different tool; see the table in "Each language has its own tool" below.
We ran everything against a copy of the promptflow source. Nothing was sent to the production servers. In the folder that holds the lockfile:
npm audit --package-lock-only --package-lock-only tells npm to look only at the lockfile. It doesn't download or install anything, so it works even if you haven't installed the packages, and none of the packages' code runs. npm audit matches the packages and versions in the lockfile against GitHub's public advisory database and lists every match.
Each entry tells you, in order:
- The package and the range of versions with the flaw (
next 9.3.4-canary.0 - 16.4.0) - The severity: critical, high, moderate, or low
- The advisory title and a link (
https://github.com/advisories/...) - A hint about the fix
- Where the package sits in your tree
The last line is the total: "50 vulnerabilities (7 moderate, 39 high, 4 critical)." That means 50 packages have a known flaw. The four critical ones were Next.js, Sentry's Next.js SDK, shell-quote (a small library for building shell commands), and Storybook (a tool for previewing UI components during development).
The fix hint is worth reading closely:
-
fix available via npm audit fixmeans a fixed version exists and you can likely move to it without changing how you use the package. -
fix available via npm audit fix --force, followed by "Will install ..., which is a breaking change," means the fix requires a major version upgrade that may break your code. - If there is no fixed version yet, npm audit says so. In our phone app, 5 of the 59 vulnerable packages were in that state.
In the main app, every one of the 50 had a fixed version, but 28 of them need a major upgrade. The npm audit fix --force suggested at the bottom upgrades everything, breaking changes included. Running it blindly can break your app. Part 10 covers how we actually upgrade.
Method 2: Trivy
Trivy is a free, open-source scanner published by Aqua Security.
The difference from npm audit: Trivy searches the whole folder for lockfiles and checks them all at once, and not only JavaScript. Python, Go, and other ecosystems are covered too. The official docs list the install options; on a Mac, Homebrew works. We used version 0.74.0, with its vulnerability database last updated the day before the check.
On the same copy:
trivy fs --scanners vuln . fs scans a folder, --scanners vuln limits the scan to dependency flaws (Trivy can also look for secrets and misconfigurations), and . is the current folder.
The summary at the top shows one row per lockfile Trivy found: the main app (package-lock.json) with 62, the phone app (mobile-app/package-lock.json) with 41, and a UI prototype (mobile/pnpm-lock.yaml) with 0.
For the main app: 5 critical, 37 high, 19 medium, 1 low. The table columns:
| Column | Meaning |
|---|---|
| Library | The package |
| Vulnerability | The flaw's global ID (starting with CVE- or GHSA-) |
| Severity | How serious it is |
| Status | "fixed" means a fixed version exists; "affected" means not yet (it can be blank, so check Fixed Version) |
| Installed Version | What you have now |
| Fixed Version | Where the flaw is fixed |
| Title | What the flaw is |
Sixty of the 62 findings in the main app had a fixed version listed. Nearly everything was in the state "the fix is out, and we haven't upgraded yet." npm audit said none of its 50 lacked a fix; the two tools count differently (see "Counting" below), so these numbers don't line up.
Each language has its own tool
npm audit sees only JavaScript packages installed with npm. Run it on a PHP, Python, or Ruby project and it checks nothing. Each language has its own package manager, and the checkers follow the same split.
Here are the main languages and a free checker for each:
| Language | Lockfile | Free checker | Example command |
|---|---|---|---|
| JavaScript / TypeScript | package-lock.json, pnpm-lock.yaml | npm audit, pnpm audit | npm audit |
| PHP | composer.lock | composer audit (built into Composer 2.4+) | composer audit |
| Python | requirements.txt | pip-audit | pip-audit -r requirements.txt |
| Ruby | Gemfile.lock | bundler-audit | bundle-audit check --update |
| Go | go.mod | govulncheck | govulncheck ./... |
| Rust | Cargo.lock | cargo-audit | cargo audit |
| Java | pom.xml, build.gradle | OWASP Dependency-Check | dependency-check --scan . |
| C# (.NET) | .csproj | built into the .NET CLI | dotnet list package --vulnerable |
Two tools work across languages: Trivy (previous section) and Dependabot ("Method 3: Dependabot" below). If your code mixes languages, running Trivy on the whole folder is a quick way to see which lockfiles you have. It can't read every format, though: of the files in the table, Trivy doesn't read build.gradle (Java) or .csproj (C#), so use the language's own tool for those.
Flaws in the language itself: Node.js and PHP in October
Some flaws never show up in a dependency check, because they are in the language runtime itself.
Two days before our check, on the same day, two runtimes disclosed serious flaws:
- Node.js (the runtime that runs JavaScript on the server): an app that accepts Brotli-compressed data could let a remote attacker run code on the server (CVE-2026-111497). The same release fixed nine other flaws. The fixed versions are 22.23.4, 24.22.1, and 26.12.1. Node.js 20 and older are past end of life and get no fix.
- PHP: two flaws that let an unauthenticated remote attacker run code on the server (CVE-2026-109618 and CVE-2026-109619). The details leaked the day before the fixed versions came out. The fixed versions are 8.5.12, 8.4.27, 8.3.36, and 8.2.35.
After the disclosures, attacks were observed against both runtimes, and CISA, the US government agency, added the Node.js flaw and both PHP flaws to its catalog of known exploited vulnerabilities. With so many services running on Node.js, and WordPress and so many other sites running on PHP, both made news around the world.
You could run npm audit every day and never see the Node.js flaw. npm audit checks the packages in package-lock.json, and Node.js itself is not in package-lock.json. The same goes for PHP and composer audit.
You have to catch runtime flaws somewhere other than your dependency check. Here is where to look:
| What to check | Node.js | PHP |
|---|---|---|
| The version running | node -v | php -v. The PHP serving your web pages (php-fpm, for example) can be a different version, so also run php-fpm -v (on Ubuntu, php-fpm8.3 -v or similar) |
| Whether that version has known flaws | npx is-my-node-vulnerable (an official tool from the Node.js team; it lists the flaws if your version has any) | Compare with the fixed versions in the official announcement |
| New security announcements | The "Vulnerabilities" posts on the official Node.js blog, and the announcement-only mailing list nodejs-sec | php.net news, and the announcement-only mailing list php-announce |
| Whether your version is still supported | The "Releases" page on the Node.js site | The "Supported Versions" page on php.net |
npx is-my-node-vulnerable checks the Node.js it runs on. Run it with the same version as production, not just on your laptop. Add it as one line in your daily automated scan, and you'll catch the next one even if you miss the announcement.
Where to look for the version depends on where you run:
- On AWS Lambda or a similar cloud service, the "Runtime" in the function settings (nodejs22.x, for example) tells you the Node.js release line. By default, AWS applies patch versions such as 22.23.4 for you. AWS sends notice ahead of time before a runtime reaches end of support.
- If you use PHP or Node.js packaged by your OS, such as Ubuntu, the fix may arrive without a version number change, so check your OS's security notices.
- If someone else hosts your server, check your host's notices to see whether they have moved to a fixed version.
Method 3: Dependabot
The third method is GitHub's built-in Dependabot. Here I'll show our real settings and their real state.
Dependabot has three separate features with similar names:
- Version updates check for new versions on a schedule you set and open pull requests to upgrade.
- Dependabot alerts notify you in GitHub when a flaw is published in a package you use.
- Security updates open a pull request to the fixed version as soon as a flaw is published, without waiting for the schedule.
All four version update entries are interval: "monthly". Once a month, not daily.
The file also carries a comment (in Japanese, so not shown in this English screenshot) saying that security updates will open pull requests immediately, regardless of the schedule. But when we asked GitHub for the repository's alerts at the time of the check, the answer was: "Dependabot alerts are disabled for this repository."
GitHub's docs say security updates only work in repositories where the dependency graph (the list of packages GitHub builds from your lockfiles) and Dependabot alerts are enabled. With alerts off, the "immediate pull request" the comment promises was not happening in our repository at the time of the check. What the config file says and what actually runs were two different things.
There is a second gap. The only npm directory listed is "/", the main app. At the time of the check, the phone app's folder (mobile-app), where Trivy found 41 findings, was not listed, so Dependabot was not updating it either.
In September and October the grouped updates arrived on the 1st. But both npm pull requests, 58 packages in September and 69 in October, were closed without merging. Why, and how we split them up instead, is in Part 10.
Alerts themselves don't require a paid plan.
The dependency graph and Dependabot alerts are included in all plans, and you turn them on from the repository's settings. If you haven't, it is the cheapest first step you can take.
Counting: why the tools disagree
For the same lockfile, npm audit said 50 and Trivy said 62. Neither is wrong. They count different things.
| npm audit | Trivy | Dependabot alerts | |
|---|---|---|---|
| What it counts | Vulnerable packages | Package version × flaw pairs | Alerts (disabled at the time) |
| Main app | 50 (4 critical, 39 high, 7 moderate) | 62 (5 critical, 37 high, 19 medium, 1 low) | None, because alerts were disabled |
| Development-only packages | Counted | Not counted by default | — |
| Which lockfiles | The one in the folder you run it in | Every lockfile in the folder | Whatever the config lists |
| Advisory data | Queried live when you run it | A database downloaded in advance | — |
Four differences matter.
The unit. npm audit counts packages, and a package counts even if its only problem is that it contains a vulnerable package. Of our 50, 25 had an advisory of their own; the other 25 were counted because of what they include. Trivy counts one row per package version per flaw. If the same package is installed at two versions, that is two rows. Trivy's 5 critical findings are Next.js installed at two versions (15.5.21 and 16.3.0), and only 3 distinct flaws.
Development packages. By default Trivy counts only packages that run in production. With --include-dev-deps, the main app went from 62 to 78, and the UI prototype from 0 to 31. Storybook, which npm audit flagged as critical, is a development tool, so it is not in Trivy's default count.
Which lockfiles. npm audit checks one, Trivy checks every one it finds, and Dependabot checks only what you configured.
Freshness. npm audit asks the registry at run time. Trivy uses a database downloaded beforehand. The database Trivy used was updated about nine hours before the critical Sentry advisory was published. Trivy's result does not include it. On the same day, one tool can see yesterday's flaw and the other can't.
Counting npm audit's results as distinct advisories, the main app had 69, and 90 including the phone app. Counting Trivy's 62 findings the same way gives 46, so even this number depends on the tool.
So a number only means something together with the tool and the options that produced it. To compare with last time, use the same tool with the same options. If the count drops after you switch tools, that doesn't mean you have fewer flaws. Pulling results from several tools into one place is the topic of Part 9.
What to check for each finding
There are too many findings to go top to bottom. For each dependency flaw, we check the following. How to rank these against the results of other checks and decide what to fix first is Part 9.
- Severity. Critical and high first.
- Production or development. Flaws in development-only packages don't run on your servers or your users' devices, so they can often wait. Not forever, though: developers' machines are targets too.
- Is there a fixed version? If yes, upgrading fixes it. If not, you need a different kind of mitigation (Part 10).
- The conditions. Advisory titles and pages often state when you are affected. The Next.js titles here mention "windows-hosted servers," "when AVIF files are used," and "next/og ImageResponse." Check each condition against how you actually run the app.
- Days since publication. Attackers try newly published flaws quickly (Part 2). Every day a published flaw stays unfixed is another day it can be tried against you.
Applying that to the five critical findings (three distinct flaws) in the main app (from Trivy and the GitHub advisory database):
| Package and installed version | Flaw (summary) | Fixed in | Published to check |
|---|---|---|---|
| Next.js 15.5.21 / 16.3.0 | Unauthenticated remote code execution on Windows-hosted servers (CVE-2026-75604) | 15.5.24, 16.3.3 | 45 days |
| Next.js 15.5.21 / 16.3.0 | Unauthenticated remote code execution in the Image Optimization API when AVIF files are used | 15.5.24, 16.3.3 | 45 days |
| Next.js 16.3.0 | Remote code execution in next/og ImageResponse | 16.3.6 | 23 days |
All three have a fixed version, and the list includes the Next.js version (15.5.21) in the app we serve in production. All three had been public for more than three weeks when we ran the check. We upgraded to the fixed versions the same day (Next.js 15.5.28 and 16.4.2, the Sentry SDK 10.76.2).
Why was a critical flaw still in the latest code? None of our three safeguards was built to catch this case.
- Our pull request check only fails on critical or high flaws that the pull request itself introduces. Its config file says so: existing flaws on the base branch don't block. When a flaw is published in a package you already have, no pull request introduces it.
- Dependabot alerts were off, so neither the notification at publication time nor the immediate fix pull request happened.
- The monthly grouped upgrades were closed unmerged two months in a row.
After this check, we turned Dependabot alerts on the same night. We now review incoming alerts every day, for both the main app and the mobile app.
In Part 3 we said we would check, in this part, where the alerts that arrived that night overlap with Trivy's results. For the main app's lockfile, Dependabot sent alerts for 52 distinct flaws that night. Trivy found 46 in this check. 37 were the same flaws. The differences:
- Of the 15 flaws only Dependabot reported, 13 were in development-only packages (vite, vitest, Storybook and others). By default Trivy counts only packages that run in production. The other 2 were in production packages (pdfjs-dist and ip-address) and did not appear in Trivy's results.
- All 9 flaws only Trivy reported were in Next.js. We had upgraded it to a fixed version about two hours before the alerts arrived.
For the mobile app's lockfile (mobile-app), 30 of 32 flaws were the same. For the UI prototype's lockfile (mobile), Dependabot reported 32 and Trivy reported none. All 32 were in development-only packages.
Even on the same lockfile, the two tools count different things. That is why we run both: Trivy every day, and Dependabot alerts.
Our daily automated scan (Part 12) collects Trivy's results in one place every day. In that collection point (DefectDojo, covered in Part 9), we counted promptflow's findings by tool on the evening of the check.
| Tool (what it checks) | Open (by severity) | Closed |
|---|---|---|
| Trivy (dependencies) | 98 (6 Critical, 64 High, 27 Medium, 1 Low) | 160 |
| npm audit (dependencies) | 46 (2 Critical, 37 High, 7 Medium) | 60 |
| Gitleaks (secrets) | 2 (2 High) | 22 |
| Semgrep (your own code) | 2 (2 Medium) | 8 |
Trivy is the largest source: 98 of the 148 findings in promptflow that were still open at that time. With npm audit, the two dependency scanners account for 144. Across all of our services, Trivy reported 673 and npm audit 228 of the 1,036 findings that were open at that time. Among the four tools feeding the collection point, dependency flaws are the biggest group by count, and now we have the numbers to show it. Trivy and npm audit sometimes report the same flaw separately, though, so adding the two does not give the number of distinct flaws. The collection point's 98 Trivy findings come from the daily scan, and they differ from the 103 we counted locally in this post (62 for the main app, 41 for the mobile app) in both timing and the version of the advisory database used.
Collecting them doesn't fix anything. Someone has to look, decide what goes first, and own it. That is Part 9. Installing the tools isn't the end of dependency checking. It protects you only once someone owns the results and the order they get fixed in.
Fixing comes in Part 10
Most dependency flaws are fixed by moving to the fixed version. But as the screens above show, some need a major upgrade, and upgrading everything at once can break the build. Breaking your own app in the name of security defeats the purpose.
Part 10 shows, with real screens, how in July we upgraded packages from Trivy's results and brought the fixable count from 73 down to 62, and why we closed Dependabot's 69-package pull request.
Next
Part 8 checks infrastructure settings: the deployment steps and file storage, looking for anything an outsider can get into or see, and shows how to check the rest of the cloud setup, servers and databases included, with read-only access. Even with no flaws in your code or your libraries, one setting can expose your data.
In this series, in an age when attack tools are free and one click away for anyone, I show step by step how to check your own service yourself and close the gaps before someone else finds them. The target is always our own service; the series does not cover attacking anyone else's site.
If running the checks yourself is hard, EarthLink Network can do the assessment for you. Found a gap you can't fix? We'll handle the fix. Feel free to reach out.
If this was useful, please like and follow.
Our products are listed at https://www.eln.ne.jp/products
EarthLink Network builds its own products to run the whole company on AI — each one born from a need along the way. See the full index of what we build:
→ The 18 products EarthLink Network builds in-house
For more about the company and each product, visit www.eln.ne.jp.








![Robot Hand and Dexterous Grasping — Ch.09: Force and Torque Control for Hands [EN]](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyh837tb5vp99ae1scr4c.png)
![Robot Hand and Dexterous Grasping — Ch.16: Checklists and Templates [EN]](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fowhozp6neqiwwv1y561k.png)