Safeguard's software composition analysis (SCA, generally available) reads your package.json together with package-lock.json, yarn.lock (Yarn Classic and Yarn Berry) or pnpm-lock.yaml, resolves the full transitive tree, and matches every resolved package and version against vulnerability advisories. Findings are ranked by reachability (static and dynamic reachability are both GA for JavaScript and TypeScript in Safeguard's docs), EPSS and CISA KEV, and Griffin can open a pull request that moves the vulnerable package to a fixed version. Autonomous remediation ships switched off, so every fix PR starts with a human decision.
This post covers which files Safeguard reads, how it handles deep npm trees, what reachability means for CommonJS and ESM code, how to run it locally and in CI, how fix PRs treat lockfiles, and where your own process takes over.
Key takeaways
- SCA (GA) covers npm, Yarn and pnpm. The CLI support matrix lists
package-lock.json,yarn.lockfor Yarn Classic and Yarn Berry,pnpm-lock.yaml, andbun.lockbfor Bun, each withpackage.jsonas the manifest. - Deep dependency scanning (GA) resolves workspaces, peer dependencies and optional dependencies, and Safeguard's docs describe resolution up to 100 levels deep, with the full path from your root to each vulnerable package.
- Reachability analysis (GA): Safeguard's docs list static and dynamic reachability for JavaScript and TypeScript as GA, including CommonJS, ESM and webpack bundles. Calls the analyser cannot decide, such as
evalor dynamic dispatch, are marked unknown and are not suppressed. - Vulnerability management (GA) enriches every finding with CVSS, EPSS and CISA KEV status and rolls them into a 0 to 100 priority score you can filter on.
- Autonomous remediation (GA, ships off): Griffin fix pull requests for npm, Yarn and pnpm, single or bulk, with lockfile updates and a breaking change rating. Every category defaults to Manual.
- License compliance (GA) reads the
licensefield inpackage.json, matches LICENSE and NOTICE files, and flags disagreements between them. - CLI, local runner and CI (GA):
safeguard scan --dir .on a laptop,safeguard ci scanin a pipeline, with--localwhen source must stay on your runner. - Policies and gates (GA) decide whether a build fails. The CLI itself reports; the platform enforces.
The problem: JavaScript dependency trees are deep, wide and move every day
Most JavaScript and TypeScript applications ship far more third party code than first party code. A service with a few dozen direct dependencies routinely installs hundreds or thousands of packages once the transitive tree is resolved, and each of those packages has its own release cadence, maintainers and advisories. Three properties of the ecosystem make this harder than it looks.
The first is that the tree you declare is not the tree you install. package.json holds version ranges. The lockfile records what the package manager actually resolved, and different package managers write different lockfiles: npm writes package-lock.json, Yarn writes yarn.lock in two different formats depending on whether you run Classic or Berry, pnpm writes pnpm-lock.yaml, and Bun now writes a text bun.lock. npm's own documentation notes that it also keeps a hidden lockfile inside node_modules/.package-lock.json, which is a cache for the installed tree and not something you commit. A scanner that reads only the manifest is guessing; a scanner that reads the lockfile is reporting.
The second is depth. A vulnerable function is rarely in the package you chose. It sits in a utility three, five or eight levels down, pulled in by a framework plugin, a build tool or a test runner. Fixing it often means either waiting for each intermediate maintainer to bump their range, or forcing a version from the root with npm overrides, Yarn resolutions or pnpm overrides. Each of those has rules. npm's documentation says overrides are only considered in the root package.json. Yarn's says resolutions can only be set at the project root. pnpm's settings now live in pnpm-workspace.yaml, and its docs say resolution settings apply per project only when each project has its own lockfile.
The third is volume. A typical JavaScript project produces a long list of advisories, and many of them are in code paths the application never calls, in development only packages that never reach production, or in packages with no realistic exploitation. Teams that treat every advisory the same way burn their time on the wrong ones and then miss the few that matter.
What standards and regulators expect
None of this is optional for teams that sell to regulated buyers or the public sector. The requirements are specific.
- NIST SP 800-218, the Secure Software Development Framework (SSDF) version 1.1, published February 2022, has task PW.4.4: verify that acquired commercial, open source and other third party components comply with your requirements throughout their life cycles. Its implementation examples include regularly checking for publicly known vulnerabilities and building automatic detection of known vulnerabilities into the toolchain. Task RV.1.1 asks producers to gather information on potential vulnerabilities in the software and the third party components it uses, including by automatically reviewing software composition data. Task RV.2.2 asks for a risk based decision on each vulnerability and a plan for temporary mitigation when no permanent fix exists. Task PS.3.2 asks producers to maintain and share provenance data for all components of each release, for example in an SBOM.
- OWASP Top 10 2025, A03 Software Supply Chain Failures, is the successor to the old "Using Components with Known Vulnerabilities" category. OWASP maps it to CWE-1395 (dependency on a vulnerable third party component), CWE-1104 (use of unmaintained third party components) and related weaknesses, and notes it had the highest average incidence rate in the contributed data.
- CISA's Known Exploited Vulnerabilities catalog is, in CISA's words, the authoritative source of vulnerabilities exploited in the wild, and CISA tells organisations to use it as an input to vulnerability prioritisation. For federal agencies, BOD 26-04, Prioritizing Security Updates Based on Risk (10 June 2026), now sets remediation timelines in its Table 1 from four factors: asset exposure, KEV status, exploit automation and technical impact. BOD 26-04 revoked BOD 22-01, whose fixed KEV deadlines are history, not current guidance.
- FIRST's Exploit Prediction Scoring System (EPSS) publishes, every day, a probability that a published CVE will be exploited in the wild in the next 30 days. It is designed to sit next to severity, not replace it.
The common thread is simple: know exactly which components you ship, find known vulnerabilities in them continuously, rank by real risk, and fix with a record of what you decided. The rest of this post shows how Safeguard does each of those for npm, Yarn and pnpm projects.
Which JavaScript and TypeScript files Safeguard reads
Safeguard's SCA page lists "npm / pnpm / Yarn" first among supported ecosystems and says manifests and lockfiles are both honoured so that resolution matches what actually installs. The CLI support matrix is the precise reference. For JavaScript and TypeScript it lists:
| Package manager | Manifest | Lockfile | Status in Safeguard's support matrix |
|---|---|---|---|
| npm | package.json | package-lock.json | Full support |
| Yarn Classic (v1) | package.json | yarn.lock | Full support |
| Yarn Berry (v2 and later) | package.json | yarn.lock | Full support |
| pnpm | package.json | pnpm-lock.yaml | Full support |
| Bun | package.json | bun.lockb | Full support |
A few practical notes follow from that table and from the docs around it.
TypeScript is covered through the same manifests. There is no separate TypeScript package manager. Your @types/* packages, your compiler and your bundler are ordinary npm packages in package.json, so they show up in the inventory like everything else. Whether they show up as production or development dependencies depends on how you declared them and on the dev dependency setting covered below.
Bun's lockfile format changed. Bun's documentation says Bun v1.2 changed the default lockfile to the text based bun.lock, and it gives a command to migrate an old binary bun.lockb. Safeguard's support matrix lists bun.lockb. If your Bun projects have moved to bun.lock, check the current support matrix or run a test scan before you rely on lockfile precision for those repositories.
Uploads work too. On the Integrations page in the web app, the File Uploads group includes Upload Manifest File, for teams that want to hand Safeguard a package.json without connecting a repository. A manifest alone carries version ranges, not resolved versions, so treat that route as a quick check rather than your system of record.
Development dependencies are a choice. The SBOM generation options for a connected project include Include Dev Dependencies. For JavaScript this decision matters more than in most ecosystems, because the development tree (test runners, bundlers, linters, type packages) is often larger than the runtime tree. Many teams include it for internal scans and exclude it from the SBOM they hand to customers.
How Safeguard resolves transitive depth
Safeguard's deep dependency scanning (GA) is the part of SCA that walks the resolved graph. The docs describe it this way for JavaScript: npm, pnpm and Yarn resolution covers workspaces, peer dependencies and optional dependencies, and the walk continues up to level 100. At each node Safeguard resolves the package URL and version against its vulnerability data, computes license and provenance information, feeds the node into reachability analysis, and records the full path from root to leaf.
The docs add that shared subgraphs are resolved once and rescoring is driven by advisory feed updates, which keeps deep scans of large monorepos practical and lets continuous scanning re-evaluate projects daily.
Reading a transitive path
Click a finding in the web app and Safeguard shows the chain of packages that pulls the vulnerable one in, whether that chain is reachable, whether another path to the same package exists, and what the safe version would change elsewhere in the tree. The layout looks like this (package names below are placeholders, not a real advisory):
your-app
└─ @your-org/ui-kit @3.4.0
└─ example-markdown-lib @2.1.0
└─ example-sanitizer @1.0.7 ← vulnerable version
Why this matters for npm specifically: if two different paths lead to example-sanitizer, one at a fixed version and one at a vulnerable version, npm will happily install both. A path view shows you which parent is holding the old version back, and whether you can fix it by bumping that parent or need a root level override.
Depth from the CLI
The CLI can resolve transitive dependencies for a local scan and lets you choose the depth. From the docs:
safeguard scan --dir . --resolve-transitive --transitive-depth 100
--resolve-transitive resolves transitive dependencies through an external dependency graph service, which is useful when a lockfile is missing or incomplete. When you have a committed lockfile, the lockfile is the better source of truth, because it is what your package manager will install.
The same safeguard scan command also takes --org-namespace, which tells the scanner which namespaces belong to you. For JavaScript that is usually your npm scope:
safeguard scan --dir . --org-namespace @your-org
This keeps your own private packages identified as yours in the results rather than mixed in with public open source components.
Reachability for JavaScript and TypeScript
Reachability answers the question every developer asks about an advisory: does my code actually call the vulnerable function? Safeguard's reachability analysis (GA) combines three kinds of evidence:
- Static reachability builds a call graph from your source, lockfile and bytecode and checks whether any path leads to the vulnerable sink.
- Dynamic reachability uses runtime data about which modules and functions are actually loaded and invoked in running workloads, and marks never executed vulnerable paths as cold.
- Configuration reachability parses framework configuration to see whether code is wired in at all. The docs use Next.js route files as one of their examples, which is directly relevant to TypeScript web applications.
The docs' language support table lists JavaScript / TypeScript: static GA, dynamic GA, with the note that it handles CommonJS, ESM and webpack bundles. That matters because a JavaScript codebase often mixes require and import, and because what reaches production is frequently a bundle rather than the source tree.
Each finding gets one of four verdicts:
| Verdict | What it means | How to treat it |
|---|---|---|
| Reachable | A real call path exists and is instantiated at runtime | Fix first, in priority order |
| Conditionally reachable | A path exists but needs a feature flag, route or configuration that is not enabled now | Fix soon; a config change can make it live |
| Unreachable | No call path exists in your compiled code | Lower priority; still fix on your normal upgrade cycle |
| Unknown | The analyser could not decide, for example dynamic dispatch, reflection or native code | Treated conservatively as potentially reachable |
JavaScript is a dynamic language, and the docs are frank about where reachability is weakest: code executed through reflection, eval or unsafe dispatch, vulnerabilities that are driven by input data rather than by a specific call, and code paths that only run under configurations the analyser cannot enumerate. In JavaScript terms, that includes computed require() paths, plugin loaders that pick modules by name at runtime, and template engines that compile user supplied strings. Safeguard marks those cases unknown and does not suppress them. For those findings, lean on the other signals: KEV, EPSS and runtime observation.
One practical point about the dynamic half. Dynamic reachability depends on runtime data from your running workloads. A scan of a repository by itself can only use static and configuration evidence. If dynamic reachability is part of your evaluation, ask for it to be scoped in your demo so you see it working on your own deployment shape.
For a deeper look at how reachability, EPSS and KEV combine across all languages, see Prioritise vulnerabilities with reachability, EPSS and KEV in Safeguard.
EPSS and KEV prioritisation
Every SCA finding in Safeguard carries the vulnerable component and version, the CVE or GHSA identifiers, severity with EPSS and KEV context, the full transitive path, the reachability verdict where available, and the safe upgrade target. Safeguard's vulnerability data comes from the National Vulnerability Database, GitHub Security Advisories, OSV, CISA KEV, vendor bulletins and researcher disclosures, and EPSS is ingested from FIRST's daily feed.
Those signals feed a single priority score from 0 to 100. The docs describe the default ordering:
| Combination | Default priority band |
|---|---|
| Reachable, on KEV, in production | 90 to 100 (P0) |
| Reachable, EPSS above 0.5, in production | 75 to 89 (P1) |
| Reachable, high CVSS, in staging | 50 to 74 (P2) |
| Unreachable, any severity | 0 to 30 (backlog) |
The weights are configurable per tenant under Settings › Prioritization, and you can declare which assets are regulated or business critical through policy so their findings rank higher.
In the Vulnerabilities tab the table has Priority and Reachability columns, sorted by priority by default. The docs list filters you can type directly:
reachable:true
kev:true
epss:>0.3
env:production reachable:true kev:true
The last one is the docs' own "P0 backlog" query. You can also ask Griffin in plain English, for example "Show my P0 findings in production that are reachable."
How this maps to the standards above: SSDF RV.2.2 asks for a risk based decision on each vulnerability. The priority score, the reachability verdict and the KEV and EPSS values on each finding are the evidence for that decision, and the Accept Risk and Mark False Positive actions (both with justification and approval) are where you record it. For a federal team working under BOD 26-04, KEV status is one of the four factors that set the timeline; the other three (asset exposure, exploit automation and technical impact) still have to come from your asset inventory and CISA's published data.
Fix pull requests for npm, Yarn and pnpm
When a fixed version exists, Safeguard's AI Remediate (part of autonomous remediation, GA) can produce the change for you. The docs list JavaScript with npm, Yarn and pnpm as Full AI Remediate support. The flow is:
- Analysis. Griffin reads the vulnerability and your codebase.
- Solution generation. It works out the safest upgrade path.
- Impact assessment. It checks breaking changes and compatibility.
- PR creation. It opens a pull request with all the changes.
- Validation. Automated tests run to verify the fix, where you have configured them.
For dependency upgrades, the docs say Griffin upgrades vulnerable packages to fixed versions, handles transitive dependency conflicts, updates lockfiles, and keeps compatibility with existing code. Each PR has a clear title with the CVE reference, a description of the vulnerability, a summary of changes, breaking change warnings and links to advisories.
You can trigger it three ways from the web app:
- Single: open the vulnerability, click AI Remediate, review the proposed changes, click Create Pull Request.
- Bulk: select several vulnerabilities, click AI Remediate Selected. Griffin batches compatible fixes and creates one PR per repository.
- Page level: click AI Remediate at the top of the Vulnerabilities page to get a remediation plan for every fixable vulnerability, then approve the PRs.
Each proposed change carries a breaking change rating: Safe, Minor, Major or Critical. The docs' advice is sensible for any JavaScript team: enable tests, start with safe fixes, and never auto-merge breaking changes.
Off by default, and why that matters
Autonomous remediation ships switched off, with every category set to Manual. Nothing is applied until an administrator turns on the master toggle and moves a category to Auto. The Vulnerabilities and Dependencies categories each offer four strategies:
- Safest: backward compatible fixes only.
- Balanced: minor version updates and patches, minimal breaking changes.
- Aggressive: all available fixes including major version updates.
- Smart: the model picks per item based on context, risk and impact.
For JavaScript, Safest maps well to semver patch and minor bumps inside your existing ranges, and Aggressive is where major version jumps (and the API changes that come with them) appear. Most teams start on Manual with Safest, review a few weeks of PRs, then decide whether any category earns Auto. The full category and strategy matrix is covered in Autonomous remediation in Safeguard: eight categories, four strategies, off by default.
What to check in a JavaScript fix PR
A fix PR for a JavaScript project is a lockfile change as much as a manifest change. When you review one:
- Confirm the lockfile moved with the manifest. npm's documentation says
npm ciexits with an error ifpackage.jsonand the lockfile disagree. A PR that changes one without the other will fail your CI install step. - Look at where the fix landed. If the vulnerable package is transitive, the change may be a bump of the parent package or a root level override. npm reads
overridesonly from the rootpackage.json, Yarn readsresolutionsonly at the root, and pnpm readsoverridesfrompnpm-workspace.yaml. An override forces a version on packages that never asked for it, so read the breaking change rating and run the parent's tests. - Check for duplicate copies. After the change, the old version should be gone from every path, not only the one in the finding. Rescan, or read the transitive path view again.
- Respect the flags the lockfile was built with. npm warns that a lockfile created with flags such as
--legacy-peer-depsneeds the same flags at install time. If your repository relies on one, make sure CI and the PR agree.
License data for npm packages
License compliance (GA) runs over the same dependency graph. For JavaScript, Safeguard reads the license field in package.json, recognises SPDX identifiers, matches the full text of LICENSE, LICENCE, COPYING and NOTICE files against known licence texts, and reads source file headers. When the signals disagree (the docs' example is package.json saying MIT while the LICENSE file is AGPL), Safeguard raises a license discrepancy finding.
Licences are classified as permissive, weak copyleft, strong copyleft, network copyleft, commercial, public domain or unknown, and Safeguard understands full SPDX expressions such as MIT OR Apache-2.0, which are common in npm metadata. You declare allow, deny and require review lists in a YAML licence policy, scope them to asset labels (an SDK you distribute can have stricter rules than a service you host), and enforce them through gates at CI time, at pull request time and at admission. Attribution files (NOTICE, third party notices and licence text copies) are generated in the platform; the docs say there is no CLI command for them yet, although safeguard scan writes a license.json with the licences it found.
The npm ecosystem has a long tail of packages with missing or unusual licence fields. Expect an "unknown" bucket on your first scan and plan time to review it. Open source licence obligations, OpenChain and attribution with Safeguard covers the licence side in depth.
Step by step: SCA for a JavaScript or TypeScript repository
Here is the full flow from an unscanned repository to gated builds and fix PRs. Commands and screen names are taken from Safeguard's docs.
1. Commit your lockfile
Before you scan anything, make sure the repository has a committed package-lock.json, yarn.lock or pnpm-lock.yaml. pnpm's documentation is direct about this: always commit the lockfile, because it makes installs consistent between development, testing and production. The same reasoning applies to scanning. If there is no lockfile, the scan has to reason from version ranges.
2. Try a local scan with the CLI
Install the CLI (GA). From the docs, on macOS and Linux:
curl -fsSL https://cli.safeguard.sh/install | bash
On Windows, in PowerShell:
irm https://cli.safeguard.sh/install.ps1 | iex
The CLI is a single static binary, so you do not need Node installed to run it, although your project obviously does. Sign in once:
safeguard login
On a machine with no browser, use safeguard login --no-browser, which prints a URL and a code to enter elsewhere. Then scan the project folder:
safeguard scan --dir .
safeguard scan builds an SBOM for the target, matches it against vulnerability databases and writes JSON results under ./output, one folder per scan, including vuln.json, license.json and collection.json. If you open the interactive terminal by typing safeguard and run /scan . while signed in, the runner uploads the SBOM and findings to the platform so they appear alongside your other projects. Your source code is not uploaded.
The CLI downloads the scanners it needs the first time you open the interactive terminal. On a fresh CI container that has never done that, point the TOOLS_DIR environment variable at a directory that holds them, as the docs describe.
3. Connect the repository in the web app
For continuous coverage, connect the repository. In the web app, open Integrations from the sidebar, filter by Source Code (SCM), and click Connect on the GitHub, GitLab, Bitbucket or generic Git card. For a private repository you provide credentials (for Bitbucket, a username and an access token or app password), click Verify Credentials, pick the workspace and repositories, and set the branch or tag, project name and version for each. The project then appears under Projects with its findings and vulnerability counts.
When you configure SBOM generation for the project, decide on Include Dev Dependencies (see above) and whether to enable Deep Analysis, which adds Griffin's analysis to the SBOM. If you prefer that Safeguard never touches your repository, skip this step and use the CLI or the --local CI mode below; Repo connection, CLI or local runner compares the options.
4. Add the CI step
Safeguard's CI integration runs safeguard ci scan in a plain shell step. There is no plugin to install. Setup is three steps from the docs:
- Create a secret key under Settings › Developer with the scopes
scans:triggerandscans:read. You can lock it to one project. - Store it in your CI as a secret named
SAFEGUARD_API_KEY. - Add the step and push. The first run creates the project.
For GitHub Actions, the docs give this workflow, saved as .github/workflows/safeguard.yml:
name: Safeguard
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- name: Safeguard scan
env:
SAFEGUARD_API_KEY: ${{ secrets.SAFEGUARD_API_KEY }}
SAFEGUARD_INSTALL_DIR: ${{ runner.temp }}/safeguard-bin
run: |
set -eu
curl -fsSL https://cli.safeguard.sh/install | bash
"$SAFEGUARD_INSTALL_DIR/safeguard" ci scan --wait --fail-on critical
No checkout step is needed in the default mode, because Safeguard clones the repository. Each push becomes a new version of the project named after the branch and commit, for example main-a1b2c3d.
If your source must stay on your own runner, add actions/checkout before the step and pass --local. In that mode only the results leave the runner, and the CI docs say it reads dependencies from lockfiles and manifests, naming package-lock.json among them. That is also the route for self hosted or firewalled Git servers.
| Mode | Command | Needs | Scans |
|---|---|---|---|
| On Safeguard (default) | safeguard ci scan | The repository connected as an integration, or public on a major Git host | SAST (GA), SBOM, dependency vulnerabilities |
| On the runner | safeguard ci scan --local | A checkout in the job, no integration | SBOM, dependency vulnerabilities |
Pin the CLI version with SAFEGUARD_VERSION in the step's environment so a new release does not change your build without warning.
5. Decide how the build fails
safeguard ci scan can fail the job in two ways:
--fail-on critical(orhigh,medium,low) with--waitfails when a dependency vulnerability at or above that severity is found.--fail-on-gate <gate>with--waitfails when a named policy gate (orall) is not Passed or Warning.
Drop --fail-on to report without failing. The docs note that SAST findings appear in Safeguard but are not part of --fail-on yet.
A severity threshold is a blunt tool for JavaScript, because it fails the build on an unreachable development only advisory as readily as on a reachable production one. A gate built from policies is more precise. The plain safeguard scan command never fails on findings by design: it exits 0 when the scan ran, 1 when the command failed, and 2 when results were written but the vulnerability scan did not actually run, so a script never mistakes that for a clean pass.
6. Build a policy and a gate
Policies (GA) evaluate your SBOMs and generate findings; gates enforce them at checkpoints. Under Settings › Policies, click Create Policy, define conditions with the visual builder or YAML, set the action and save. Conditions in the docs include vulnerability severity, CVSS score, EPSS score, licence, component age and maintenance status. Each policy has one action:
| Action | What a failed policy does to the gate |
|---|---|
| Block | The gate fails |
| Warn | The gate passes, with a warning naming the policy |
| Notify | The gate passes and a notification goes to your configured channels |
A gate with no linked policies shows Not evaluated, never Passed, and a policy that errors is never counted as a pass. Policy outcomes are turned on per workspace by Safeguard; if you do not see an Outcome column on the Gates and Policies tables, ask your Safeguard contact to enable it. You can also write policies in plain English, covered in Plain English security policies to release gates in Safeguard.
A sensible first set for a TypeScript service: Block on reachable KEV listed vulnerabilities, Warn on reachable critical and high, Notify on new denied licences, and leave unreachable findings to the normal upgrade cycle.
7. Triage in the Vulnerabilities tab
Open the project and go to Vulnerabilities. Findings are grouped into Critical, High, Medium, Low and Exploitable. Start from the P0 filter (env:production reachable:true kev:true), then work down. For each finding you can:
- click View Fix to see upgrade options and breaking change warnings,
- click AI Remediate to have Griffin propose the change and open a PR,
- click Mark False Positive with a justification, sent for approval if your policy requires it,
- click Accept Risk with an expiry date and business justification.
Those last two actions are your SSDF RV.2.2 record.
8. Merge fixes and let continuous scanning watch
Review and merge the fix PRs using the checks above. After that, Safeguard's continuous scanning re-evaluates every project daily as new CVEs are published, so a package that was clean when you merged it is flagged when an advisory lands later. Your SBOM can be exported in CycloneDX or SPDX formats for customers and auditors; SBOM generation and analysis in Safeguard covers that side, including SSDF PS.3.2.
Example output
A local scan writes plain JSON files you can keep as CI artifacts or process yourself. The CI docs show a gate on the local scan output with jq, which fails if any match has critical severity:
safeguard scan --dir . --output out/
jq -e '[.data.matches[].vulnerability.severity | ascii_downcase]
| index("critical") | not' out/*/vuln.json
For real gating, the platform's policies and gates are the documented route. The transitive path view described earlier shows which parent package brought each finding in.
Decision table: which way to run SCA on a JavaScript repository
| Your situation | Use | Why |
|---|---|---|
| A developer wants to check a branch before pushing | safeguard scan --dir . locally | Fast, results as JSON, nothing leaves the laptop unless you scan from the signed in terminal |
| You want results in the platform from a laptop | safeguard, then /scan . | Uploads SBOM and findings, never source |
| Repository is on a major Git host and you are fine with Safeguard cloning it | Connect it under Integrations, add safeguard ci scan | Continuous daily rescans, fix PRs, SAST (GA) on the same scan |
| Source must not leave your CI runner | safeguard ci scan --local | Only results leave; reads your lockfile on the runner |
| Self hosted or firewalled Git server | safeguard ci scan --local | No integration needed |
| Monorepo with many workspaces | Connected project or --local, with a committed root lockfile | Deep scanning resolves workspaces from the lockfile |
You only have a package.json, no lockfile | Upload Manifest File, or --resolve-transitive | Quick view, but ranges not resolved versions; commit a lockfile next |
| You need to fail builds on real risk, not raw severity | --fail-on-gate with reachability aware policies | Gates combine reachability, KEV, EPSS and licence |
Requirements versus what Safeguard does versus what you still own
| Requirement | What Safeguard does | What you still own |
|---|---|---|
| SSDF PW.4.4: verify third party components for known vulnerabilities through their life cycle | SCA on every push plus daily continuous rescans | Making sure every repository is connected or scanned in CI |
| SSDF RV.1.1: gather vulnerability information, including from composition data | Advisory matching from NVD, GHSA, OSV and KEV, rescored as feeds update | Watching for issues in your own first party code and reports from your users |
| SSDF RV.2.2: risk based decision on each vulnerability | Priority score, reachability verdict, Accept Risk and Mark False Positive with approval | Setting the policy for who approves what, and reviewing expiring exceptions |
| SSDF PS.3.2: provenance data for every component of a release | SBOM generation and export in SPDX and CycloneDX | Deciding which SBOM goes to which customer, and when |
| OWASP Top 10 2025 A03: dependency on vulnerable or unmaintained components | Vulnerability findings, component age and maintenance conditions in policies | Replacing abandoned packages where no fix will come |
| BOD 26-04 (federal agencies): timelines from exposure, KEV, automation and impact | KEV status and exploitability signals on every finding | Asset exposure data and the agency's reporting obligations |
What Safeguard does not do
Honest limits, so you can plan around them:
- It does not replace your lockfile discipline. Safeguard reads what your lockfile says. If developers install with ranges and never commit the lockfile, or commit one that is out of sync with
package.json, the scan reflects that. Committing the lockfile and installing withnpm ci,yarn installwith immutable settings, orpnpm install --frozen-lockfileremains your job. - The support matrix lists
bun.lockb, not the newer textbun.lock. Bun projects on the current default lockfile should be tested before you depend on lockfile precision there. Other JavaScript package managers not listed in the matrix are not claimed. - Reachability is evidence, not proof. Dynamic
require,eval, plugin loaders and input driven bugs can defeat static analysis. Safeguard marks those unknown and does not hide them, but an unreachable verdict is a reason to schedule a fix later, not a reason to never fix. Dynamic reachability needs runtime data from your workloads; a repository scan alone uses static and configuration evidence. - The CLI does not enforce policy.
safeguard scannever fails a build on findings, and the CLI does not evaluate policies or gates. Gates are evaluated in the platform, so a build that must fail on policy needssafeguard ci scan --wait --fail-on-gate, and policy outcomes have to be enabled for your workspace. - Fix PRs need a fixed version, and a human, by default. AI Remediate moves you to a version that resolves the advisory. If no fixed version exists, Griffin can propose a safe downgrade where one exists, but otherwise the decision about a temporary mitigation is yours. Autonomous remediation ships off, so nothing merges itself unless you turn it on, and even then you should keep breaking changes on human review.
- It does not test your application. A fix PR is validated by the tests you have configured. If your test coverage is thin, a passing PR tells you less. Safeguard does not write your integration tests for you as part of a dependency fix.
- Some views have no CLI or API yet. The docs say there is no CLI command to query findings by priority, to query the dependency graph, or to generate attribution files, and the
/v1REST endpoints described on some docs pages are not available today. Use the web app, Griffin, or the MCP server for those. - It only sees what you scan. Packages installed globally on a build machine, scripts pulled at build time from a URL, and code copied into the repository without a manifest entry are outside the dependency graph that SCA reads.
Common mistakes
Scanning node_modules instead of the lockfile. A developer's node_modules folder reflects whatever was installed last, possibly with a different package manager or flags. npm itself keeps a hidden lockfile there that it ignores if another tool has changed the tree. Scan the committed lockfile so results match what CI installs.
Treating the dev dependency tree as noise. Build tools and test runners execute on developer laptops and CI runners that hold credentials. Keep dev dependencies in your internal scans even if you exclude them from customer SBOMs.
Fixing transitive issues with a root override and forgetting it. An override in npm, a resolution in Yarn or an override in pnpm pins a version until someone removes it. Six months later it can hold you on an old version of the very package you were trying to fix. Track overrides as technical debt and remove them when the parent catches up.
Failing builds on severity alone. --fail-on high will block a release over an unreachable advisory in a test helper. Start with report only, then move to a gate that uses reachability and KEV.
Auto merging major version bumps. The Aggressive strategy can propose major upgrades. JavaScript libraries often change APIs in majors. Keep Major and Critical breaking change ratings on human review.
Forgetting fork pull requests. GitHub gives pull requests from forks no secrets, so the CI step cannot authenticate. The docs suggest scanning pushes only, or using pull_request_target with care and never running the fork's code in that job.
Ignoring the unknown licence bucket. npm packages with missing or custom licence fields end up as unknown. Leaving them unreviewed is how a copyleft dependency ends up in a distributed SDK.
FAQ
Does Safeguard support npm, Yarn and pnpm lockfiles?
Yes. Safeguard's CLI support matrix lists full support for npm (package-lock.json), Yarn Classic and Yarn Berry (yarn.lock) and pnpm (pnpm-lock.yaml), each with package.json as the manifest. It also lists Bun with bun.lockb. SCA for these package managers is generally available.
Does Safeguard scan TypeScript projects?
Yes. TypeScript projects use the same npm, Yarn or pnpm manifests and lockfiles, so SCA covers them the same way. Safeguard's reachability docs list JavaScript and TypeScript static and dynamic reachability as GA, handling CommonJS, ESM and webpack bundles.
How deep does Safeguard resolve npm transitive dependencies?
Safeguard's docs describe deep dependency scanning up to 100 levels, resolving workspaces, peer dependencies and optional dependencies for npm, pnpm and Yarn. Each finding shows the full path from your project root to the vulnerable package.
Does Safeguard scan devDependencies?
You choose. Connected projects have an Include Dev Dependencies option when generating the SBOM. Many teams include them for internal security scans and exclude them from SBOMs they share with customers.
Can Safeguard fix vulnerable npm packages automatically?
Griffin's AI Remediate supports npm, Yarn and pnpm and opens pull requests that upgrade vulnerable packages and update lockfiles, with a breaking change rating. Autonomous remediation ships switched off, with every category set to Manual, so by default a person triggers and reviews each PR.
How does Safeguard decide which JavaScript vulnerabilities to fix first?
It combines reachability, EPSS, CISA KEV status, CVSS and asset context into a priority score from 0 to 100. Reachable, KEV listed findings in production rank highest. Weights are configurable under Settings › Prioritization.
Can I run Safeguard SCA without sending my source code?
Yes. safeguard scan --dir . runs on your machine, and safeguard ci scan --local runs on your CI runner and sends only results. The interactive terminal's /scan . uploads the SBOM and findings, never source.
How do I fail a CI build on npm vulnerabilities with Safeguard?
Use safeguard ci scan --wait --fail-on critical for a severity threshold, or --fail-on-gate <gate> to fail when a policy gate fails. The plain safeguard scan command reports without failing, so gate on its output or use the CI command.
Is SAST part of the same JavaScript scan?
The default safeguard ci scan mode scans SAST, SBOM and dependency vulnerabilities. SAST in Safeguard is generally available, and its findings are not part of --fail-on yet. The --local mode covers SBOM and dependency vulnerabilities only.
Before you wait on a maintainer
Most JavaScript advisories have a fixed version somewhere in the tree, and the steps above get you to it. The harder cases are a transitive package whose maintainer has not released a fix, or an abandoned package your framework still depends on; the section below describes what Safeguard offers for both, including which parts are generally available.
Fix it without waiting for upstream
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation categories include dependencies, licenses, security posture and findings, and each one is set separately. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds.
3. Gold Registry: start from components that are already fixed
Gold Registry provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs and a completed license check, with signed build provenance (signed with Safeguard's key and verifiable with cosign, SLSA Build Level 3 as the design target) and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. Ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | Safeguard policies and gates in CI |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
The first demo is a real Safeguard CLI session on a JavaScript repository. You install the CLI, open the interactive terminal with safeguard, and ask it to fix the lodash CVE in the repository. It reads the finding and the advisory, asks before any command that changes something, edits package.json to lodash 4.17.21, runs npm install and npm test with your approval, prints a summary and pushes nothing. It then shows /permissions, /model and /scan ., the same scan this post describes.
Click through it at your own pace, or open it full screen.
The second demo sets up the local runner on your own machine, approves it with its device code, and scans from the terminal. This is the route for teams that want SCA results in the platform while their JavaScript source stays on their own hardware.
Click through it at your own pace, or open it full screen.
The third demo shows Claude connected to Safeguard: you add Safeguard from Claude's connector directory, sign in with OAuth and choose which tools Claude can use. Once connected, you can ask Claude about the npm findings in your projects in plain language, for example which reachable vulnerabilities in a TypeScript service are on KEV. Safeguard is listed in the Claude connector directory as a community connector.
Click through it at your own pace, or open it full screen.
To try this on your own repository, download the CLI at safeguard.sh/download and follow the docs at docs.safeguard.sh/docs.
Quick check
A pnpm monorepo has no committed pnpm-lock.yaml. Will a Safeguard scan show exactly what CI installs?
Not reliably. Without a lockfile the scan has to reason from version ranges in package.json, and CI may resolve different versions on each run. Commit pnpm-lock.yaml, install with a frozen lockfile in CI, then scan; Safeguard honours the lockfile so results match what actually installs.
A critical advisory in a transitive npm package is marked unreachable. Should you close it?
No. Unreachable lowers its priority, it does not remove the risk. Check whether it is on KEV or has a high EPSS score, schedule the upgrade on your normal cycle, and if you defer it, record an Accept Risk with an expiry date and a justification so the decision is visible and revisited.
Sources
- Safeguard docs: Software Composition Analysis
- Safeguard docs: CLI support matrix
- Safeguard docs: Deep Dependency Scanning
- Safeguard docs: Vulnerability Prioritization (Reachability Analysis)
- Safeguard docs: Vulnerabilities (ESSCM)
- Safeguard docs: AI Remediate
- Safeguard docs: License Compliance
- Safeguard docs: Generate SBOM
- Safeguard docs: Safeguard CLI
- Safeguard docs: CLI usage guide
- Safeguard docs: CI/CD Integration
- Safeguard docs: GitHub Actions
- Safeguard docs: Policies and Gates
- Safeguard docs: Policy outcomes and actions
- Safeguard docs: Continuous Scanning
- npm docs: package-lock.json
- npm docs: npm ci
- npm docs: package.json (overrides)
- Yarn Classic docs: yarn.lock
- Yarn docs: manifest (resolutions)
- pnpm docs: settings
- pnpm docs: working with Git (lockfiles)
- Bun docs: lockfile
- NIST SP 800-218, SSDF version 1.1
- NIST SP 800-218 full text (PDF)
- OWASP Top 10 2025: A03 Software Supply Chain Failures
- CISA Known Exploited Vulnerabilities Catalog
- CISA BOD 26-04: Prioritizing Security Updates Based on Risk
- FIRST: Exploit Prediction Scoring System
- CycloneDX specification overview
Last reviewed: 25 October 2026. Product capabilities change; check docs.safeguard.sh for the current state.