@4da/mcp-server 6.2.4 → 6.2.5
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/CHANGELOG.md +484 -0
- package/README.md +22 -6
- package/dist/db.d.ts +38 -0
- package/dist/db.js +78 -23
- package/dist/doctor.js +6 -3
- package/dist/embeddings.d.ts +1 -1
- package/dist/embeddings.js +16 -14
- package/dist/index.js +73 -103
- package/dist/live/advisory-groups.d.ts +68 -0
- package/dist/live/advisory-groups.js +131 -0
- package/dist/live/cache.js +11 -3
- package/dist/live/cargo-platform.d.ts +6 -0
- package/dist/live/cargo-platform.js +8 -2
- package/dist/live/changelog.js +38 -6
- package/dist/live/dependency-set.d.ts +9 -1
- package/dist/live/dependency-set.js +10 -2
- package/dist/live/fix-paths.d.ts +258 -9
- package/dist/live/fix-paths.js +790 -45
- package/dist/live/github-releases.js +3 -0
- package/dist/live/held-dependencies.d.ts +93 -0
- package/dist/live/held-dependencies.js +212 -0
- package/dist/live/held-targets.d.ts +30 -0
- package/dist/live/held-targets.js +107 -0
- package/dist/live/http-utils.js +13 -3
- package/dist/live/index.d.ts +7 -4
- package/dist/live/index.js +24 -19
- package/dist/live/install-state.js +6 -2
- package/dist/live/js-lockfile-readers.d.ts +8 -1
- package/dist/live/js-lockfile-readers.js +43 -6
- package/dist/live/lockfile-parents.d.ts +15 -0
- package/dist/live/lockfile-parents.js +85 -10
- package/dist/live/lockfile-parsers-pygo.d.ts +47 -2
- package/dist/live/lockfile-parsers-pygo.js +230 -12
- package/dist/live/lockfile-parsers-rbphp.d.ts +38 -0
- package/dist/live/lockfile-parsers-rbphp.js +123 -0
- package/dist/live/lockfile-parsers.d.ts +6 -0
- package/dist/live/lockfile-parsers.js +116 -4
- package/dist/live/lockfile-types.d.ts +28 -1
- package/dist/live/maintenance.d.ts +20 -4
- package/dist/live/maintenance.js +31 -2
- package/dist/live/malware.d.ts +20 -0
- package/dist/live/malware.js +31 -0
- package/dist/live/npm-packument.d.ts +4 -1
- package/dist/live/npm-packument.js +5 -2
- package/dist/live/offered-release.d.ts +53 -0
- package/dist/live/offered-release.js +117 -0
- package/dist/live/osv-ranges.d.ts +111 -0
- package/dist/live/osv-ranges.js +173 -14
- package/dist/live/osv-scanner.d.ts +87 -5
- package/dist/live/osv-scanner.js +310 -59
- package/dist/live/pinned-advisories.d.ts +2 -2
- package/dist/live/pinned-advisories.js +3 -3
- package/dist/live/pypi-advice.d.ts +36 -0
- package/dist/live/pypi-advice.js +158 -0
- package/dist/live/rate-limiter.d.ts +7 -0
- package/dist/live/rate-limiter.js +22 -1
- package/dist/live/release-metadata.d.ts +5 -0
- package/dist/live/resolution.d.ts +3 -0
- package/dist/live/resolution.js +6 -3
- package/dist/live/scan-coverage.d.ts +26 -0
- package/dist/live/scan-coverage.js +70 -0
- package/dist/live/semver-utils.d.ts +8 -0
- package/dist/live/semver-utils.js +26 -3
- package/dist/live/severity-scope.d.ts +26 -1
- package/dist/live/severity-scope.js +32 -0
- package/dist/live/shell-safe.d.ts +19 -0
- package/dist/live/shell-safe.js +36 -0
- package/dist/live/types.d.ts +134 -8
- package/dist/live/version-compare.d.ts +49 -0
- package/dist/live/version-compare.js +131 -0
- package/dist/live/withdrawn-releases.d.ts +50 -0
- package/dist/live/withdrawn-releases.js +125 -0
- package/dist/project-manifests.d.ts +7 -0
- package/dist/project-manifests.js +45 -5
- package/dist/project-scanner.js +6 -4
- package/dist/project-tree.d.ts +56 -0
- package/dist/project-tree.js +174 -5
- package/dist/schema-registry.d.ts +47 -4
- package/dist/schema-registry.js +76 -9
- package/dist/server-instructions.js +4 -2
- package/dist/setup.d.ts +8 -0
- package/dist/setup.js +21 -2
- package/dist/tool-dispatch.js +3 -3
- package/dist/tools/agent-memory.d.ts +5 -0
- package/dist/tools/agent-memory.js +9 -0
- package/dist/tools/briefing-task-scope.js +5 -2
- package/dist/tools/decision-memory.js +2 -0
- package/dist/tools/dependency-check-held.d.ts +44 -0
- package/dist/tools/dependency-check-held.js +98 -0
- package/dist/tools/dependency-check-signals.d.ts +19 -2
- package/dist/tools/dependency-check-signals.js +84 -13
- package/dist/tools/dependency-check-versions.d.ts +37 -4
- package/dist/tools/dependency-check-versions.js +48 -13
- package/dist/tools/dependency-check.d.ts +11 -1
- package/dist/tools/dependency-check.js +81 -29
- package/dist/tools/dependency-health.js +209 -45
- package/dist/tools/get-context.d.ts +3 -0
- package/dist/tools/get-context.js +4 -0
- package/dist/tools/project-scope.d.ts +8 -0
- package/dist/tools/project-scope.js +21 -0
- package/dist/tools/upgrade-impact-features.d.ts +37 -0
- package/dist/tools/upgrade-impact-features.js +176 -0
- package/dist/tools/upgrade-impact-literals.js +26 -4
- package/dist/tools/upgrade-impact-report.d.ts +5 -1
- package/dist/tools/upgrade-impact-report.js +26 -5
- package/dist/tools/upgrade-impact.js +10 -3
- package/dist/tools/upgrade-planner-fixpaths.d.ts +162 -5
- package/dist/tools/upgrade-planner-fixpaths.js +0 -0
- package/dist/tools/upgrade-planner.d.ts +59 -4
- package/dist/tools/upgrade-planner.js +237 -61
- package/dist/tools/vulnerability-scan-concise.d.ts +47 -2
- package/dist/tools/vulnerability-scan-concise.js +37 -3
- package/dist/tools/vulnerability-scan-format.d.ts +126 -24
- package/dist/tools/vulnerability-scan-format.js +193 -26
- package/dist/tools/vulnerability-scan.d.ts +224 -63
- package/dist/tools/vulnerability-scan.js +50 -15
- package/dist/tools/what-should-i-know.d.ts +6 -1
- package/dist/tools/what-should-i-know.js +72 -15
- package/package.json +2 -2
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* One advisory, many records. OSV publishes the same issue as a GHSA record
|
|
4
|
+
* and as an ecosystem record (PYSEC, RUSTSEC, GO), each naming the other in
|
|
5
|
+
* `aliases`. Counting records counted one issue two or three times:
|
|
6
|
+
* - tokio 1.18.0 showed "6 advisories" for 3 (audit A9);
|
|
7
|
+
* - pygoat's django "96 known vulnerabilities" for 51 (readiness audit M1, 2026-10-16).
|
|
8
|
+
* Every count of advisories goes through here.
|
|
9
|
+
*/
|
|
10
|
+
import { isMaintenanceNotice } from "./maintenance.js";
|
|
11
|
+
import { isMalware } from "./malware.js";
|
|
12
|
+
const RANK = { unknown: 0, low: 1, medium: 2, high: 3, critical: 4 };
|
|
13
|
+
/**
|
|
14
|
+
* Group the records of ONE installed copy by shared ids. The grouping is transitive: a record
|
|
15
|
+
* naming ids of two groups joins them.
|
|
16
|
+
*/
|
|
17
|
+
export function groupAdvisories(entries) {
|
|
18
|
+
let groups = [];
|
|
19
|
+
for (const v of entries ?? []) {
|
|
20
|
+
const ids = [v.vulnId, ...(v.aliases ?? [])];
|
|
21
|
+
const hit = groups.filter((g) => ids.some((id) => g.ids.has(id)));
|
|
22
|
+
const merged = { ids: new Set(ids), best: v, members: [v] };
|
|
23
|
+
for (const g of hit) {
|
|
24
|
+
for (const id of g.ids)
|
|
25
|
+
merged.ids.add(id);
|
|
26
|
+
merged.members.push(...g.members);
|
|
27
|
+
if ((RANK[g.best.severity] ?? 0) > (RANK[merged.best.severity] ?? 0))
|
|
28
|
+
merged.best = g.best;
|
|
29
|
+
}
|
|
30
|
+
groups = [...groups.filter((g) => !hit.includes(g)), merged];
|
|
31
|
+
}
|
|
32
|
+
return groups;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* THE grade of one alias group, shared with the 4DA app (`fourda_depengine::severity`, vectors
|
|
36
|
+
* severity.jsonl sv-001..sv-019): the MOST severe OWN grade among the group's records; only when no
|
|
37
|
+
* record has one, the highest grade among their GHSA aliases that can be read (`aliasGrades`, null =
|
|
38
|
+
* held but ungraded), the smallest id on a tie. An unmaintained-package notice is never graded
|
|
39
|
+
* from an alias.
|
|
40
|
+
*/
|
|
41
|
+
export function gradeGroup(records, aliasGrades) {
|
|
42
|
+
let own = null;
|
|
43
|
+
for (const r of records) {
|
|
44
|
+
if (r.own === "unknown")
|
|
45
|
+
continue;
|
|
46
|
+
if (!own || RANK[r.own] > RANK[own.grade])
|
|
47
|
+
own = { id: r.id, grade: r.own };
|
|
48
|
+
}
|
|
49
|
+
let severity = null;
|
|
50
|
+
let inheritedFrom = null;
|
|
51
|
+
if (own) {
|
|
52
|
+
severity = own.grade;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const ids = new Set(records.map((r) => r.id));
|
|
56
|
+
const ghsas = [...new Set(records.filter((r) => !r.maintenance).flatMap((r) => r.aliases.filter((a) => /^GHSA-/i.test(a) && !ids.has(a))))].sort();
|
|
57
|
+
let best = null;
|
|
58
|
+
for (const id of ghsas) {
|
|
59
|
+
const g = aliasGrades.get(id);
|
|
60
|
+
if (!g || g === "unknown")
|
|
61
|
+
continue;
|
|
62
|
+
if (!best || RANK[g] > RANK[best.grade])
|
|
63
|
+
best = { id, grade: g };
|
|
64
|
+
}
|
|
65
|
+
if (best) {
|
|
66
|
+
severity = best.grade;
|
|
67
|
+
inheritedFrom = best.id;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// A malicious-package record (malware.ts) is critical (the 4DA lead, 2026-10-18; sv-010 read high).
|
|
71
|
+
const malicious = records.some((r) => isMalware(r.id, r.aliases, r.cweIds ?? [], r.summary ?? ""));
|
|
72
|
+
const urgency = malicious
|
|
73
|
+
? "critical"
|
|
74
|
+
: severity === "critical" || severity === "high" || severity === "medium"
|
|
75
|
+
? severity
|
|
76
|
+
: severity === "low"
|
|
77
|
+
? "watch"
|
|
78
|
+
: records.every((r) => r.maintenance)
|
|
79
|
+
? "watch"
|
|
80
|
+
: "medium";
|
|
81
|
+
return { severity, inheritedFrom, ownFrom: own?.id ?? null, urgency };
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* One severity per advisory, in every tool (the rule shared with the 4DA app, gradeGroup): within
|
|
85
|
+
* one alias group of one installed copy, every record carries the group's grade. vulnerability_scan
|
|
86
|
+
* kept the "richest" record's grade while dependency_check, dependency_health and upgrade_planner
|
|
87
|
+
* kept the most severe, so CVE-2026-4277 (GHSA-pwjp-ccjc-ghwg rated LOW, PYSEC-2026-52 CVSS 9.8) was
|
|
88
|
+
* low in one tool and critical in the others (final release audit of 6.2.5, 2026-10-18). A record
|
|
89
|
+
* that does not carry the grade itself names where it came from (`severityFrom`: the group record,
|
|
90
|
+
* or the GHSA alias of an inherited grade). A grade a record took from its alias
|
|
91
|
+
* (osv-scanner.ts rateFromAliases) never overrides an own grade in its group.
|
|
92
|
+
*/
|
|
93
|
+
export function gradeAliasGroups(entries) {
|
|
94
|
+
const byCopy = new Map();
|
|
95
|
+
for (const v of entries) {
|
|
96
|
+
const key = `${v.ecosystem}\u0000${v.package}\u0000${v.currentVersion}\u0000${v.installDriftOf ?? ""}`;
|
|
97
|
+
byCopy.set(key, [...(byCopy.get(key) ?? []), v]);
|
|
98
|
+
}
|
|
99
|
+
const regraded = new Map();
|
|
100
|
+
for (const copy of byCopy.values()) {
|
|
101
|
+
for (const group of groupAdvisories(copy)) {
|
|
102
|
+
if (group.members.length < 2)
|
|
103
|
+
continue;
|
|
104
|
+
const inherited = (m) => Boolean(m.severityFrom) && m.severityFrom !== m.vulnId;
|
|
105
|
+
const aliasGrades = new Map();
|
|
106
|
+
for (const m of group.members)
|
|
107
|
+
if (inherited(m))
|
|
108
|
+
aliasGrades.set(m.severityFrom, m.severity);
|
|
109
|
+
const grade = gradeGroup(group.members.map((m) => ({ id: m.vulnId, own: inherited(m) ? "unknown" : m.severity, aliases: [...m.aliases, ...(inherited(m) ? [m.severityFrom] : [])], maintenance: isMaintenanceNotice(m) })), aliasGrades);
|
|
110
|
+
const severity = grade.severity ?? "unknown";
|
|
111
|
+
const from = grade.inheritedFrom ?? grade.ownFrom;
|
|
112
|
+
for (const m of group.members) {
|
|
113
|
+
if (isMaintenanceNotice(m) && grade.inheritedFrom)
|
|
114
|
+
continue;
|
|
115
|
+
if (m.severity === severity && (!inherited(m) || m.severityFrom === from))
|
|
116
|
+
continue;
|
|
117
|
+
const next = { ...m, severity };
|
|
118
|
+
if (from && from !== m.vulnId)
|
|
119
|
+
next.severityFrom = from;
|
|
120
|
+
else
|
|
121
|
+
delete next.severityFrom;
|
|
122
|
+
regraded.set(m, next);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return regraded.size === 0 ? [...entries] : entries.map((e) => regraded.get(e) ?? e);
|
|
127
|
+
}
|
|
128
|
+
/** Distinct advisories among the records of one installed copy. */
|
|
129
|
+
export function distinctAdvisoryCount(entries) {
|
|
130
|
+
return groupAdvisories(entries).length;
|
|
131
|
+
}
|
package/dist/live/cache.js
CHANGED
|
@@ -37,6 +37,14 @@ export function closeServerCacheDb() {
|
|
|
37
37
|
serverCache.close();
|
|
38
38
|
serverCache = null;
|
|
39
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* A row fetched "in the future" was written under a clock that was ahead, or the clock has
|
|
42
|
+
* since gone back (an RTC reset). Its expires_at came from that clock too, so it could keep a
|
|
43
|
+
* year-old "no advisories" answer live: scan 0 vulnerable, dependency_check proceed,
|
|
44
|
+
* what_should_i_know safe_to_delegate (audit 6.2.5 B-3). Such a row is never an answer, fresh or
|
|
45
|
+
* stale, and is purged at start. Five minutes of skew between processes is tolerated.
|
|
46
|
+
*/
|
|
47
|
+
const SANE_FETCHED_AT = "fetched_at <= datetime('now', '+5 minutes')";
|
|
40
48
|
/**
|
|
41
49
|
* Registry, OSV and changelog responses with a TTL.
|
|
42
50
|
*
|
|
@@ -82,7 +90,7 @@ export class LiveCache {
|
|
|
82
90
|
const hit = this.memory.get(key);
|
|
83
91
|
return hit && this.live(hit) ? JSON.parse(hit.data) : null;
|
|
84
92
|
}
|
|
85
|
-
const row = this.db.prepare(
|
|
93
|
+
const row = this.db.prepare(`SELECT data FROM live_cache WHERE cache_key = ? AND expires_at > datetime('now') AND ${SANE_FETCHED_AT}`).get(key);
|
|
86
94
|
if (!row)
|
|
87
95
|
return null;
|
|
88
96
|
try {
|
|
@@ -97,7 +105,7 @@ export class LiveCache {
|
|
|
97
105
|
const hit = this.memory.get(key);
|
|
98
106
|
return hit ? { data: JSON.parse(hit.data), fetchedAt: hit.fetched_at } : null;
|
|
99
107
|
}
|
|
100
|
-
const row = this.db.prepare(
|
|
108
|
+
const row = this.db.prepare(`SELECT data, fetched_at FROM live_cache WHERE cache_key = ? AND ${SANE_FETCHED_AT}`).get(key);
|
|
101
109
|
if (!row)
|
|
102
110
|
return null;
|
|
103
111
|
try {
|
|
@@ -150,7 +158,7 @@ export class LiveCache {
|
|
|
150
158
|
purged++;
|
|
151
159
|
return purged;
|
|
152
160
|
}
|
|
153
|
-
const result = this.db.prepare(
|
|
161
|
+
const result = this.db.prepare(`DELETE FROM live_cache WHERE expires_at <= datetime('now') OR NOT (${SANE_FETCHED_AT})`).run();
|
|
154
162
|
return result.changes;
|
|
155
163
|
}
|
|
156
164
|
invalidateSource(source) {
|
|
@@ -22,6 +22,12 @@ export declare function activeCratesForHost(dir: string): Set<string> | null;
|
|
|
22
22
|
* untrusted input. `CARGO_TARGET_DIR` points at the same scratch directory so
|
|
23
23
|
* cargo's metadata writes never land in the user's tree either.
|
|
24
24
|
*
|
|
25
|
+
* The scratch directory lives in the server's per-user data directory, not
|
|
26
|
+
* the system temp directory. On a shared machine `/tmp` is writable by every
|
|
27
|
+
* user, so a `/tmp/.cargo/config.toml` or a pre-created
|
|
28
|
+
* `/tmp/4da-mcp-cargo-resolve/.cargo/config.toml` planted by another account
|
|
29
|
+
* could set `build.rustc-wrapper` and run code as this user (audit 6.2.5 C-2).
|
|
30
|
+
*
|
|
25
31
|
* Exported for tests.
|
|
26
32
|
*/
|
|
27
33
|
export declare function cargoTreeInvocation(dir: string): {
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
*/
|
|
43
43
|
import { execFileSync } from "node:child_process";
|
|
44
44
|
import * as fs from "node:fs";
|
|
45
|
-
import * as os from "node:os";
|
|
46
45
|
import * as path from "node:path";
|
|
46
|
+
import { serverDataDir } from "../db.js";
|
|
47
47
|
import { fileSignature } from "./file-signature.js";
|
|
48
48
|
/** Rust target triple for the current host, or null for a platform we do not map. */
|
|
49
49
|
export function hostTriple() {
|
|
@@ -133,10 +133,16 @@ function computeActiveCrates(dir) {
|
|
|
133
133
|
* untrusted input. `CARGO_TARGET_DIR` points at the same scratch directory so
|
|
134
134
|
* cargo's metadata writes never land in the user's tree either.
|
|
135
135
|
*
|
|
136
|
+
* The scratch directory lives in the server's per-user data directory, not
|
|
137
|
+
* the system temp directory. On a shared machine `/tmp` is writable by every
|
|
138
|
+
* user, so a `/tmp/.cargo/config.toml` or a pre-created
|
|
139
|
+
* `/tmp/4da-mcp-cargo-resolve/.cargo/config.toml` planted by another account
|
|
140
|
+
* could set `build.rustc-wrapper` and run code as this user (audit 6.2.5 C-2).
|
|
141
|
+
*
|
|
136
142
|
* Exported for tests.
|
|
137
143
|
*/
|
|
138
144
|
export function cargoTreeInvocation(dir) {
|
|
139
|
-
const scratch = path.join(
|
|
145
|
+
const scratch = path.join(serverDataDir(), "cargo-resolve");
|
|
140
146
|
return {
|
|
141
147
|
args: [
|
|
142
148
|
"tree",
|
package/dist/live/changelog.js
CHANGED
|
@@ -107,7 +107,36 @@ export function parseVersionHeading(raw) {
|
|
|
107
107
|
return null;
|
|
108
108
|
return { version, date: extractDate(rest) };
|
|
109
109
|
}
|
|
110
|
-
|
|
110
|
+
/**
|
|
111
|
+
* An ATX heading `## text ##` as [level, text], parsed by hand in linear time.
|
|
112
|
+
* The regex it replaces (`/^(#{1,6})\s+(.*?)\s*#*\s*$/`) was cubic on interior
|
|
113
|
+
* whitespace: a 1,000-space heading in a package's CHANGELOG took over 20 s on
|
|
114
|
+
* the event loop (audit 2026-10-12, B-10). Same result otherwise ("C#" reads "C").
|
|
115
|
+
*/
|
|
116
|
+
function parseAtx(line) {
|
|
117
|
+
const open = /^#{1,6}/.exec(line);
|
|
118
|
+
if (!open)
|
|
119
|
+
return null;
|
|
120
|
+
const level = open[0].length;
|
|
121
|
+
const after = line.slice(level);
|
|
122
|
+
const body = after.trimStart();
|
|
123
|
+
if (body.length === after.length)
|
|
124
|
+
return null; // "#tag" or "#######": not a heading
|
|
125
|
+
let end = body.length;
|
|
126
|
+
while (end > 0 && /\s/.test(body[end - 1]))
|
|
127
|
+
end--;
|
|
128
|
+
while (end > 0 && body[end - 1] === "#")
|
|
129
|
+
end--;
|
|
130
|
+
while (end > 0 && /\s/.test(body[end - 1]))
|
|
131
|
+
end--;
|
|
132
|
+
return [level, body.slice(0, end)];
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Lines longer than this are cut before the line-shape regexes run, so every
|
|
136
|
+
* one of them works on bounded input and a hostile line costs milliseconds.
|
|
137
|
+
* No real heading, label or entry comes near it.
|
|
138
|
+
*/
|
|
139
|
+
const MAX_LINE = 4096;
|
|
111
140
|
/**
|
|
112
141
|
* A setext / reStructuredText underline. `=` and `-` at any length (Markdown);
|
|
113
142
|
* the other RST adornments need three, so a stray `**` is not a heading rule.
|
|
@@ -279,7 +308,10 @@ function adornmentLevel(ch) {
|
|
|
279
308
|
}
|
|
280
309
|
/** Parse a changelog into version sections, in document order (usually newest first). */
|
|
281
310
|
export function parseChangelog(text) {
|
|
282
|
-
const lines = text
|
|
311
|
+
const lines = text
|
|
312
|
+
.replace(/\r\n?/g, "\n")
|
|
313
|
+
.split("\n")
|
|
314
|
+
.map((l) => (l.length > MAX_LINE ? l.slice(0, MAX_LINE) : l));
|
|
283
315
|
const state = {
|
|
284
316
|
sections: [],
|
|
285
317
|
current: null,
|
|
@@ -318,14 +350,14 @@ export function parseChangelog(text) {
|
|
|
318
350
|
}
|
|
319
351
|
if (LINK_DEF.test(line))
|
|
320
352
|
continue;
|
|
321
|
-
const atx =
|
|
353
|
+
const atx = parseAtx(line);
|
|
322
354
|
if (atx) {
|
|
323
|
-
const
|
|
324
|
-
const
|
|
355
|
+
const [level, text] = atx;
|
|
356
|
+
const heading = parseVersionHeading(text);
|
|
325
357
|
if (heading)
|
|
326
358
|
openSection(state, heading, level);
|
|
327
359
|
else
|
|
328
|
-
subHeading(state,
|
|
360
|
+
subHeading(state, text, level);
|
|
329
361
|
continue;
|
|
330
362
|
}
|
|
331
363
|
const next = lines[i + 1];
|
|
@@ -10,4 +10,12 @@ import type { ResolvedDependency, VulnerabilityScanResult } from "./types.js";
|
|
|
10
10
|
*/
|
|
11
11
|
export declare function commonPathRoot(dirs: string[]): string | null;
|
|
12
12
|
export declare function dedupeDependencies(deps: ResolvedDependency[]): ResolvedDependency[];
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* A scan with no findings. `unchecked` dependencies (offline: none of them
|
|
15
|
+
* could be asked) are counted in `totalScanned` and reported as unchecked,
|
|
16
|
+
* never as clean.
|
|
17
|
+
*/
|
|
18
|
+
export declare function emptyVulnResult(projectPath: string, offline: boolean, unchecked?: {
|
|
19
|
+
count: number;
|
|
20
|
+
reason: string;
|
|
21
|
+
} | null): VulnerabilityScanResult;
|
|
@@ -77,12 +77,17 @@ function mergeInstallContext(existing, dep) {
|
|
|
77
77
|
existing.installedVersion = dep.installedVersion;
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
|
|
80
|
+
/**
|
|
81
|
+
* A scan with no findings. `unchecked` dependencies (offline: none of them
|
|
82
|
+
* could be asked) are counted in `totalScanned` and reported as unchecked,
|
|
83
|
+
* never as clean.
|
|
84
|
+
*/
|
|
85
|
+
export function emptyVulnResult(projectPath, offline, unchecked = null) {
|
|
81
86
|
return {
|
|
82
87
|
scannedAt: new Date().toISOString(),
|
|
83
88
|
projectPath,
|
|
84
89
|
ecosystemsScanned: [],
|
|
85
|
-
totalScanned: 0,
|
|
90
|
+
totalScanned: unchecked?.count ?? 0,
|
|
86
91
|
totalVulnerable: 0,
|
|
87
92
|
platformInactiveVulnerable: 0,
|
|
88
93
|
bySeverity: { critical: 0, high: 0, medium: 0, low: 0, unknown: 0 },
|
|
@@ -91,5 +96,8 @@ export function emptyVulnResult(projectPath, offline) {
|
|
|
91
96
|
scanDurationMs: 0,
|
|
92
97
|
cached: false,
|
|
93
98
|
offline,
|
|
99
|
+
unchecked: unchecked?.count ?? 0,
|
|
100
|
+
uncheckedReason: unchecked?.reason ?? null,
|
|
101
|
+
staleAnswers: 0,
|
|
94
102
|
};
|
|
95
103
|
}
|
package/dist/live/fix-paths.d.ts
CHANGED
|
@@ -26,7 +26,38 @@ import type { LiveCache } from "./cache.js";
|
|
|
26
26
|
import type { RateLimiter } from "./rate-limiter.js";
|
|
27
27
|
import { type AdvisoryRanges, type CleanTarget } from "./osv-ranges.js";
|
|
28
28
|
import { type LockfileFormat } from "./lockfile-parents.js";
|
|
29
|
+
import { type AdvisoryQuery, type HeldDependency } from "./held-dependencies.js";
|
|
29
30
|
import type { OsvEcosystem } from "./types.js";
|
|
31
|
+
/** What a registry says about one package's releases, for fix targets and parent advice. */
|
|
32
|
+
export interface PackageReleaseFacts {
|
|
33
|
+
/** Published, installable versions: every release except the `withdrawn` ones. */
|
|
34
|
+
versions: string[];
|
|
35
|
+
/**
|
|
36
|
+
* Releases the registry withdrew, never a fix target (withdrawn-releases.ts, the 4DA app's
|
|
37
|
+
* rule): yanked (crates.io; PyPI: every file), and npm `deprecated` only where a later,
|
|
38
|
+
* non-deprecated stable release exists on the same caret line and `latest` is not deprecated.
|
|
39
|
+
*/
|
|
40
|
+
withdrawn: string[];
|
|
41
|
+
/** Per withdrawn release, the registry's words ("4.18.0 is deprecated on npm ("Bad release...")"). */
|
|
42
|
+
withdrawnWhy: Record<string, string>;
|
|
43
|
+
/** The registry's latest release (npm `latest` tag; crates.io the highest non-yanked), or null. */
|
|
44
|
+
latest: string | null;
|
|
45
|
+
/** The package-level deprecation of the newest release (npm), or null. */
|
|
46
|
+
deprecatedLatest: string | null;
|
|
47
|
+
/**
|
|
48
|
+
* npm: releases that carry a `deprecated` message the withdrawal rule does NOT withdraw (a
|
|
49
|
+
* notice on a whole line, such as glob's "Old versions of glob are not supported"), with the
|
|
50
|
+
* message. Still candidates; a tool that offers one says it is deprecated (final release audit
|
|
51
|
+
* of 6.2.5: the plan targeted glob 10.5.0 while dependency_check said review on it).
|
|
52
|
+
*/
|
|
53
|
+
lineNotices?: Record<string, string>;
|
|
54
|
+
/**
|
|
55
|
+
* npm: per release, the dependencies it holds (exact pins and narrow ranges, held-dependencies.ts):
|
|
56
|
+
* name -> spec. A release that holds none is absent. Its advisories come with the release
|
|
57
|
+
* (audit 6.2.5 D-4: express 4.20.0 exact-pins qs 6.11.0).
|
|
58
|
+
*/
|
|
59
|
+
heldSpecs?: Record<string, Record<string, string>>;
|
|
60
|
+
}
|
|
30
61
|
export interface CrateDepReq {
|
|
31
62
|
req: string;
|
|
32
63
|
optional: boolean;
|
|
@@ -34,12 +65,20 @@ export interface CrateDepReq {
|
|
|
34
65
|
export interface CratesSparse {
|
|
35
66
|
/** Versions not yanked. */
|
|
36
67
|
versions: string[];
|
|
68
|
+
/** Yanked versions. */
|
|
69
|
+
yanked?: string[];
|
|
37
70
|
/** version -> dependency crate name -> requirements (one per target/kind entry; dev-dependencies left out). */
|
|
38
71
|
reqs: Record<string, Record<string, CrateDepReq[]>>;
|
|
39
72
|
}
|
|
73
|
+
/** Per published release of a parent: its requirements on one package, and whether it declares it only as a peer. */
|
|
74
|
+
export type RequirementHistory = Record<string, {
|
|
75
|
+
reqs: string[];
|
|
76
|
+
peer: boolean;
|
|
77
|
+
}>;
|
|
40
78
|
export declare class FixPathSources {
|
|
41
79
|
private cache;
|
|
42
80
|
private rateLimiter;
|
|
81
|
+
private npmDocs;
|
|
43
82
|
constructor(cache: LiveCache, rateLimiter: RateLimiter);
|
|
44
83
|
/** Every non-withdrawn OSV advisory for a package, as ranges; null when OSV could not be read. */
|
|
45
84
|
packageAdvisories(ecosystem: OsvEcosystem, name: string): Promise<AdvisoryRanges[] | null>;
|
|
@@ -51,15 +90,138 @@ export declare class FixPathSources {
|
|
|
51
90
|
* records none). Null when unknown; [] when the parent declares none.
|
|
52
91
|
*/
|
|
53
92
|
parentRequirements(ecosystem: OsvEcosystem, parent: string, parentVersion: string, dep: string): Promise<string[] | null>;
|
|
54
|
-
|
|
93
|
+
/**
|
|
94
|
+
* What every published release of `parent` requires of `dep`, from the
|
|
95
|
+
* registry: version -> requirements (`reqs: []` when that release does not
|
|
96
|
+
* depend on it). npm reads dependencies and optionalDependencies of the
|
|
97
|
+
* abbreviated document (as `parentRequirements`), and, when neither holds
|
|
98
|
+
* it, peerDependencies, flagged `peer`: npm 7+ installs a missing peer, so
|
|
99
|
+
* moving a package into peerDependencies is not dropping it (readiness
|
|
100
|
+
* review 2026-10-17, finding 10). crates.io: the sparse index. Null when
|
|
101
|
+
* unknown or unsupported. Used to find the smallest parent release that
|
|
102
|
+
* admits a clean version (audit H1).
|
|
103
|
+
*/
|
|
104
|
+
requirementHistory(ecosystem: OsvEcosystem, parent: string, dep: string): Promise<RequirementHistory | null>;
|
|
105
|
+
/**
|
|
106
|
+
* One npm abbreviated document per package, shared by the release facts and the requirement
|
|
107
|
+
* history of a plan (a parent's document was downloaded twice: NodeGoat's mongodb, ~600
|
|
108
|
+
* releases). Kept in memory for a short while, at most a few at a time; a failure is not kept.
|
|
109
|
+
*/
|
|
110
|
+
private npmDocument;
|
|
111
|
+
/**
|
|
112
|
+
* Release facts for a parent or a fix target: what is installable, what is
|
|
113
|
+
* deprecated or yanked, and the latest release. Null when the registry
|
|
114
|
+
* could not be read or is not supported.
|
|
115
|
+
*/
|
|
116
|
+
releaseFacts(ecosystem: OsvEcosystem, name: string): Promise<PackageReleaseFacts | null>;
|
|
117
|
+
/**
|
|
118
|
+
* npm's abbreviated document: versions without READMEs, with each
|
|
119
|
+
* version's `deprecated` message. A withdrawn release is never offered as a
|
|
120
|
+
* fix target: lodash 4.18.0 ("Bad release. Please use lodash@4.17.21
|
|
121
|
+
* instead.") was recommended by vulnerability_scan (audit B-8, 2026-10-12).
|
|
122
|
+
* Withdrawn means the app's rule (withdrawn-releases.ts): a deprecated
|
|
123
|
+
* release with a later clean one on its caret line. A whole-major notice
|
|
124
|
+
* (glob, tar 6.x) withdraws nothing, or glob 10.4.5 is sent to 12.0.0.
|
|
125
|
+
*/
|
|
126
|
+
private npmFacts;
|
|
55
127
|
/** A crate's sparse-index record: versions and per-version dependency requirements. */
|
|
56
128
|
cratesSparse(name: string): Promise<CratesSparse | null>;
|
|
57
|
-
|
|
129
|
+
/**
|
|
130
|
+
* PyPI's JSON API: every release with files; one whose every file is yanked is withdrawn
|
|
131
|
+
* (PEP 592, the app's `parse_pypi`). A release with no files cannot be installed and is not listed.
|
|
132
|
+
*/
|
|
133
|
+
private pypiFacts;
|
|
58
134
|
private goVersions;
|
|
59
135
|
}
|
|
60
|
-
export type FixTargetResult = CleanTarget
|
|
61
|
-
/**
|
|
62
|
-
|
|
136
|
+
export type FixTargetResult = CleanTarget & {
|
|
137
|
+
/** No target and a malicious landing: "<pkg> is flagged malicious (<id>): remove or replace <pkg>" (#994 review). */
|
|
138
|
+
landingNote?: string;
|
|
139
|
+
/**
|
|
140
|
+
* True when the package's advisories could not be read (OSV /v1/query
|
|
141
|
+
* failed) but the registry could: `target` is then only the smallest
|
|
142
|
+
* installable release at or above the highest per-advisory fix, which is
|
|
143
|
+
* deprecated, yanked or unpublished (`floorWithdrawn` says which). It is
|
|
144
|
+
* NOT checked against the package's other advisories; callers report the
|
|
145
|
+
* fix as checked against the installed version's advisories only.
|
|
146
|
+
*/
|
|
147
|
+
advisoriesUnread?: boolean;
|
|
148
|
+
/**
|
|
149
|
+
* True when the package's registry could not be read: withdrawn (yanked, deprecated) releases
|
|
150
|
+
* were not checked, and the target comes from the advisory data alone, as before FX-H5.
|
|
151
|
+
*/
|
|
152
|
+
registryUnread?: boolean;
|
|
153
|
+
/** The registry's words for each `withheld` release and each withdrawn line fix. */
|
|
154
|
+
withheldWhy?: Record<string, string>;
|
|
155
|
+
/**
|
|
156
|
+
* Per-advisory fixes the registry withdrew, keyed by that fix: the release to take instead
|
|
157
|
+
* (`fix: null` when none past it is known) and the withdrawn ones passed (installableFix).
|
|
158
|
+
*/
|
|
159
|
+
lineFixes?: Record<string, {
|
|
160
|
+
fix: string | null;
|
|
161
|
+
withheld: string[];
|
|
162
|
+
}>;
|
|
163
|
+
/**
|
|
164
|
+
* npm: the target carries a deprecation notice the withdrawal rule keeps (one on its whole line,
|
|
165
|
+
* no later release on the line free of it: glob 10.5.0, "Old versions of glob are not
|
|
166
|
+
* supported"). It stays the target, the smallest fix without a major upgrade, and every tool
|
|
167
|
+
* says it is deprecated, as dependency_check does with `review` (final release audit 7f).
|
|
168
|
+
*/
|
|
169
|
+
targetNotice?: string;
|
|
170
|
+
/** The package's malware advisories (#981 rule a): a release one affects is never the target. */
|
|
171
|
+
malware?: string[];
|
|
172
|
+
/**
|
|
173
|
+
* npm (audit 6.2.5 D-4): the release the package's own advisories pointed at, passed because a
|
|
174
|
+
* dependency it holds (an exact pin, or a narrow range whose newest release is affected) brings
|
|
175
|
+
* an advisory in; `heldPast` names each release passed and why. `target` is then the smallest
|
|
176
|
+
* later release on that line whose own advisories AND held dependencies are clean.
|
|
177
|
+
*/
|
|
178
|
+
heldFrom?: string;
|
|
179
|
+
heldPast?: Array<{
|
|
180
|
+
version: string;
|
|
181
|
+
affected: HeldDependency[];
|
|
182
|
+
}>;
|
|
183
|
+
/** The target holds an affected dependency and no later release searched (`searched`) clears it: not a clean fix. */
|
|
184
|
+
heldBlocked?: {
|
|
185
|
+
affected: HeldDependency[];
|
|
186
|
+
searched: string[];
|
|
187
|
+
};
|
|
188
|
+
/** Whether the target's held dependencies are affected could not be checked, and why: not a clean fix either. */
|
|
189
|
+
heldUnchecked?: string;
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Whether a checked target may be reported as checked against every advisory (fix_checked
|
|
193
|
+
* `all_advisories`): its own advisories were read, and, for npm, what it installs at a fixed
|
|
194
|
+
* version was judged clean (audit 6.2.5 D-4).
|
|
195
|
+
*/
|
|
196
|
+
export declare function targetCheckedAll(clean: FixTargetResult): boolean;
|
|
197
|
+
/** The words for a target's held dependencies (FixTargetResult.heldFrom / heldBlocked / heldUnchecked), or null when there is nothing to say. */
|
|
198
|
+
export declare function heldNote(clean: FixTargetResult): string | null;
|
|
199
|
+
/**
|
|
200
|
+
* The minimal clean target for one installed copy, or null when neither the
|
|
201
|
+
* advisories nor anything that changes the answer could be read.
|
|
202
|
+
*
|
|
203
|
+
* Advisories unreadable, registry readable (audit H5: OSV /v1/query down):
|
|
204
|
+
* a deprecated or yanked release is still never the target. lodash 4.17.20
|
|
205
|
+
* went to 4.18.0 ("Bad release") because the whole check was skipped; the
|
|
206
|
+
* registry facts alone move it to 4.18.1, flagged `advisoriesUnread`.
|
|
207
|
+
*
|
|
208
|
+
* With no per-advisory fix at all (advisories that give only `last_affected`,
|
|
209
|
+
* audit A6), and the installed version known, the registry is asked for the
|
|
210
|
+
* first published release above the installed one that no known advisory
|
|
211
|
+
* affects: a fix candidate the advisories never named, flagged
|
|
212
|
+
* `noAdvisoryFix`, instead of "no fix".
|
|
213
|
+
*/
|
|
214
|
+
export declare function cleanFixTarget(sources: FixPathSources, ecosystem: OsvEcosystem, name: string, perAdvisoryFixes: string[], installed?: string | null): Promise<FixTargetResult | null>;
|
|
215
|
+
/** #981 (b): an advisory whose ranges cannot be read leaves the release unknown, never skipped silently. */
|
|
216
|
+
export declare function unreadableText(ids: string[]): string;
|
|
217
|
+
/** The 4DA lead's rule (2026-10-18): no release is offered that brings in an advisory the installed version does not have. */
|
|
218
|
+
export declare function introducesText(ids: string[]): string;
|
|
219
|
+
/** #981 round 3: an installed copy a malicious advisory affects names no release; the advice is to remove or replace it. */
|
|
220
|
+
export declare function malwareText(name: string, ids: string[]): string;
|
|
221
|
+
/** The words for a target that carries a line-wide deprecation notice (FixTargetResult.targetNotice). */
|
|
222
|
+
export declare function lineNoticeText(version: string, message: string): string;
|
|
223
|
+
/** `withheldNote` for a checked target: the withdrawn releases passed, in plain words; null when none. */
|
|
224
|
+
export declare function withheldText(clean: FixTargetResult, ecosystem: string): string | null;
|
|
63
225
|
/**
|
|
64
226
|
* Verified targets for many installed copies at once, within a time budget:
|
|
65
227
|
* the copies that could not be checked in time (or offline) are simply
|
|
@@ -70,9 +232,27 @@ export declare function verifyFixTargets(sources: FixPathSources | null, items:
|
|
|
70
232
|
ecosystem: OsvEcosystem;
|
|
71
233
|
name: string;
|
|
72
234
|
fixes: string[];
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
235
|
+
installed?: string;
|
|
236
|
+
}>, budgetMs: number, concurrency?: number, options?: {
|
|
237
|
+
heldQuery?: AdvisoryQuery;
|
|
238
|
+
}): Promise<Map<string, FixTargetResult>>;
|
|
239
|
+
/** Said whenever the registry could not be read: withdrawn releases are then not ruled out, never silently. */
|
|
240
|
+
export declare const REGISTRY_UNREAD_NOTE = "the registry could not be read, so whether it yanked or deprecated this release was not checked";
|
|
241
|
+
/**
|
|
242
|
+
* One clause for a target that is not the highest per-advisory fix, or null when it is.
|
|
243
|
+
* Withdrawn releases passed on the way are named in the registry's own words (FX-H5).
|
|
244
|
+
*/
|
|
245
|
+
export declare function targetNote(clean: FixTargetResult, ecosystem?: string): string | null;
|
|
246
|
+
/**
|
|
247
|
+
* A finding's own fix as the registry still offers it (the app's `installable_fix`): the
|
|
248
|
+
* advisory's fixed version, or, when the registry withdrew it, the release to take instead
|
|
249
|
+
* (null: none past it is known), with the withdrawn ones and the plain-words note.
|
|
250
|
+
*/
|
|
251
|
+
export declare function rowFix(fixed: string | null, clean: FixTargetResult | undefined, ecosystem: string): {
|
|
252
|
+
fixed: string | null;
|
|
253
|
+
withheld: string[];
|
|
254
|
+
note: string | null;
|
|
255
|
+
};
|
|
76
256
|
export interface RefreshVerdict {
|
|
77
257
|
/** True when a lockfile refresh reaches a clean version (read, or inferred: see `inferred`). */
|
|
78
258
|
refreshFixes: boolean;
|
|
@@ -86,11 +266,15 @@ export interface RefreshVerdict {
|
|
|
86
266
|
format: LockfileFormat;
|
|
87
267
|
/** The command, run in the lockfile's directory; for yarn 1 and bun, the manual steps (no command moves one copy in place). */
|
|
88
268
|
command: string;
|
|
89
|
-
/**
|
|
269
|
+
/**
|
|
270
|
+
* Parent requirements that do NOT admit a clean version (why it is waiting on upstream).
|
|
271
|
+
* `bundled`: the copy ships inside that parent's tarball, so neither a refresh nor an override reaches it (audit E8).
|
|
272
|
+
*/
|
|
90
273
|
blocking: Array<{
|
|
91
274
|
parent: string;
|
|
92
275
|
parentVersion: string | null;
|
|
93
276
|
requirement: string;
|
|
277
|
+
bundled?: boolean;
|
|
94
278
|
}>;
|
|
95
279
|
/** The version a refresh resolves to, when the registry's version list was read. */
|
|
96
280
|
resolvesTo: string | null;
|
|
@@ -108,6 +292,70 @@ export interface RefreshVerdict {
|
|
|
108
292
|
* pnpm 8.15 to 10.34 also moved a root project's transitive copy without it.
|
|
109
293
|
*/
|
|
110
294
|
export declare function refreshCommand(format: LockfileFormat, name: string, installed: string, target: string, workspaceRoot?: boolean): string | null;
|
|
295
|
+
/**
|
|
296
|
+
* Whether an override (npm `overrides`, pnpm `pnpm.overrides`, yarn `resolutions`) can reach the copy
|
|
297
|
+
* these blocking parents hold: never when one of them ships it bundled (rule vectors rv-066..rv-068).
|
|
298
|
+
*/
|
|
299
|
+
export declare function overrideReaches(blocking: Array<{
|
|
300
|
+
bundled?: boolean;
|
|
301
|
+
}>): boolean;
|
|
302
|
+
/**
|
|
303
|
+
* How a DIRECT dependency moves to its fix target, worded for the tool and the manifest that
|
|
304
|
+
* declares it: a port of the 4DA app's `osv/direct_upgrade.rs` (rule vectors fc-028..fc-053),
|
|
305
|
+
* read from the declaring project's own manifest. A command is named only where it was run
|
|
306
|
+
* against the tool (2026-10-15/16: npm 10.9.2, pnpm 10.34.6, yarn 1.22.22, yarn 4.9.1, cargo
|
|
307
|
+
* 1.95, go 1.24) and moved the dependency while keeping it as declared. Plain `npm install x@v`
|
|
308
|
+
* wrote `^v` over an exact pin and over `~` (readiness review 2026-10-17, finding 4).
|
|
309
|
+
*
|
|
310
|
+
* Null, and the plan keeps its plain "bump it where it is declared", for anything this cannot
|
|
311
|
+
* read with certainty: a dependency declared in two sections; a declared spec that does not
|
|
312
|
+
* admit the installed copy; `>=`, `||`, x-range, tag, git, `workspace:` or alias specs; a git,
|
|
313
|
+
* path or alternate-registry crate; a yarn workspace member (`yarn up` is project-wide); a Go
|
|
314
|
+
* `replace`; hashed or compiled (pip-compile, uv) requirements; bun or an unknown tool.
|
|
315
|
+
*/
|
|
316
|
+
export type DirectFix = {
|
|
317
|
+
command: string;
|
|
318
|
+
refresh?: false;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* The declaration ALREADY admits the target (navcal: sharp, next and vite under `^`): the minimal
|
|
322
|
+
* advice is the tool's in-range lockfile refresh, which leaves the manifest untouched, not a bump
|
|
323
|
+
* of the declaration (the 4DA app's DirectFix::Refresh). Each was run on a project declaring
|
|
324
|
+
* `ms ^2.1.1` locked at 2.1.1 (2026-10-18): it moved the lock to 2.1.3 and left package.json
|
|
325
|
+
* unchanged; plain `pnpm update ms` rewrote `^2.1.1` to `^2.1.3`, hence `--no-save`. Cargo:
|
|
326
|
+
* `itoa = "1.0.5"` locked 1.0.11 with Cargo.toml unchanged. Any readable range qualifies,
|
|
327
|
+
* `>=` and `||` included: no operator needs carrying.
|
|
328
|
+
*/
|
|
329
|
+
| {
|
|
330
|
+
command: string;
|
|
331
|
+
refresh: true;
|
|
332
|
+
} | {
|
|
333
|
+
instruction: string;
|
|
334
|
+
};
|
|
335
|
+
type NpmManager = "npm" | "pnpm" | "yarn1" | "yarn-berry";
|
|
336
|
+
/** The spec `name` is declared with, when exactly one of dependencies / devDependencies / optionalDependencies holds it. */
|
|
337
|
+
export declare function npmDeclared(packageJson: string, name: string): string | null;
|
|
338
|
+
/** The command that moves a declared npm-family dependency, keeping its operator (quoted: `^` is cmd.exe's escape). */
|
|
339
|
+
export declare function npmDirectCommand(manager: NpmManager, declared: string, name: string, target: string): string | null;
|
|
340
|
+
interface CargoDecl {
|
|
341
|
+
kind: "normal" | "dev" | "build";
|
|
342
|
+
target: string | null;
|
|
343
|
+
key: string;
|
|
344
|
+
req: string | null;
|
|
345
|
+
workspace: boolean;
|
|
346
|
+
otherSource: boolean;
|
|
347
|
+
}
|
|
348
|
+
/** Every declaration of `crate` in a Cargo.toml (by crate name, so a renamed dependency is found through `package = ...`). */
|
|
349
|
+
export declare function cargoDeclarations(manifest: string, crate: string): CargoDecl[];
|
|
350
|
+
/** `go get <module>@v<target>` when go.mod requires the module directly at `installed` and no `replace` points it elsewhere. */
|
|
351
|
+
export declare function goDirectCommand(goMod: string, module: string, installed: string, target: string): string | null;
|
|
352
|
+
/** The edit of the requirements line that pins the package with `==` at `installed` (markers, extras kept). */
|
|
353
|
+
export declare function pypiDirectInstruction(requirements: string, file: string, name: string, installed: string, target: string): string | null;
|
|
354
|
+
/**
|
|
355
|
+
* The fix for `name` at `installed`, declared in the project at `projectDir`, read from its own
|
|
356
|
+
* manifest; null when it cannot be named with certainty (see DirectFix).
|
|
357
|
+
*/
|
|
358
|
+
export declare function directFix(projectDir: string, ecosystem: string, name: string, installed: string, target: string): DirectFix | null;
|
|
111
359
|
/** For a lockfile with no in-place refresh command (yarn 1, bun): the steps that move the copy. */
|
|
112
360
|
export declare function manualRefreshSteps(format: "yarn" | "bun", name: string): string;
|
|
113
361
|
/** The refresh command for the project whose lockfile is `lockfile`; null when no command is named for it. */
|
|
@@ -140,3 +388,4 @@ export declare function refreshVerdict(sources: FixPathSources, ecosystem: OsvEc
|
|
|
140
388
|
* at its newest matching version.
|
|
141
389
|
*/
|
|
142
390
|
export declare function cargoPrecisePrerequisites(sources: FixPathSources, lockContent: string, name: string, installed: string, target: string): Promise<string[] | null>;
|
|
391
|
+
export {};
|