osv-scanner-mcp 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -7
- package/dist/index.js +5 -4
- package/dist/osv/suggestFix.js +22 -6
- package/dist/osv/versionScheme.js +22 -0
- package/dist/tools/suggestFix.js +1 -1
- package/dist/utils/nugetVersion.js +41 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
|
|
8
8
|
An MCP server that wraps Google's [OSV-Scanner](https://github.com/google/osv-scanner). Ask an MCP client such as Claude to "check this project for vulnerabilities", and it returns the known vulnerabilities (CVE / GHSA) in your dependencies as a report sorted by severity, together with recommended upgrades.
|
|
9
9
|
|
|
10
|
-
> **Status**: [Published on npm](https://www.npmjs.com/package/osv-scanner-mcp) (`npx -y osv-scanner-mcp`). Supports lockfiles and manifests for Java (Maven / Gradle), JavaScript (npm / yarn / pnpm / bun), Python (Poetry / uv / Pipenv / PDM / requirements.txt), Go, Rust (`Cargo.lock`), and .NET (NuGet: `packages.lock.json`, `obj/project.assets.json`, `packages.config`, `*.deps.json`, and project files), and PHP (Composer: `composer.lock`), with upgrade recommendations for Maven, npm, PyPI, Go, crates.io, and
|
|
10
|
+
> **Status**: [Published on npm](https://www.npmjs.com/package/osv-scanner-mcp) (`npx -y osv-scanner-mcp`). Supports lockfiles and manifests for Java (Maven / Gradle), JavaScript (npm / yarn / pnpm / bun), Python (Poetry / uv / Pipenv / PDM / requirements.txt), Go, Rust (`Cargo.lock`), and .NET (NuGet: `packages.lock.json`, `obj/project.assets.json`, `packages.config`, `*.deps.json`, and project files), and PHP (Composer: `composer.lock`), with upgrade recommendations for Maven, npm, PyPI, Go, crates.io, Packagist, and NuGet. Setup instructions are provided for Claude Code, Claude Desktop, Codex CLI, Antigravity, and VS Code (GitHub Copilot).
|
|
11
11
|
|
|
12
12
|
## Features
|
|
13
13
|
|
|
14
14
|
- **PHP / Composer (source checkout)**: `scan_project` scans `composer.lock`, including both `packages` and `packages-dev`, as Packagist packages. `suggest_fix` recommends release versions and checks candidates against OSV. Composer and PHP code are never executed. See [Composer support](#composer-support).
|
|
15
15
|
- **One-shot multi-language scan**: Pass a project path to `scan_project`, and it detects and scans the Java, JavaScript, Python, Go, Rust, .NET, and PHP lockfiles in the project together. Dependencies that could not be scanned (for example, a manifest without a lockfile or an unpinned requirement) are reported up front in `coverage`
|
|
16
|
-
- **Upgrade recommendations**: `suggest_fix` recommends, for each vulnerable package, the upgrade closest to the current release line that addresses its fix targets, for Maven, npm, PyPI, Go, Rust (crates.io),
|
|
16
|
+
- **Upgrade recommendations**: `suggest_fix` recommends, for each vulnerable package, the upgrade closest to the current release line that addresses its fix targets, for Maven, npm, PyPI, Go, Rust (crates.io), PHP (Packagist), and .NET (NuGet). Known unfixed and informational advisories remain visible. The recommended version is also checked against the OSV database for additional fix-target vulnerabilities
|
|
17
17
|
- **Rust without false positives**: For `Cargo.lock`, only crates from crates.io are scanned. Your own workspace crates, git dependencies, and crates from private registries are not looked up as crates.io crates (OSV-Scanner alone would match them by name, and send their names to the OSV API). RustSec notices about unmaintained or unsound crates are labeled `informational` and counted apart from vulnerabilities
|
|
18
18
|
- **.NET without guessing versions**: Each project file is matched to its own lockfile or `obj/project.assets.json`. Without one, only its direct `PackageReference` items are scanned, and a `Version="x"` (which means x or higher) is reported as a lower bound. References whose version cannot be known without running MSBuild (properties, conditions, floating versions, `Update` / `Remove`) are listed instead of being looked up: OSV treats an unparsable version as older than every release and would report every vulnerability for it
|
|
19
19
|
- **Direct and transitive dependencies**: Packages are marked as direct or transitive dependencies (from `package-lock.json`, `go.mod`, `requirements.txt`, `pom.xml`, `Cargo.lock`, and the .NET files), and update hints are tailored accordingly, for example by naming the direct dependency that pulls in a transitive one
|
|
@@ -22,7 +22,7 @@ An MCP server that wraps Google's [OSV-Scanner](https://github.com/google/osv-sc
|
|
|
22
22
|
- **SBOM scan**: `scan_sbom` checks the dependencies recorded in a CycloneDX/SPDX JSON SBOM. It states explicitly that the SBOM's completeness and its match with the actual artifacts are not verified
|
|
23
23
|
- **Scan history (opt-in)**: With `OSV_MCP_HISTORY=1`, each scan is recorded locally, and `scan_history` lists past runs and shows what changed since the previous run (new and resolved vulnerabilities, version changes, coverage changes)
|
|
24
24
|
- **Severity-sorted reports**: Vulnerabilities are grouped by package and sorted by CVSS score, with five severity labels (critical / high / medium / low / unknown) and summary counts
|
|
25
|
-
- **Fixed versions**: Each vulnerability includes `fixed_versions`, sorted correctly by Maven version precedence (including non-semver forms such as `2.17.1-RELEASE`), Semantic Versioning precedence for npm, Go, and Rust, PEP 440 for PyPI, and
|
|
25
|
+
- **Fixed versions**: Each vulnerability includes `fixed_versions`, sorted correctly by Maven version precedence (including non-semver forms such as `2.17.1-RELEASE`), Semantic Versioning precedence for npm, Go, and Rust, PEP 440 for PyPI, Composer ordering for Packagist, and NuGet ordering for NuGet
|
|
26
26
|
- **Security first**: No shell execution, a fixed argument allowlist, path normalization and boundary checks, scanning private copies instead of the original files, and timeouts and output size limits are built in
|
|
27
27
|
|
|
28
28
|
## Composer support
|
|
@@ -310,7 +310,13 @@ Detects the lockfiles and manifests in a project and scans the Java / JavaScript
|
|
|
310
310
|
- .NET: `Direct` / `Transitive` in `packages.lock.json`; the dependencies of `project.frameworks` in `project.assets.json`; the dependencies of the project library in `*.deps.json`; and every reference read from a project file are direct. `packages.config` does not say, so it is `unknown`. `introduced_by` and `declared_in` are not available yet
|
|
311
311
|
- Other formats (`gradle.lockfile`, `yarn.lock`, `pnpm-lock.yaml`, `bun.lock`, `poetry.lock`, `uv.lock`, `Pipfile.lock`, and `pdm.lock`), npm lockfile version 1, and entries not reachable from the project are `unknown`. If lockfiles disagree, the value is `mixed`
|
|
312
312
|
- `dependency_groups` is the raw dependency group reported by OSV-Scanner (for example `dev`). It is missing or inaccurate for some lockfile formats (absent for pnpm, `optional` for pdm, and so on), so treat it as informational
|
|
313
|
-
- Upgrade recommendations (`suggest_fix`) are available for Maven, npm, PyPI, Go, Rust (crates.io),
|
|
313
|
+
- Upgrade recommendations (`suggest_fix`) are available for Maven, npm, PyPI, Go, Rust (crates.io), PHP (Packagist), and .NET (NuGet; source checkout).
|
|
314
|
+
|
|
315
|
+
**NuGet recommendations (source checkout).** `suggest_fix` compares NuGet release versions with one to four numeric components (missing components become zero), including legacy revisions such as `1.2.3.4`. Numeric components must fit a nonnegative 32-bit integer; unsupported formats withhold a recommendation. Prerelease identifiers compare numerically when they fit signed 32-bit integers, and otherwise by ASCII case-insensitive lexical order, matching NuGet VersionRelease; numeric prerelease identifiers with leading zeroes are rejected. Build metadata does not affect ordering. Package IDs match case-insensitively. Recommendations use OSV `ECOSYSTEM` affected ranges and are rechecked against OSV; unsupported or incomplete ranges cannot establish a verified fix. Stable candidates are preferred; prerelease-only fixes are labeled. The existing cautious 0.x upgrade tiers apply.
|
|
316
|
+
|
|
317
|
+
For direct dependencies, update the `PackageReference` (or `packages.config` in older projects); with central management, update `PackageVersion` in `Directory.Packages.props`. For transitive dependencies, update the parent dependency or explicitly reference a compatible fixed version if NuGet resolution allows it. Runtime packs (`Microsoft.NETCore.App.Runtime.*`, `Microsoft.AspNetCore.App.Runtime.*`, `Microsoft.WindowsDesktop.App.Runtime.*`) generally require an SDK/runtime or target-framework update rather than a new explicit package reference. Check target-framework/RID compatibility and all version constraints, regenerate restore/lockfiles, and rescan. These are suggestions, not edits; `dotnet restore` and MSBuild are not run.
|
|
318
|
+
|
|
319
|
+
`PackageReference Version="x"` or an inclusive minimum range is scanned at its lower bound, not at a confirmed restored version. Its recommendation retains `version_is_lower_bound: true`, and the note means raising that bound. Coverage still records missing restore/lockfiles and unresolved references. Neither a verified affected-range check nor `candidate_check: "clean"` proves framework compatibility, availability on a private feed, or absence of unknown vulnerabilities.
|
|
314
320
|
|
|
315
321
|
### `scan_java_project`
|
|
316
322
|
|
|
@@ -375,7 +381,7 @@ If an existing parent POM cannot be read (larger than 10 MiB, not a regular file
|
|
|
375
381
|
```
|
|
376
382
|
|
|
377
383
|
- `packages` is sorted by the most severe vulnerability, and each `vulnerabilities` list by severity (unknown last)
|
|
378
|
-
- `fixed_versions` lists the fixed versions recorded in OSV, in ascending order: by Maven precedence for Maven, Semantic Versioning precedence for npm, Go, and Rust (values that are not valid SemVer come last), PEP 440 for PyPI (in the order listed by OSV up to v0.5.0),
|
|
384
|
+
- `fixed_versions` lists the fixed versions recorded in OSV, in ascending order: by Maven precedence for Maven, Semantic Versioning precedence for npm, Go, and Rust (values that are not valid SemVer come last), PEP 440 for PyPI (in the order listed by OSV up to v0.5.0), Composer ordering for Packagist (since v0.17.0), and NuGet ordering for NuGet (since v0.18.0); other ecosystems keep the order listed by OSV (the order is not guaranteed). Several release lines may be mixed (for example a 2.12 backport and the 2.15 line). Pre-releases (`5.0.0-beta.3`) and Go pseudo-versions (`0.0.0-20180925071336-cf3bd585ca2a`) may be included. An empty array means that OSV lists no fixed version. Up to v0.4.1, this was always empty for packages outside Maven
|
|
379
385
|
- Vulnerabilities without a severity score are reported as `null` / `"unknown"`
|
|
380
386
|
|
|
381
387
|
### `scan_java_artifact`
|
|
@@ -432,7 +438,7 @@ Invalid JSON returns `invalid_sbom`, an unsupported format `unsupported_sbom_for
|
|
|
432
438
|
|
|
433
439
|
### `suggest_fix`
|
|
434
440
|
|
|
435
|
-
Runs the same detection and scan as `scan_project` and recommends an **upgrade version** for each vulnerable package. Recommendations are available for Java (Maven / Gradle), JavaScript (npm), Python (PyPI), Go, Rust (crates.io),
|
|
441
|
+
Runs the same detection and scan as `scan_project` and recommends an **upgrade version** for each vulnerable package. Recommendations are available for Java (Maven / Gradle), JavaScript (npm), Python (PyPI), Go, Rust (crates.io), PHP (Packagist / Composer), and .NET (NuGet; source checkout). Instead of simply taking the highest fixed version, it picks the fixed version closest to the current release line, falling back through three tiers:
|
|
436
442
|
|
|
437
443
|
| Tier | Meaning |
|
|
438
444
|
|---|---|
|
|
@@ -645,7 +651,7 @@ Design notes and open items are in [docs/DESIGN_TODO.md](docs/DESIGN_TODO.md) (i
|
|
|
645
651
|
- [x] Scan history and changes between runs (`scan_history`, opt-in)
|
|
646
652
|
- [x] `scan_project` for .NET (NuGet), with per-project matching and lower bounds for project files
|
|
647
653
|
- [x] `scan_project` and `suggest_fix` for PHP (Composer `composer.lock`)
|
|
648
|
-
- [
|
|
654
|
+
- [x] `suggest_fix` for .NET (NuGet; source checkout)
|
|
649
655
|
- [ ] Direct and transitive dependencies for `gradle.lockfile` and the YAML/TOML lockfiles
|
|
650
656
|
|
|
651
657
|
## License
|
package/dist/index.js
CHANGED
|
@@ -66,7 +66,7 @@ if (startupWarning !== null) {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
// NOTE: リリース時はpackage.jsonのversionと同じ値に更新すること
|
|
69
|
-
const SERVER_VERSION = "0.
|
|
69
|
+
const SERVER_VERSION = "0.18.0";
|
|
70
70
|
const server = new McpServer({
|
|
71
71
|
name: "osv-scanner-mcp",
|
|
72
72
|
version: SERVER_VERSION,
|
|
@@ -90,7 +90,7 @@ server.registerTool("scan_project", {
|
|
|
90
90
|
"Vulnerabilities marked informational (RustSec notices such as unmaintained or unsound crates) are not counted in vulnerability_count; they are counted in informational_count. " +
|
|
91
91
|
"Each package's dependency_relation tells whether it is a direct or transitive dependency (determined for package-lock.json, go.mod, requirements.txt, and pom.xml; unknown otherwise). " +
|
|
92
92
|
"Composer files are scanned from sanitized copies containing package names and release versions only; local dist.type=path packages are not looked up and are counted in local_composer_packages; branch/dev versions and invalid entries are excluded and reported in skipped_files. Composer and PHP code are never run. Other private Composer package names are sent to api.osv.dev; composer_warnings flags missing standard Packagist notification URLs, which are only a heuristic for origin. " +
|
|
93
|
-
"If coverage.complete is false, do not conclude that the project is safe even with zero findings. Upgrade recommendations (suggest_fix) are available for Java, JavaScript, Python, Go, Rust,
|
|
93
|
+
"If coverage.complete is false, do not conclude that the project is safe even with zero findings. Upgrade recommendations (suggest_fix) are available for Java, JavaScript, Python, Go, Rust, PHP, and .NET (NuGet). Cargo.lock dependency graphs provide direct/transitive relations, introduced_by, and uniquely mapped declaring Cargo.toml paths in declared_in.",
|
|
94
94
|
inputSchema: {
|
|
95
95
|
project_path: z
|
|
96
96
|
.string()
|
|
@@ -114,12 +114,13 @@ server.registerTool("scan_java_project", {
|
|
|
114
114
|
server.registerTool("suggest_fix", {
|
|
115
115
|
title: "Recommend upgrades that fix vulnerabilities",
|
|
116
116
|
description: "Scans the project with the same detection as scan_project and recommends an upgrade version for each vulnerable package. " +
|
|
117
|
-
"Recommendations are available for Java (Maven / Gradle), JavaScript (npm), Python (PyPI), Go, Rust (crates.io),
|
|
117
|
+
"Recommendations are available for Java (Maven / Gradle), JavaScript (npm), Python (PyPI), Go, Rust (crates.io), PHP (Packagist / Composer), and .NET (NuGet). " +
|
|
118
|
+
"NuGet versions support up to four numeric components, case-insensitive prerelease labels, and ignored build metadata. Updates must satisfy target-framework and dependency constraints; runtime packs generally require SDK/runtime or target-framework updates. No dotnet restore or MSBuild is run. " +
|
|
118
119
|
"Composer recommendations support classical numeric release versions with up to four components and alpha/beta/RC/patch suffixes; dev branches, aliases, and unsupported version formats are not evaluated. Check PHP/extension constraints and application compatibility before upgrading. " +
|
|
119
120
|
"The fixed version closest to the current release line is chosen with a three-tier fallback " +
|
|
120
121
|
"(same_minor: same release line -> major_internal: same major version -> cross_major: major upgrade), " +
|
|
121
122
|
"and the response gives the recommended version, the upgrade distance (upgrade_tier), and the fixed version per CVE. For npm, Go, PyPI, and Rust, a minor update within 0.x is also cross_major (may include breaking changes). " +
|
|
122
|
-
"Dependencies scanned at a requirements.txt lower bound (>=) are marked version_is_lower_bound, and the recommendation then means raising the lower bound. " +
|
|
123
|
+
"Dependencies scanned at a requirements.txt lower bound (>=) or a NuGet PackageReference minimum/range are marked version_is_lower_bound, and the recommendation then means raising the lower bound rather than confirming the installed version. " +
|
|
123
124
|
"update_hint explains how to upgrade, depending on whether the package is a direct or transitive dependency (dependency_relation; for npm and Cargo.lock also introduced_by and declared_in). Cargo updates are conditional on parent version requirements; cross-major updates may require Cargo.toml changes. " +
|
|
124
125
|
"The recommended version is checked against api.osv.dev, and versions with known vulnerabilities that do not affect the current version are avoided (result in candidate_check). " +
|
|
125
126
|
"clean excludes known unfixed advisories, known informational advisories that are not fix targets, and new Rust unmaintained/notice records; these may still apply. New unsound records are fix targets. " +
|
package/dist/osv/suggestFix.js
CHANGED
|
@@ -38,6 +38,17 @@ function updateHint(pkg) {
|
|
|
38
38
|
const relation = pkg.dependency_relation;
|
|
39
39
|
const by = pkg.introduced_by?.join(", ");
|
|
40
40
|
switch (pkg.ecosystem) {
|
|
41
|
+
case "NuGet": {
|
|
42
|
+
if (/^Microsoft\.(?:NETCore|AspNetCore|WindowsDesktop)\.App\.Runtime\./i.test(pkg.name)) {
|
|
43
|
+
return "Runtime pack. Update the .NET SDK/runtime or target framework that supplies this pack and regenerate restore/build outputs; do not add an explicit PackageReference merely to change the runtime pack. Check framework/RID compatibility and rescan. This server does not run dotnet restore or MSBuild";
|
|
44
|
+
}
|
|
45
|
+
const shared = "If centrally managed, change PackageVersion in Directory.Packages.props. Regenerate restore/lockfiles and rescan in a trusted environment; verify target-framework compatibility and dependency constraints. This server does not run dotnet restore or MSBuild";
|
|
46
|
+
if (relation === "direct")
|
|
47
|
+
return "Direct dependency. Update its PackageReference in the project file (or packages.config for older projects). " + shared;
|
|
48
|
+
if (relation === "transitive")
|
|
49
|
+
return "Transitive dependency. Update the direct dependency that requires it, or explicitly reference a compatible fixed version when NuGet dependency resolution permits it. " + shared;
|
|
50
|
+
return "Update the dependency version in the project file or packages.config; for a transitive dependency, update its parent or explicitly reference a compatible fixed version. " + shared;
|
|
51
|
+
}
|
|
41
52
|
case "npm":
|
|
42
53
|
if (relation === "direct") {
|
|
43
54
|
return `Direct dependency. Update the version in ${pkg.declared_in?.join(", ") ?? "package.json"}` +
|
|
@@ -133,6 +144,7 @@ function hintFields(pkg, recommended = null, tier = null) {
|
|
|
133
144
|
/** スキャン結果に付いた直接/推移的依存の項目を提案にも写す(無ければ何も出さない) */
|
|
134
145
|
function relationFields(pkg) {
|
|
135
146
|
return {
|
|
147
|
+
...(pkg.version_is_lower_bound ? { version_is_lower_bound: true } : {}),
|
|
136
148
|
...(pkg.dependency_relation !== undefined ? { dependency_relation: pkg.dependency_relation } : {}),
|
|
137
149
|
...(pkg.introduced_by !== undefined ? { introduced_by: pkg.introduced_by } : {}),
|
|
138
150
|
...(pkg.introduced_by_omitted !== undefined ? { introduced_by_omitted: pkg.introduced_by_omitted } : {}),
|
|
@@ -213,15 +225,19 @@ function buildNote(scheme, pkg, recommended, tier, fixableCount, unfixedCount) {
|
|
|
213
225
|
if (unfixedCount > 0)
|
|
214
226
|
note += `. The remaining ${unfixedCount} CVEs have no fixed version newer than the current one and are excluded from the fix. Check recommended_status`;
|
|
215
227
|
}
|
|
216
|
-
|
|
217
|
-
note += `. The current version (${pkg.version}) is the lower bound in requirements.txt (>= or ~=) and may differ from the installed version. ` +
|
|
218
|
-
"The recommendation means raising the lower bound to at least that version";
|
|
219
|
-
}
|
|
228
|
+
note += lowerBoundNote(pkg);
|
|
220
229
|
if (pkg.ecosystem === "Go" && GO_PSEUDO_VERSION.test(pkg.version)) {
|
|
221
230
|
note += ". The current version is a pseudo-version (an untagged commit)";
|
|
222
231
|
}
|
|
223
232
|
return note;
|
|
224
233
|
}
|
|
234
|
+
function lowerBoundNote(pkg) {
|
|
235
|
+
if (!pkg.version_is_lower_bound)
|
|
236
|
+
return "";
|
|
237
|
+
const source = pkg.ecosystem === "NuGet" ? "PackageReference (NuGet minimum/range)" : "requirements.txt (>= or ~=)";
|
|
238
|
+
return `. The current version (${pkg.version}) is the lower bound in ${source} and may differ from the installed version. ` +
|
|
239
|
+
"Any recommendation means raising the lower bound to at least that version";
|
|
240
|
+
}
|
|
225
241
|
/**
|
|
226
242
|
* 1パッケージ分のアップグレード提案を組み立てる(同期・純粋)。
|
|
227
243
|
* contextを渡すと、照会で見つかった脆弱性も避けて候補を選び直す(contextなしの結果が従来の推奨)。
|
|
@@ -318,9 +334,9 @@ export function suggestUpgradeForPackage(pkg, context = {}) {
|
|
|
318
334
|
upgrade_note: (recommended === null && fixableCount > 0
|
|
319
335
|
? "No known fixed version could be confirmed to be outside the affected ranges of every CVE to fix. The recommendation is also withheld when the data are insufficient or use unsupported range types." +
|
|
320
336
|
(unparseableCount > 0 ? ` ${unparseableCount} CVEs list fixed versions that cannot be parsed as versions (tier: unparseable_fix), so the recommendation is withheld.` : "")
|
|
321
|
-
: buildNote(scheme, pkg, recommended, upgradeTier, fixableCount, unfixedCount)) +
|
|
337
|
+
: buildNote(scheme, pkg, recommended, upgradeTier, fixableCount, unfixedCount)) +
|
|
338
|
+
(recommended === null && fixableCount > 0 ? lowerBoundNote(pkg) : "") + (informationalNotes ? ` ${informationalNotes}` : ""),
|
|
322
339
|
...hintFields(pkg, recommended, upgradeTier),
|
|
323
|
-
...(pkg.version_is_lower_bound ? { version_is_lower_bound: true } : {}),
|
|
324
340
|
per_cve_detail: details,
|
|
325
341
|
verification: recommended === null ? "no_verified_candidate" : "verified",
|
|
326
342
|
};
|
|
@@ -13,6 +13,7 @@ import { compareMavenVersions, mavenVersionSeries } from "../utils/mavenVersion.
|
|
|
13
13
|
import { compareParsedPep440, parsePep440 } from "../utils/pep440Version.js";
|
|
14
14
|
import { compareParsedSemver, parseSemver } from "../utils/semverVersion.js";
|
|
15
15
|
import { compareComposerVersions, parseComposerVersion } from "../utils/composerVersion.js";
|
|
16
|
+
import { compareNugetVersions, parseNugetVersion } from "../utils/nugetVersion.js";
|
|
16
17
|
const SEMVER_ECOSYSTEMS = new Set(["npm", "Go", "crates.io"]);
|
|
17
18
|
const MAVEN_SCHEME = {
|
|
18
19
|
isValid: () => true,
|
|
@@ -120,8 +121,27 @@ const COMPOSER_SCHEME = {
|
|
|
120
121
|
return parsed ? `${parsed.parts[0]}.${parsed.parts[1]}` : null;
|
|
121
122
|
},
|
|
122
123
|
};
|
|
124
|
+
/** NuGet VersionRelease ordering; prerelease names and package IDs ignore ASCII case. */
|
|
125
|
+
const NUGET_SCHEME = {
|
|
126
|
+
isValid: (v) => parseNugetVersion(v) !== null,
|
|
127
|
+
compare: compareNugetVersions,
|
|
128
|
+
isPrerelease: (v) => (parseNugetVersion(v)?.prerelease.length ?? 0) > 0,
|
|
129
|
+
classify(current, candidate) {
|
|
130
|
+
const a = parseNugetVersion(current), b = parseNugetVersion(candidate);
|
|
131
|
+
if (!a || !b)
|
|
132
|
+
return "cross_major";
|
|
133
|
+
return classifyCaret({ major: a.parts[0], minor: a.parts[1], patch: a.parts[2] }, { major: b.parts[0], minor: b.parts[1], patch: b.parts[2] });
|
|
134
|
+
},
|
|
135
|
+
seriesLabel(v) {
|
|
136
|
+
const parsed = parseNugetVersion(v);
|
|
137
|
+
return parsed ? parsed.parts[0] === 0 && parsed.parts[1] === 0
|
|
138
|
+
? `0.0.${parsed.parts[2]}` : `${parsed.parts[0]}.${parsed.parts[1]}` : null;
|
|
139
|
+
},
|
|
140
|
+
};
|
|
123
141
|
/** 修正版の推奨に対応するエコシステムの操作。未対応ならnull */
|
|
124
142
|
export function versionSchemeFor(ecosystem) {
|
|
143
|
+
if (ecosystem === "NuGet")
|
|
144
|
+
return NUGET_SCHEME;
|
|
125
145
|
if (ecosystem === "Packagist")
|
|
126
146
|
return COMPOSER_SCHEME;
|
|
127
147
|
if (ecosystem === "Maven")
|
|
@@ -137,6 +157,8 @@ export function versionSchemeFor(ecosystem) {
|
|
|
137
157
|
* PyPIはPEP 503の正規化(小文字化、`-` `_` `.`の連続を`-`)で比較する(実データでは完全一致したが防御的に)
|
|
138
158
|
*/
|
|
139
159
|
export function samePackageName(a, b, ecosystem) {
|
|
160
|
+
if (ecosystem === "NuGet")
|
|
161
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
140
162
|
if (ecosystem !== "PyPI")
|
|
141
163
|
return a === b;
|
|
142
164
|
const normalize = (name) => name.toLowerCase().replace(/[-_.]+/g, "-");
|
package/dist/tools/suggestFix.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `suggest_fix`ツールのハンドラ。
|
|
3
3
|
* scan_projectと同じ検出・スキャンを実行し、脆弱なパッケージごとの
|
|
4
|
-
* 推奨アップグレードバージョン(3段階Tier)を返す。推奨はMaven・npm・Go・PyPI・crates.ioに対応し、
|
|
4
|
+
* 推奨アップグレードバージョン(3段階Tier)を返す。推奨はMaven・npm・Go・PyPI・crates.io・Packagist・NuGetに対応し、
|
|
5
5
|
* それ以外はunsupported_ecosystemとして返す。
|
|
6
6
|
*/
|
|
7
7
|
import { isRemoteResolutionDisabled } from "../osv/runner.js";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const INT_MAX = 2_147_483_647;
|
|
2
|
+
export function parseNugetVersion(version) {
|
|
3
|
+
if (version.length > 256)
|
|
4
|
+
return null;
|
|
5
|
+
const match = /^(\d{1,10}(?:\.\d{1,10}){0,3})(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.exec(version);
|
|
6
|
+
if (!match || match[0] !== version)
|
|
7
|
+
return null;
|
|
8
|
+
const parts = match[1].split(".").map(Number);
|
|
9
|
+
if (parts.some(n => n > INT_MAX))
|
|
10
|
+
return null;
|
|
11
|
+
while (parts.length < 4)
|
|
12
|
+
parts.push(0);
|
|
13
|
+
const prerelease = match[2]?.toLowerCase().split(".") ?? [];
|
|
14
|
+
// NuGet rejects purely numeric release labels with leading zeroes.
|
|
15
|
+
if (prerelease.some(label => /^0\d+$/.test(label)))
|
|
16
|
+
return null;
|
|
17
|
+
return { parts, prerelease };
|
|
18
|
+
}
|
|
19
|
+
export function compareNugetVersions(a, b) {
|
|
20
|
+
const x = parseNugetVersion(a), y = parseNugetVersion(b);
|
|
21
|
+
if (!x || !y)
|
|
22
|
+
return 0;
|
|
23
|
+
for (let i = 0; i < 4; i++)
|
|
24
|
+
if (x.parts[i] !== y.parts[i])
|
|
25
|
+
return x.parts[i] < y.parts[i] ? -1 : 1;
|
|
26
|
+
if (x.prerelease.length === 0 || y.prerelease.length === 0) {
|
|
27
|
+
return x.prerelease.length === y.prerelease.length ? 0 : x.prerelease.length === 0 ? 1 : -1;
|
|
28
|
+
}
|
|
29
|
+
for (let i = 0; i < Math.min(x.prerelease.length, y.prerelease.length); i++) {
|
|
30
|
+
const left = x.prerelease[i], right = y.prerelease[i];
|
|
31
|
+
const numeric = (label) => /^-?\d+$/.test(label) && Number(label) >= -2_147_483_648 && Number(label) <= INT_MAX;
|
|
32
|
+
const leftNumeric = numeric(left);
|
|
33
|
+
const rightNumeric = numeric(right);
|
|
34
|
+
if (leftNumeric !== rightNumeric)
|
|
35
|
+
return leftNumeric ? -1 : 1;
|
|
36
|
+
const l = leftNumeric ? Number(left) : left, r = rightNumeric ? Number(right) : right;
|
|
37
|
+
if (l !== r)
|
|
38
|
+
return l < r ? -1 : 1;
|
|
39
|
+
}
|
|
40
|
+
return Math.sign(x.prerelease.length - y.prerelease.length);
|
|
41
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "osv-scanner-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "MCP server that wraps Google's OSV-Scanner to scan Java, JavaScript, Python, Go, Rust, .NET, and PHP dependencies for known vulnerabilities and recommend fixes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|