Repository navigation
Releases
fleetreach 1.6.0
Added
- SBOM input matches OS packages.
pkg:deb,pkg:apkandpkg:rpmcomponents go to the same
distro matchers as an image, so a Syft or Trivy image SBOM scans in full. The release comes from
thedistroqualifier or the operating-system component, and each package's source from what
the tool wrote. A document that names no release, or several, is a coverage gap, and so is one
that names no source packages. SPDX has no module stream labels, so on a Red Hat module build
the scan says it may differ from the image. fix planreads each repo's ranges before it proposes a target, for npm's
package-lock.jsonand for Cargo. A target your own manifest leaves out is a new
manifest-bumpedit:fix applyraises the range, an exact pin included, then updates the
lock. A target a published package leaves out, peer ranges included, is blocked and names the
package to update first, rather than failing infix apply.fix plankeeps targets clear of advisories of their own. The queue only knows the
advisories some repo already has, so a target could carry one that starts above every installed
version, and the re-scan then refused the bump. The plan now reads the OSV exports (--osv-dir,
or wherever a scan finds them) and moves the target to the lowest fix that adds nothing. This
also restores Maven's four-part fixes such as3.41.2.2, which the plan had cut to a version
that does not exist.scan --reachabilitybuilds witness chains for Ruby, PHP, .NET, Java and Kotlin, Elixir and
Swift. A chain starts at the import line in your source and follows the lockfile, for example
imported at app/models/user.rb:3 via loofah → nokogiri. Positive-only, like Python's. JSON
reports the line asreachability.import_site.- SARIF
codeFlowsfollow import chains, for npm and Python too, and start at the import line
where it is known. licenses --detectreads the license files packages ship, with thespdxcrate's own
matcher over the SPDX texts, in pure Rust, on this machine. A package nothing declared takes
the license its files say, unless one of them could not be recognized. A package whose files
name a license its declaration does not account for is a mismatch, with its verdict in
[settings.licenses] mismatch.licenses --detect --downloadreads packages that are not on this machine. It fetches each
public package's archive for npm, Cargo, PyPI, RubyGems, Hex and Go, checks it against the
lockfile's hash (the tree hash ingo.sumfor Go), and keeps only its license files in a cache
keyed by that hash, which--detectthen reads offline.- SBOM input takes CycloneDX XML, SPDX tag-value and GitHub's dependency graph export, and
finds SBOMs by itself.[settings.sbom] discoverscans the SBOMs in a repo with nothing else
to read,[[repo]] sbom_discoversets that per repo, and[[source]] sbom_assetsfetches
those on the latest release onfleet sync(GitHub, Gitea, Forgejo). An SBOM older than the
newest lockfile commit is a coverage gap. pkg:golangandpkg:cargoin an SBOM are now matched, with RustSec and the crates.io
export for Rust. Go names compare without case where a purl lost it, as Trivy's do. Direct
dependencies are found below the lockfile and project nodes Trivy puts under the root, in
every ecosystem.scan --healthasks registries what a lockfile cannot show: npm and NuGet deprecations,
PyPI, RubyGems, crates.io and NuGet yanks, Packagistabandonedwith the replacement, Go
deprecations and retractions, Hex retirements and Maven relocations, plus archived GitHub
repos with a token and low OpenSSF Scorecards with[settings.health] min_scorecard. Only
public packages are sent, and answers are cached for a week. A package with no answer is a
coverage gap, never clean.- Scope for Cargo, Bundler, Hex, NuGet and Go, and a gate per scope. A package only dev
dependencies lead to is nowscope: devin these five too, so--ignore-dev-deps, SBOM
scope: excludedand licensescope = "runtime"cover them.--fail-on-devand
[settings.gate.scope] devgive dev packages their own floor, ornever;diff --basetakes
the flag. Known malware keeps the runtime floor. Whatever cannot be read stays runtime: against
cargo tree,go list, Bundler andmix depson real repos, no package those tools ship was
called dev. Go needs the dependency source, so rungo mod downloadfirst. See "Gate on what
ships". diff --base <ref>says what a change brought in. New findings are compared per
occurrence and placed by git: brought in, with the lockfile line and the manifest line that
caused it; already at the merge base, so the advisory is what changed, which no longer
gates; or not placed, which still gates. Fixture PRs from real lockfiles passed on all twelve
ecosystems.- SARIF regions now also cover
gradle.lockfile,packages.config,pylock.tomland
requirements files under other names. - Five more
[[source]]kinds:gitea,forgejo,bitbucket(Cloud),bitbucket-server
(Data Center) andazure(Azure DevOps).topicson a forge without topics is a config
error. Listings refuse anextlink off the API, a pager that repeats, and Azure's redirect
to sign-in. A sync without a token now says that only public repos were listed. fleet syncandscan --syncreport the repos added, removed and renamed since the last
listing, by the forge's own repo id, and--history-dirappends them toinventory.jsonl.SSL_CERT_FILEsets the trusted roots for forge listings, for a forge behind a private CA.- A repo with nothing fleetreach reads now says "no recognized lockfile or manifest", not "no
Cargo.lock found". scan --incrementalreuses a repo's findings while the binary, the advisory data for its
ecosystems and the files a scan reads are unchanged, and skips loading the advisory data of any
ecosystem with nothing to re-scan. On 10,471 repos a warm run took about half the time and a
third of the memory, with byte-identical output. A fleet of a few large monorepos gains
nothing; see the new scale benchmark page. Maven, Gradle, SBOM inputs and govulncheck are
never reused.db syncupdates an export record by record. It reads the ecosystem'smodified_id.csv,
fetches only the changed records and rewritesall.zip, instead of downloading the whole zip
again. A cache five days old took 8 seconds, against 300 MB before. Every one of 329,822
records then matched a fresh download byte for byte, and a 121-repo scan came out identical.
Anything doubtful falls back to the whole export;--forcealways downloads it. Releases
likeDebian:12have no listing on osv.dev and stay whole downloads.db snapshotand--db-snapshotpin a run. The manifest hashes every source; a pinned
scan is offline and exits 2 unless the directory holds exactly those files.db bundleanddb import --bundlecarry all the advisory data in one tar: OSV and
distro exports, RustSec, KEV, EPSS, CVE and CSAF indexes, and the NVD and Vulnrichment caches.
db sync --sourcegainsrustsec,kev,epss,nvdandvulnrichment. An imported
scan with--enrichmatched the online one on 1,182 findings, run with network access denied,
and 44 images matched on 19,286 findings.scan --offline --enrichreads KEV, EPSS, NVD scores and Vulnrichment from the data directory.--dbon a RustSec clone reports its commit, so--max-db-ageworks there. It used to be
unknown.fix applyhandles pnpm, yarn, Composer, Bundler, NuGet and Maven. Lockfile managers
update with scripts and plugins off; NuGet and Maven change the declared version, or the
property it reads. Checked on 30 throwaway repos across all twelve update paths: 28 edits
verified, and every changed lockfile passed the package manager's frozen install.fix applykeeps its pull requests current. A re-run leaves a branch verified on the
current base alone, rebuilds a stale one, and skips closed or merged requests and branches
someone else pushed to.--close-obsoletecloses requests the plan no longer wants, only
in repos it scanned.fix apply --max-prs,--canaryand--stop-after-failureslimit a rollout.- Gradle builds without a lockfile are scanned. They were skipped before. Version catalogs
and literal declarations resolve through the POM resolver, with the highest version winning,
and the result is always marked partial. Against real Gradle, 163 of 173 packages matched on
Spring PetClinic, 17 of 18 on Aeron and 71 of 109 on the Airflow Java SDK. - Releases add static musl builds for Linux x86-64 and ARM64, and a Windows ARM64 build. The
install script picks musl on Alpine. pnpm-lock.yamlversion 5 is read, and acatalog:dependency is proven to use the version
its importer records. Across 39 real lockfiles, the package sets matchpnpm list,yarn list,
yarn infoandbun pm lson 35; the other four are explained in
docs/specs/16-a-plus.md.- SBOM components carry hashes from the lockfile: Cargo, npm, pnpm, yarn v1, bun, Bundler, mix,
uv, Poetry, pylock and Composer. Sampled against registry downloads: 442 hashes across ten
lockfile formats, all matching. NuGet'scontentHashis left out, since it skips the package
signature and matched none of 25 downloads. sbomwrites CycloneDX 1.7 by default, with--cyclonedx-version 1.6on request, and XML
with-f cyclonedx-xml. All 121 test BOMs pass the official schemas in both versions, the XML
holds the same content as the JSON, and Grype and Trivy read them.- CI runs the whole test suite in a network namespace with no route out, so a test or feature
that needs the network fails the build. ci/fleetreach.gitlab-ci.yml, a template a GitLab pipeline can include to scan a r...
fleetreach 1.5.0
Added
- Repo context and SSVC decisions (spec 08).
[[repo]]takesexposure,
mission_impact,safety_impact,ownerandtags, with[settings.context.defaults]
for the rest. Each vulnerable occurrence then gets a CISA SSVC deployer decision
(immediate,out-of-cycle,scheduled,defer) from KEV exploitation, the advisory's
CVSS vector (Automatable) and the repo's exposure and impact, through the published
ssvc:DT_DP:1.0.0table, tested row by row against CERT/CC's data.-f ssvclists the
decisions with their vector and reason;fix-firstandremediationsort by them; SARIF
results carry the outcome and take their level from it;-f ownerssplits the fix queue by
owner.--fail-on-ssvc <outcome>gates on them and, given alone, replaces the default
severity gate. Fail-closed throughout: without KEV data nothing is decided (and the gate
exits 2), missing context takes the most urgent values, and only a soundNotReachableor a
phantom dependency lowers a decision todefer.[settings.ssvc] poc_epssoptionally
counts a high EPSS as a public proof of concept. - Findings carry the advisory's
cvss_vector(additive JSON field). - SSVC, continued (spec 08). CISA Vulnrichment supplies CISA's own Exploitation and
Automatable per CVE where it has assessed one (fetched under--enrich, cached a day, or
read from a clone with--vulnrichment-dir); it only ever raises Exploitation.
[settings.ssvc] tableloads your own decision table in CERT/CC's CSV layout, validated to
cover all 72 combinations and recorded ascustom:<sha256>.impact,blastand
packages(andpackages-json) show the most urgent outcome and a reach weighted by it
([settings.ssvc] weights, default 8/4/2/0), and rank by it.[[source]]takes the same
context fields for every repo it discovers. - Upgrade risk from fleet evidence (spec 13, phase 1). Each remediation upgrade is rated
low(within the current major),medium(breaking, but at least 3 repos already run the
target or newer in its major) orhigh, with the reasons and the evidence (repos,
newest).-f remediationshows a Bump risk column,remediation-jsontherisk,
risk_reasonsandevidencefields, andfix planedits and pull request descriptions
carry the risk. The JSON report gainspackage_versions: the versions of each vulnerable
package that the fleet's lockfiles hold, and in which repos. bun.lockis read in full (bun 1.2+, lockfile versions 0 and 1): workspaces, nested
copies resolved the way Node loads them, dev scope and dependency paths. Checked against
osv-scanner on opencode, midday, elysia and bun's own lockfile: the same 4,829 registry
packages. The binarybun.lockbis still reported as partial coverage.- SPDX output and vulnerabilities in the SBOM (spec 05).
fleetreach sbom -f spdxwrites
SPDX 2.3 JSON (purls,DEPENDS_ONandDEV_DEPENDENCY_OFrelationships, coverage in
comments, a content-derived namespace).--report report.jsonembeds a saved scan's
findings: CycloneDXvulnerabilities[]with ratings,affects, KEV, EPSS and SSVC
properties and ananalysisthat isnot_affectedonly for a sound verdict (static
NotReachable:code_not_reachable; phantom:requires_dependency), or SPDXSECURITY
advisory references. Both formats pass their official schemas, and an SPDX document scans
back to the same findings. - Scan history, trends and SLAs (spec 09, phases 1 and 2).
scan --history-dir DIR
records each run as a gzipped snapshot plus an index of every finding's first and last
sighting and fix.fleetreach history trend|age|sla|mttrreads it: open findings per run
by severity, SSVC, ecosystem or owner; the oldest open findings; breaches of a
[settings.sla]policy keyed by SSVC outcome or severity (history slaexits 1 on a
breach, 2 without a policy); and time to fix. Fail-closed: a run that exited 2, an errored
repo or a partly-read source never marks a finding fixed, ignored findings are recorded as
ignored, and--history-dirrefuses the flags that drop findings.[settings.history] keepprunes old snapshots while the index keeps first-seen dates; a lost or stale index is
rebuilt from the snapshots. - Notifications (spec 09, phase 3).
fleetreach notify --history-dir DIRreports what
the newest run changed (new findings, fixed findings) and every breach of[settings.sla]
(with-c), grouped by owner. It prints, or POSTs to--webhook(HTTPS; network builds),
a stablefleetreach-notify/1JSON payload or, with-f slack, a Slack Block Kit message;
a webhook gets nothing when nothing changed unless--always.--github-issues(gh) or
--gitlab-issues(glab) with--repokeeps one issue per owner, found again by a hidden
marker, rewritten each run, closed when the owner has nothing open and reopened when a
finding comes back. A gap run never makes old findings look new. - Static HTML report (spec 09, phase 4).
fleetreach report html --history-dir DIR -o fleet-report.htmlwrites one self-contained page (no external resources) to host
anywhere: summary tiles, open findings per run by severity with gap runs marked, SLA
breaches, a queue per owner, fix campaigns from a savedfix status(--fix-status) and
time to fix by severity. Light and dark, usable at phone width, deterministic for a given
history. - GitLab merge requests for
fix apply --open-prandfix status, throughglab:
the open merge request from afleetreach/*branch is updated, otherwise one is created.
The forge is read from each repo'soriginhost, or set with--forge gitlab|github.
fix statusrows gain aforgefield and GitLab states are reported asOPEN,MERGED
andCLOSED, like GitHub's. - Fleet novelty signal (spec 10, phase 4). With
--signalsand--history-dir,
fleetreach:new-to-fleetflags packages whose name no earlier recorded run of the fleet
has read, the cheap early sign of a planted dependency (strongest next to a typosquat or a
recent release). Each history snapshot now records the package names it read, and the
index keeps them past retention. The first run that records names is the baseline and
flags nothing. - Maven mirrors from
settings.xml(spec 04).--maven-fetchfetches POMs through
the mirror of Maven Central that~/.m2/settings.xml(or--maven-settings FILE) names,
chosen with Maven'smirrorOfrules, with credentials from the matching<server>and
${env.NAME}expansion. A configured mirror that cannot be used (plain HTTP, encrypted
password, unset variable) stops the scan instead of falling back to Maven Central.
sbomandlicensestake--maven-fetchand--maven-settingstoo. - Fixes on an older line (spec 13). Remediation items list
alternatives: for repos
still on an older major (or0.xminor) than the target, the smallest version on their
own line that clears their advisories, with what it leaves open and the fleet evidence.
-f remediationshows them under the action (or 1.4.2 on 1.x (3 repos)), andfix plan
gives those repos a low-risk lock bump to it instead of a blocked breaking upgrade. Plan
edits are now marked breaking per repo: a repo on an older line crossing a major is
breaking even when the fleet's newest version is not. fix applyfor Go and uv (spec 07). Go modules are bumped withgo get <module>@v<to>, thengo list -deps -test ./...records the checksums the build needs
(both withGOTOOLCHAIN=localandGOWORK=off), anduv.lockwithuv lock --upgrade-package <pkg>==<to> --no-build, so neither runs package code; both are
verified by the re-scan like Cargo and npm. A raisedgodirective is named in the result,
the commit and the pull request, which now also say what the update ran.- Go from the osv.dev export (spec 01, phase 3). Without govulncheck, the module-level
Go scan readsGo/all.zipfrom--osv-diror the default cache like every other
toolchain-free ecosystem, sofleetreach db syncnow covers Go and no separate
vuln.go.dev mirror is needed.--go-vuln-db=file://still takes a mirror (or an unzipped
export). OnlyGO-records are used, so GHSA copies are not double-counted; an export
with noGO-record is an error, not a clean database. - A new website (spec 15): docs, guides, integrations, use cases, dated comparisons and
examples in one Astro Starlight site, replacing the mdBook docs. Every example is real
output from a frozen demo fleet (website/demo), regenerated bynpm run examplesand
checked in CI, as is the CLI reference generated from--help. Each page has a Markdown
copy, and the site publishesllms.txt,llms-full.txt, a sitemap and JSON-LD. Old
/docs/*.htmladdresses redirect. The README is shorter and points to the site.
Changed
- Faster on large lockfiles and large fleets, with byte-identical output (checked on a
10,471-repo fleet and 13 large open-source monorepos):- Dependency chains are built from one walk of each lockfile's graph instead of one walk per
package. npm, Hex and Julia asked for a chain for every package, so a big lockfile was
quadratic: the 13-monorepo scan went from 9.2s to 2.7s. sbombuilds each repo's inventory and BOM in parallel, and reads each crate's or NuGet
package's license once per run instead of once per repo: 37s to 4.3s on the 10k fleet.
With--fetch-licenses, deps.dev is asked once per package for the whole fleet, not once
per repo.licensesreads repos in parallel too.--signalsreads repos in parallel: 16s to 5.2s on the 10k fleet.- The OSV databases a fleet needs load at the same time instead of one after another.
- Dependency chains are built from one walk of each lockfile's graph instead of one walk per
Fixed
Found by checking each feature against real repos (gitea, caddy, hugo, spring-petclinic,
elysia, a ...
fleetreach 1.4.0
Added
-
SBOMs as a scan input.
[[repo]] sbom = "build/bom.json"scans a CycloneDX (1.4 to
1.6) or SPDX 2.3 JSON document instead of the repo's lockfiles, for builds fleetreach cannot
read. Components are routed by purl to the ecosystem matchers (each feeder gained a public
scan_installed), with direct dependencies and paths from the document's graph. Coverage is
full only when CycloneDX asserts completeness; unscannable components are named. An SBOM
fromfleetreach sbomscans back to identical findings. -
Fix pull requests.
fix apply --pushpushes each verifiedfleetreach/*branch, and
--open-pr [--draft]opens a pull request for it withgh, or updates the open one on a
re-run. The description lists the advisories, flags breaking jumps, states the
verification and names the campaign's other repos. Unverified branches are never pushed.
fix statuslists the pull requests of every repo. Tested against a real private repo. -
Typosquat signal.
scan --signalsalso flags npm, PyPI and Cargo dependencies one edit
from a popular package they are not, against bundled lists (17,338 npm, 15,000 PyPI and
5,000 crate names, refreshed byscripts/update-popular.py). Registry-equivalent spellings,
siblings in one npm scope and version-digit forks are not flagged. Measured at 1.6 flags per
1,000 distinct names on real projects, all legitimate, so it stays opt-in. -
Release-age signal (
scan --release-age 72h). Warns on dependency versions published
less than the given time ago, the question that matters during a worm outbreak. Publish
times come from deps.dev (opt-in: it sends names and versions; cached), and a version
deps.dev cannot date is listed separately, never treated as old.licenses --fetchand
this signal share one deps.dev client and cache. -
Maven transitive resolution. A
pom.xmlis resolved into the dependency tree Maven
would build, in pure Rust and without running Maven: parent chains, properties,
dependencyManagementand imported BOMs, default profiles, nearest-wins mediation with
scope widening, exclusions, optional dependencies, version ranges and multi-module
reactors. POMs come from~/.m2/repositoryand a local cache;scan --maven-fetchalso
fetches missing ones from Maven Central (opt-in: it sends coordinates). Unresolvable items
are named as partial coverage. Matchedmvn dependency:liston test projects (25 of 25, and
85 of 85 artifacts and versions for a Spring Boot project, with one scope label differing). -
Supply-chain signals (
scan --signals). Lockfile hygiene as warnings, read from files
already parsed and with no network: install scripts (npmhasInstallScript), git, URL or
path sources, registries outside the public ones or[settings.signals] registries
(matched on the whole host, so lookalike hosts are caught), registry packages without a
checksum, and third-party GitHub Actions pinned to a movable tag instead of a commit SHA.
Opt-in, so existing reports and--fail-on-warningsgates do not change. -
Known malware is first-class. OSV
MAL-findings are always critical, are never
hidden by--min-severity, and the remediation queue andfix plansay to remove the
package and rotate credentials instead of suggesting a version. -
License inventory and policy.
fleetreach licensesgives every dependency's license
(SPDX, with where it came from:composer.lock,package-lock.json,node_modules, the
Cargo registry cache, virtualenv metadata, the NuGet cache) and a category (forbidden,
restricted, reciprocal, notice, unencumbered).--fetchlooks unknown ones up on deps.dev,
opt-in because it sends package names, with a permanent local cache. A
[settings.licenses]policy (allow/deny lists, per-category verdicts,unknown = "deny"
by default, runtime scope, per-package exceptions with a reason) turns them into
violations: exit 1 on a denied license.fleetreach sbomfills CycloneDXlicensesfrom
the same data. Cargo licenses agree with cargo-deny's on this repository. -
fleetreach fix: verified fix branches.fix planturns a saved report's
remediation queue into per-repo edits (lock bumps, or blocked with the reason: no fix, a
breaking jump without--allow-breaking, an unsupported ecosystem).fix applyperforms
each lock bump in a git worktree on afleetreach/<campaign>/...branch with a lock-only
command that runs no package code (cargo update --precise,npm update --package-lock-only --ignore-scripts, or a GitHub Actions tag rewrite), then re-scans:
the commit is marked verified only when the targeted advisories are gone and nothing new
appeared. Your work tree is never touched and nothing is pushed. -
GitLab fleet sources.
[[source]] kind = "gitlab"lists a GitLab group with all its
subgroups (or a user namespace) on gitlab.com or a self-managed instance (api_url), with
the same include/exclude globs, topics, archived and fork filters and sparse checkouts as
GitHub.GITLAB_TOKEN(ortoken_env) authenticates private groups. -
Stable SARIF fingerprints. Every result carries
partialFingerprintskeyed on the
advisory, ecosystem, repo and package (not the version), so GitHub code scanning keeps one
alert per finding with its history across scans and through bumps that do not fix it.
Before, every result pointed at the same lockfile location, so code scanning could merge
the same advisory across repos or packages into one alert.
Changed
- Repo ids from fleet sources keep the full namespace path of nested URLs
(group/sub/name, previouslysub/name), so same-named projects in different subgroups
no longer collide. Agitsource with a nested URL gets a new id, so a saved baseline
or a[[repo]]override written against the old id needs updating.
fleetreach 1.3.0
Added
-
fleetreach sbom: CycloneDX 1.6 SBOMs. One BOM per repo from the same sourcesscan
reads, in all 12 ecosystems: a component per package with its purl (git dependencies keep
their remote and revision), dev-only packages asscope: excluded, the lockfiles'
dependency graph, andcompositionsthat mark components from partly-read sources
incomplete. Deterministic (content-derived serial number,SOURCE_DATE_EPOCH); exit 2
if any source could not be read. Validated against the official CycloneDX 1.6 schema on
21 real repos (up to 13,291 packages), and against osv-scanner's package lists. -
Python without a lockfile. The PyPI feeder now reads
pylock.toml(PEP 751) and, when
a repo has no Python lockfile at all, its requirements files (requirements*.txt,
*requirements.txt,requirements/*.txt) with-rincludes,-cconstraints, hashes and
markers. pip-compile and uv output is full coverage with dependency paths from# via
annotations; pinned files with no sign of a resolver are labelled partial, and unpinned
requirements direct-only with their names listed, never guessed. Checked against pip's own
parser on warehouse, NetBox and Home Assistant (1,603 of 1,603 pinned packages). Fleet
sources now check out these files too. -
.NET without
packages.lock.json. The NuGet feeder readsobj/project.assets.json
(the full graph fromdotnet restore, with dependency paths; partial when older than the
project file), legacypackages.config, and otherwise the project files'PackageReference
items with central package management,VersionOverride, global references and simple
$(Property)values, labelled direct-only. Range and floating versions are named, never
guessed. Checked statically on eShop, both CleanArchitecture templates and nopCommerce:
every one of 236 package references read with its central version.
fleetreach 1.2.0
Added
- Prebuilt binaries, Homebrew and a container image. Each tagged release now builds
fleetreach(with thenetworkfeature) for Linux x86-64/ARM64, macOS Apple silicon/Intel
and Windows x86-64, attaches the archives withSHA256SUMSand signed build provenance,
publishesghcr.io/tess-fun/fleetreachfor linux/amd64 and linux/arm64, and renders a
Homebrew formula.scripts/install.shinstalls the right archive after checking its
SHA-256. The GitHub Action installs the prebuilt binary (seconds) instead of compiling from
source, with a newversioninput; it still builds with cargo where no binary exists. - OSV exports without per-ecosystem flags. Network builds fetch exactly the osv.dev
exports a fleet needs into~/.cache/fleetreach/osv/and keep them fresh (conditional
requests, atomic install, never replacing a good copy with a failed download). One
--osv-dir(orFLEETREACH_OSV_DIR,[settings.osv] dir) laid out like the osv.dev
bucket serves every ecosystem for air-gapped use. Newfleetreach db syncand
fleetreach db status. Each export's source, age and hash is recorded in
provenance.osv. - pnpm and yarn lockfiles. The npm feeder reads
pnpm-lock.yaml(v6, v9) andyarn.lock
(v1 and berry, including aliases, patches and workspaces) with direct/transitive, dev scope
and dependency paths, validated on 19 real lockfiles. Several npm-family lockfiles in one
directory are all read. - Coverage labels. Sources read with less than a full resolved graph (
pom.xmlwithout
gradle.lockfile,bun.lock/bun.lockb) are listed in a JSONcoveragearray, on
stderr and in the summary line.--require-full-coveragemakes them exit 2. - Fleet sources.
[[source]]entries discover repos from a GitHub organization or user
(with include/exclude globs, topics, archived and fork filters, GitHub Enterprise via
api_url) or a list of https URLs.fleetreach fleet sync/scan --synckeep shallow,
blob-less, sparse checkouts holding only lockfiles and manifests; the commit scanned is
recorded inprovenance.sources. A failed listing stops the run; a failed clone is an
errored gap.gitruns without hooks, submodules or thefile://transport, and the token
never appears on the command line.
Changed
--max-db-agenow also checks every OSV export used (upstreamLast-Modifiedfrom a sync,
else file mtime; unknown age refuses).- NVD severity backfill caches answers per CVE (7 days for a score, 1 day for none), waits
only between real requests, and says how long an unkeyed lookup will take.NVD_API_KEY
is now documented.
Fixed
--npm-prune-unreachablecould mark packagesNotReachablein a pnpmnode_modules
layout, whose transitive packages the import graph does not walk. It no longer prunes
there.--npm-prune-unreachablepruned tools the project runs. The npm import graph only started
from imports, so a package run as a command (vitest, jest, stylelint, eslint from a
package.jsonscript or a CI workflow step) and everything it loads came outNotReachable;
on matomo all 18 npm findings were, including a critical Vitest advisory. Executables named in
scriptsand.github/workflowsare now resolved through each installed package'sbinand
treated as entry points, with witness chains such asvitest → vite → esbuild.