@trustify-da/trustify-da-javascript-client 0.3.0-ea.61444d4 → 0.3.0-ea.62b88e5

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.
Files changed (59) hide show
  1. package/README.md +22 -8
  2. package/dist/package.json +6 -5
  3. package/dist/src/analysis.d.ts +40 -19
  4. package/dist/src/analysis.js +41 -5
  5. package/dist/src/cli.js +95 -17
  6. package/dist/src/config.d.ts +120 -0
  7. package/dist/src/config.js +260 -0
  8. package/dist/src/cyclone_dx_sbom.d.ts +13 -0
  9. package/dist/src/cyclone_dx_sbom.js +39 -1
  10. package/dist/src/index.d.ts +23 -23
  11. package/dist/src/index.js +23 -39
  12. package/dist/src/license/index.d.ts +2 -2
  13. package/dist/src/license/index.js +9 -6
  14. package/dist/src/license/licenses_api.d.ts +2 -2
  15. package/dist/src/license/licenses_api.js +1 -1
  16. package/dist/src/package_version.d.ts +8 -0
  17. package/dist/src/package_version.js +31 -0
  18. package/dist/src/providers/base_java.d.ts +53 -0
  19. package/dist/src/providers/base_java.js +64 -17
  20. package/dist/src/providers/base_javascript.d.ts +54 -0
  21. package/dist/src/providers/base_javascript.js +106 -4
  22. package/dist/src/providers/golang_gomodules.js +6 -5
  23. package/dist/src/providers/java_gradle.d.ts +48 -0
  24. package/dist/src/providers/java_gradle.js +204 -20
  25. package/dist/src/providers/java_maven.d.ts +28 -8
  26. package/dist/src/providers/java_maven.js +104 -10
  27. package/dist/src/providers/javascript_bun.d.ts +12 -0
  28. package/dist/src/providers/javascript_bun.js +42 -1
  29. package/dist/src/providers/javascript_npm.d.ts +13 -0
  30. package/dist/src/providers/javascript_npm.js +40 -1
  31. package/dist/src/providers/javascript_pnpm.d.ts +13 -0
  32. package/dist/src/providers/javascript_pnpm.js +47 -1
  33. package/dist/src/providers/javascript_yarn.d.ts +12 -0
  34. package/dist/src/providers/javascript_yarn.js +75 -2
  35. package/dist/src/providers/manifest.js +6 -3
  36. package/dist/src/providers/processors/yarn_berry_processor.d.ts +5 -1
  37. package/dist/src/providers/processors/yarn_berry_processor.js +3 -2
  38. package/dist/src/providers/processors/yarn_classic_processor.d.ts +5 -1
  39. package/dist/src/providers/processors/yarn_classic_processor.js +8 -6
  40. package/dist/src/providers/python_uv.js +6 -0
  41. package/dist/src/providers/requirements_parser.js +1 -1
  42. package/dist/src/providers/rust_cargo.js +34 -52
  43. package/dist/src/remediate.d.ts +90 -4
  44. package/dist/src/remediate.js +153 -58
  45. package/dist/src/remediation.d.ts +54 -30
  46. package/dist/src/remediation.js +92 -53
  47. package/dist/src/remediation_report.d.ts +3 -20
  48. package/dist/src/remediation_report.js +37 -16
  49. package/dist/src/sbom.d.ts +11 -0
  50. package/dist/src/sbom.js +10 -0
  51. package/dist/src/tools.d.ts +9 -11
  52. package/dist/src/tools.js +34 -13
  53. package/dist/src/updaters/maven_updater.d.ts +40 -8
  54. package/dist/src/updaters/maven_updater.js +26 -3
  55. package/dist/src/updaters/toml_updater.d.ts +42 -13
  56. package/dist/src/updaters/toml_updater.js +36 -4
  57. package/dist/src/workspace.d.ts +2 -1
  58. package/dist/src/workspace.js +2 -1
  59. package/package.json +7 -6
@@ -1,3 +1,14 @@
1
+ /**
2
+ * Resolves a target path to the supported manifest files it contains: the single file
3
+ * if `targetPath` is a supported manifest, or every supported manifest discovered
4
+ * recursively if it's a directory. This is the single source of truth for what
5
+ * `runRemediation` will scan, so callers (e.g. the CLI) can reuse it to tell
6
+ * "no manifests here" apart from "manifests, but nothing to fix".
7
+ * @param {string} targetPath - path to a manifest file or directory
8
+ * @returns {string[]} absolute paths to supported manifests (empty for an empty directory)
9
+ * @throws if the path does not exist, or is a file of an unsupported manifest type
10
+ */
11
+ export function findManifests(targetPath: string): string[];
1
12
  /**
2
13
  * Orchestrates the full remediation pipeline for a single manifest or directory:
3
14
  * discover manifests → scan via DA backend → extract remediations → apply or preview.
@@ -7,15 +18,90 @@
7
18
  * @param {boolean} [options.dryRun=false] - preview changes without modifying files (applies by default)
8
19
  * @param {string} [options.providers] - comma-separated provider list
9
20
  * @param {string} [options.sources] - comma-separated source list
10
- * @param {'dependency'|'bundle'} [options.groupBy='dependency'] - report grouping strategy
11
- * @returns {Promise<{exitCode: number, output: string}>}
21
+ * @param {string} [options.backendUrl] - Trustify DA backend URL
22
+ * @param {boolean} [options.perDependencyChanges=false] - when true, each remediation is populated with
23
+ * a `changes` array describing the isolated, single-dependency edit (see {@link DependencyFix}). This lets
24
+ * callers create one commit/PR per dependency without attributing diff hunks themselves.
25
+ * @returns {Promise<{exitCode: number, remediations: AppliedRemediation[], manifests: string[], appliedFiles: string[]}>}
26
+ * exitCode is 2 for a dry-run that found remediations (nothing written), 0 otherwise. `remediations`
27
+ * is the structured, per-manifest list of applicable updates — each entry carries the originating
28
+ * manifest path(s) in `files` so callers can group and create per-dependency changes. `appliedFiles`
29
+ * lists only the manifests actually written to disk (empty on a dry-run), so callers can report a
30
+ * truthful "updated N files" count without conflating "had remediations" with "was written".
12
31
  */
13
32
  export function runRemediation(targetPath: string, options?: {
14
33
  dryRun?: boolean | undefined;
15
34
  providers?: string | undefined;
16
35
  sources?: string | undefined;
17
- groupBy?: "dependency" | "bundle" | undefined;
36
+ backendUrl?: string | undefined;
37
+ perDependencyChanges?: boolean | undefined;
18
38
  }): Promise<{
19
39
  exitCode: number;
20
- output: string;
40
+ remediations: AppliedRemediation[];
41
+ manifests: string[];
42
+ appliedFiles: string[];
21
43
  }>;
44
+ /**
45
+ * A single version change requested from an updater: bump `groupId:artifactId` to `newVersion`.
46
+ * The input side of every updater; each updater's output side (its `applied` entries) is its own
47
+ * type — see {@link AppliedChange}.
48
+ */
49
+ export type VersionChangeRequest = {
50
+ groupId: string;
51
+ artifactId: string;
52
+ newVersion: string;
53
+ };
54
+ /**
55
+ * One entry from an updater's `applied` list, describing where and how a version was changed.
56
+ * Owned by the updater layer: the union of each updater's applied-entry shape. `changeKey`
57
+ * (per {@link ManifestType}) turns one of these into a stable edit-site identifier.
58
+ */
59
+ export type AppliedChange = import("./updaters/maven_updater.js").MavenAppliedChange | import("./updaters/toml_updater.js").TomlAppliedChange;
60
+ /**
61
+ * The result of running an updater over a manifest's raw content.
62
+ */
63
+ export type UpdaterResult = {
64
+ content: string;
65
+ applied: AppliedChange[];
66
+ skipped: Array<{
67
+ groupId: string;
68
+ artifactId: string;
69
+ newVersion: string;
70
+ reason: string;
71
+ }>;
72
+ };
73
+ /**
74
+ * A supported manifest type and the operations that act on it.
75
+ * `changeKey` builds a stable edit-site key from a single `applied` entry, so callers can detect
76
+ * inseparable remediations (same key => same commit/PR).
77
+ */
78
+ export type ManifestType = {
79
+ test: (basename: string) => boolean;
80
+ updater: (content: string, versionChanges: VersionChangeRequest[]) => UpdaterResult;
81
+ label: ("maven" | "toml");
82
+ changeKey: (manifestPath: string, applied: AppliedChange) => string;
83
+ };
84
+ /**
85
+ * An isolated, single-dependency edit to one manifest file.
86
+ * - `after` is the *original* manifest content with only this dependency's fix applied, so a caller
87
+ * can create an isolated commit by writing `after` to `path` on a branch cut from the base.
88
+ * - `changeKey` is a stable identifier for the underlying edit site. Two remediations that share a
89
+ * `changeKey` are inseparable (e.g. two Maven deps whose versions resolve to the same `${property}`,
90
+ * or two Gradle libraries sharing one `version.ref`) and MUST land in the same commit/PR — the
91
+ * caller should union their CVEs/advisories.
92
+ */
93
+ export type DependencyFix = {
94
+ path: string;
95
+ after: string;
96
+ changeKey: string;
97
+ };
98
+ /**
99
+ * A remediation grounded in the scanned workspace: the canonical
100
+ * {@link import ('./remediation.js').Remediation} base as produced by `extractRemediations`, enriched
101
+ * by `runRemediation` with the originating manifest path(s) in `files` (always present) and,
102
+ * optionally, the isolated per-dependency edits in `changes`.
103
+ */
104
+ export type AppliedRemediation = import("./remediation.js").Remediation & {
105
+ files: string[];
106
+ changes?: DependencyFix[];
107
+ };
@@ -3,34 +3,113 @@ import path from 'node:path';
3
3
  import analysis from './analysis.js';
4
4
  import { availableProviders, match } from './provider.js';
5
5
  import { extractRemediations } from './remediation.js';
6
- import { generateReport } from './remediation_report.js';
7
- import { updateMavenVersions } from './updaters/maven_updater.js';
8
- import { updateTomlVersions } from './updaters/toml_updater.js';
6
+ import { mavenChangeKey, updateMavenVersions } from './updaters/maven_updater.js';
7
+ import { tomlChangeKey, updateTomlVersions } from './updaters/toml_updater.js';
9
8
  import { selectTrustifyDABackend } from './index.js';
10
9
  // Mirrors DEFAULT_WORKSPACE_DISCOVERY_IGNORE in workspace.js
11
10
  const SKIP_DIRS = new Set(['node_modules', '.git']);
11
+ /**
12
+ * A single version change requested from an updater: bump `groupId:artifactId` to `newVersion`.
13
+ * The input side of every updater; each updater's output side (its `applied` entries) is its own
14
+ * type — see {@link AppliedChange}.
15
+ * @typedef {{groupId: string, artifactId: string, newVersion: string}} VersionChangeRequest
16
+ */
17
+ /**
18
+ * One entry from an updater's `applied` list, describing where and how a version was changed.
19
+ * Owned by the updater layer: the union of each updater's applied-entry shape. `changeKey`
20
+ * (per {@link ManifestType}) turns one of these into a stable edit-site identifier.
21
+ * @typedef {import('./updaters/maven_updater.js').MavenAppliedChange
22
+ * | import('./updaters/toml_updater.js').TomlAppliedChange} AppliedChange
23
+ */
24
+ /**
25
+ * The result of running an updater over a manifest's raw content.
26
+ * @typedef {{content: string, applied: AppliedChange[], skipped: Array<{groupId: string, artifactId: string, newVersion: string, reason: string}>}} UpdaterResult
27
+ */
28
+ /**
29
+ * A supported manifest type and the operations that act on it.
30
+ * `changeKey` builds a stable edit-site key from a single `applied` entry, so callers can detect
31
+ * inseparable remediations (same key => same commit/PR).
32
+ * @typedef {{
33
+ * test: (basename: string) => boolean,
34
+ * updater: (content: string, versionChanges: VersionChangeRequest[]) => UpdaterResult,
35
+ * label: ('maven'|'toml'),
36
+ * changeKey: (manifestPath: string, applied: AppliedChange) => string
37
+ * }} ManifestType
38
+ */
39
+ /**
40
+ * An isolated, single-dependency edit to one manifest file.
41
+ * - `after` is the *original* manifest content with only this dependency's fix applied, so a caller
42
+ * can create an isolated commit by writing `after` to `path` on a branch cut from the base.
43
+ * - `changeKey` is a stable identifier for the underlying edit site. Two remediations that share a
44
+ * `changeKey` are inseparable (e.g. two Maven deps whose versions resolve to the same `${property}`,
45
+ * or two Gradle libraries sharing one `version.ref`) and MUST land in the same commit/PR — the
46
+ * caller should union their CVEs/advisories.
47
+ * @typedef {{ path: string, after: string, changeKey: string }} DependencyFix
48
+ */
49
+ /**
50
+ * A remediation grounded in the scanned workspace: the canonical
51
+ * {@link import('./remediation.js').Remediation} base as produced by `extractRemediations`, enriched
52
+ * by `runRemediation` with the originating manifest path(s) in `files` (always present) and,
53
+ * optionally, the isolated per-dependency edits in `changes`.
54
+ * @typedef {import('./remediation.js').Remediation & {
55
+ * files: string[],
56
+ * changes?: DependencyFix[]
57
+ * }} AppliedRemediation
58
+ */
59
+ /** @type {ManifestType[]} */
12
60
  const MANIFEST_TYPES = [
13
61
  {
14
62
  test: (basename) => basename === 'pom.xml',
15
63
  updater: updateMavenVersions,
16
64
  label: 'maven',
65
+ changeKey: mavenChangeKey
17
66
  },
18
67
  {
19
68
  test: (basename) => basename.endsWith('.versions.toml') || basename === 'libs.versions.toml',
20
69
  updater: updateTomlVersions,
21
70
  label: 'toml',
71
+ changeKey: tomlChangeKey
22
72
  },
23
73
  ];
24
74
  /**
25
- * Discovers supported manifest files recursively within a directory.
26
- * @param {string} dirPath - absolute path to the directory
27
- * @returns {string[]} array of absolute paths to supported manifest files
75
+ * Returns the manifest type descriptor for a given filename, or null if unsupported.
76
+ * @param {string} basename - the file name to check
77
+ * @returns {ManifestType|null}
78
+ */
79
+ function getManifestType(basename) {
80
+ return MANIFEST_TYPES.find(t => t.test(basename)) || null;
81
+ }
82
+ /**
83
+ * Resolves a target path to the supported manifest files it contains: the single file
84
+ * if `targetPath` is a supported manifest, or every supported manifest discovered
85
+ * recursively if it's a directory. This is the single source of truth for what
86
+ * `runRemediation` will scan, so callers (e.g. the CLI) can reuse it to tell
87
+ * "no manifests here" apart from "manifests, but nothing to fix".
88
+ * @param {string} targetPath - path to a manifest file or directory
89
+ * @returns {string[]} absolute paths to supported manifests (empty for an empty directory)
90
+ * @throws if the path does not exist, or is a file of an unsupported manifest type
28
91
  */
29
- function discoverManifests(dirPath) {
92
+ export function findManifests(targetPath) {
93
+ const resolvedPath = path.resolve(targetPath);
94
+ let stat;
95
+ try {
96
+ stat = fs.statSync(resolvedPath);
97
+ }
98
+ catch {
99
+ throw new Error(`Path not found: ${resolvedPath}`);
100
+ }
101
+ // A single file must itself be a supported manifest.
102
+ if (!stat.isDirectory()) {
103
+ const basename = path.basename(resolvedPath);
104
+ if (!getManifestType(basename)) {
105
+ throw new Error(`Unsupported manifest type: ${basename}`);
106
+ }
107
+ return [resolvedPath];
108
+ }
109
+ // A directory yields every supported manifest found recursively beneath it.
30
110
  const manifests = [];
31
111
  function walk(dir) {
32
- const entries = fs.readdirSync(dir, { withFileTypes: true });
33
- for (const entry of entries) {
112
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
34
113
  if (SKIP_DIRS.has(entry.name)) {
35
114
  continue;
36
115
  }
@@ -43,17 +122,9 @@ function discoverManifests(dirPath) {
43
122
  }
44
123
  }
45
124
  }
46
- walk(dirPath);
125
+ walk(resolvedPath);
47
126
  return manifests;
48
127
  }
49
- /**
50
- * Returns the manifest type descriptor for a given filename, or null if unsupported.
51
- * @param {string} basename - the file name to check
52
- * @returns {{test: function, updater: function, label: string}|null}
53
- */
54
- function getManifestType(basename) {
55
- return MANIFEST_TYPES.find(t => t.test(basename)) || null;
56
- }
57
128
  /**
58
129
  * Orchestrates the full remediation pipeline for a single manifest or directory:
59
130
  * discover manifests → scan via DA backend → extract remediations → apply or preview.
@@ -63,38 +134,31 @@ function getManifestType(basename) {
63
134
  * @param {boolean} [options.dryRun=false] - preview changes without modifying files (applies by default)
64
135
  * @param {string} [options.providers] - comma-separated provider list
65
136
  * @param {string} [options.sources] - comma-separated source list
66
- * @param {'dependency'|'bundle'} [options.groupBy='dependency'] - report grouping strategy
67
- * @returns {Promise<{exitCode: number, output: string}>}
137
+ * @param {string} [options.backendUrl] - Trustify DA backend URL
138
+ * @param {boolean} [options.perDependencyChanges=false] - when true, each remediation is populated with
139
+ * a `changes` array describing the isolated, single-dependency edit (see {@link DependencyFix}). This lets
140
+ * callers create one commit/PR per dependency without attributing diff hunks themselves.
141
+ * @returns {Promise<{exitCode: number, remediations: AppliedRemediation[], manifests: string[], appliedFiles: string[]}>}
142
+ * exitCode is 2 for a dry-run that found remediations (nothing written), 0 otherwise. `remediations`
143
+ * is the structured, per-manifest list of applicable updates — each entry carries the originating
144
+ * manifest path(s) in `files` so callers can group and create per-dependency changes. `appliedFiles`
145
+ * lists only the manifests actually written to disk (empty on a dry-run), so callers can report a
146
+ * truthful "updated N files" count without conflating "had remediations" with "was written".
68
147
  */
69
148
  export async function runRemediation(targetPath, options = {}) {
70
- const { dryRun = false, providers, sources, groupBy = 'dependency' } = options;
71
- const resolvedPath = path.resolve(targetPath);
72
- let manifestPaths;
73
- let stat;
74
- try {
75
- stat = fs.statSync(resolvedPath);
76
- }
77
- catch {
78
- throw new Error(`Path not found: ${resolvedPath}`);
79
- }
80
- if (stat.isDirectory()) {
81
- manifestPaths = discoverManifests(resolvedPath);
82
- if (manifestPaths.length === 0) {
83
- return { exitCode: 0, output: 'No supported manifest files found.' };
84
- }
85
- }
86
- else {
87
- const basename = path.basename(resolvedPath);
88
- if (!getManifestType(basename)) {
89
- throw new Error(`Unsupported manifest type: ${basename}`);
90
- }
91
- manifestPaths = [resolvedPath];
149
+ const { dryRun = false, providers, sources, perDependencyChanges = false, backendUrl } = options;
150
+ const manifestPaths = findManifests(targetPath);
151
+ if (manifestPaths.length === 0) {
152
+ return { exitCode: 0, remediations: [], manifests: manifestPaths, appliedFiles: [] };
92
153
  }
93
154
  const opts = {};
94
- if (providers) {
155
+ if (backendUrl !== undefined) {
156
+ opts.TRUSTIFY_DA_BACKEND_URL = backendUrl;
157
+ }
158
+ if (providers !== undefined) {
95
159
  opts.TRUSTIFY_DA_PROVIDERS = providers;
96
160
  }
97
- if (sources) {
161
+ if (sources !== undefined) {
98
162
  opts.TRUSTIFY_DA_SOURCES = sources;
99
163
  }
100
164
  const url = selectTrustifyDABackend(opts);
@@ -120,15 +184,52 @@ export async function runRemediation(targetPath, options = {}) {
120
184
  if (remediations.length === 0) {
121
185
  continue;
122
186
  }
123
- allRemediations.push(...remediations);
187
+ // Tag each remediation with the manifest it came from so callers can group
188
+ // changes per dependency across a multi-manifest workspace.
189
+ for (const remediation of /** @type {AppliedRemediation[]} */ (remediations)) {
190
+ remediation.files = [manifestPath];
191
+ }
192
+ // Read the pristine manifest once. Both the atomic apply and the per-dependency
193
+ // change computation must diff against the *original* content.
194
+ const needsContent = perDependencyChanges || !dryRun;
195
+ const originalContent = needsContent ? fs.readFileSync(manifestPath, 'utf-8') : null;
196
+ if (perDependencyChanges) {
197
+ // With per-dependency changes every returned remediation must carry an isolated
198
+ // edit. A dependency the updater cannot locate in this manifest — e.g. a vulnerable
199
+ // *transitive* dependency surfaced by analysis but not declared here — yields no
200
+ // applied change, so it is dropped rather than returned with an absent `changes`
201
+ // array (which would crash callers that iterate `remediation.changes` to build PRs).
202
+ const applicable = [];
203
+ for (const remediation of remediations) {
204
+ const isolated = manifestType.updater(originalContent, [{
205
+ groupId: remediation.groupId,
206
+ artifactId: remediation.artifactId,
207
+ newVersion: remediation.fixedInVersion,
208
+ }]);
209
+ if (isolated.applied.length === 0) {
210
+ continue;
211
+ }
212
+ remediation.changes = [{
213
+ path: manifestPath,
214
+ after: isolated.content,
215
+ changeKey: manifestType.changeKey(manifestPath, isolated.applied[0])
216
+ }];
217
+ applicable.push(remediation);
218
+ }
219
+ allRemediations.push(...applicable);
220
+ }
221
+ else {
222
+ allRemediations.push(...remediations);
223
+ }
124
224
  if (!dryRun) {
125
- const content = fs.readFileSync(manifestPath, 'utf-8');
126
- const versionChanges = remediations.map(r => ({
225
+ // Disk receives the union of every dependency's fix. When
226
+ // perDependencyChanges is also set, each remediation's `changes[].after`
227
+ // deliberately stays isolated (single-dep) for branch-per-dep workflows.
228
+ const result = manifestType.updater(originalContent, remediations.map(r => ({
127
229
  groupId: r.groupId,
128
230
  artifactId: r.artifactId,
129
231
  newVersion: r.fixedInVersion,
130
- }));
131
- const result = manifestType.updater(content, versionChanges);
232
+ })));
132
233
  if (result.applied.length > 0) {
133
234
  fs.writeFileSync(manifestPath, result.content, 'utf-8');
134
235
  appliedFiles.push(manifestPath);
@@ -136,14 +237,8 @@ export async function runRemediation(targetPath, options = {}) {
136
237
  }
137
238
  }
138
239
  if (allRemediations.length === 0) {
139
- return { exitCode: 0, output: 'No remediations found.' };
140
- }
141
- const report = generateReport(allRemediations, { groupBy });
142
- if (dryRun) {
143
- return { exitCode: 2, output: report };
240
+ return { exitCode: 0, remediations: [], manifests: manifestPaths, appliedFiles };
144
241
  }
145
- const summary = appliedFiles.length > 0
146
- ? `Updated ${appliedFiles.length} file(s):\n${appliedFiles.map(f => ` ${f}`).join('\n')}\n\n${report}`
147
- : report;
148
- return { exitCode: 0, output: summary };
242
+ // Dry-run signals "changes available but not written" via exit code 2.
243
+ return { exitCode: dryRun ? 2 : 0, remediations: allRemediations, manifests: manifestPaths, appliedFiles };
149
244
  }
@@ -6,52 +6,76 @@
6
6
  * version selection strategy to resolve conflicts. Dependencies with no remediation
7
7
  * data are skipped.
8
8
  *
9
- * @param {object} analysisReport - raw DA AnalysisReport JSON response
9
+ * @param {import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport} analysisReport - raw DA AnalysisReport JSON response
10
10
  * @param {object} [options] - extraction options
11
11
  * @param {string[]} [options.providerPriority] - provider names in descending priority order.
12
12
  * The first entry has the highest priority. Providers not listed share the lowest priority.
13
13
  * When omitted or empty, all providers are treated equally and the highest fix version wins.
14
- * @param {object} [options.versionStrategy] - version selection strategy with selectVersion
14
+ * @param {VersionStrategy} [options.versionStrategy] - version selection strategy with selectVersion
15
15
  * and resolveConflict methods. Defaults to closestCoverageStrategy.
16
- * @returns {Array<{purl: string, groupId: string, artifactId: string, currentVersion: string, fixedInVersion: string, fixedInPurl: string, provider: string, source: string, advisories: Array<{id: string, url: string}>, severity: string, cves: string[]}>}
16
+ * @returns {Remediation[]} `vulnerabilities` is the sole source of vulnerability data — each entry
17
+ * holds one CVE with its own severity and advisories. Use {@link maxSeverity} to derive a
18
+ * dependency-level severity.
17
19
  */
18
- export function extractRemediations(analysisReport: object, options?: {
20
+ export function extractRemediations(analysisReport: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport, options?: {
19
21
  providerPriority?: string[] | undefined;
20
- versionStrategy?: object | undefined;
21
- }): Array<{
22
- purl: string;
23
- groupId: string;
24
- artifactId: string;
25
- currentVersion: string;
26
- fixedInVersion: string;
27
- fixedInPurl: string;
28
- provider: string;
29
- source: string;
30
- advisories: Array<{
31
- id: string;
32
- url: string;
33
- }>;
22
+ versionStrategy?: VersionStrategy | undefined;
23
+ }): Remediation[];
24
+ /**
25
+ * Derives a dependency-level severity as the max across a list of vulnerabilities.
26
+ * Returns 'UNKNOWN' for an empty or missing list.
27
+ * @param {Array<{severity: string}>} [vulnerabilities]
28
+ * @returns {string}
29
+ */
30
+ export function maxSeverity(vulnerabilities?: Array<{
34
31
  severity: string;
35
- cves: string[];
36
- }>;
32
+ }>): string;
33
+ /**
34
+ * @typedef {{selectVersion: (fixedInVersions: string[], currentVersion: string) => string, resolveConflict: (existing: ConflictCandidate, candidate: ConflictCandidate) => 'existing'|'candidate'}} VersionStrategy
35
+ */
37
36
  /**
38
37
  * Version selection strategy that prefers the closest compatible version
39
38
  * within the same major version stream. Falls back to the lowest cross-major
40
39
  * version when no same-major option exists.
41
- * @type {{selectVersion: function(string[], string): string, resolveConflict: function(object, object): string}}
40
+ * @type {VersionStrategy}
42
41
  */
43
- export const closestCoverageStrategy: {
44
- selectVersion: (arg0: string[], arg1: string) => string;
45
- resolveConflict: (arg0: object, arg1: object) => string;
46
- };
42
+ export const closestCoverageStrategy: VersionStrategy;
47
43
  /**
48
44
  * Version selection strategy that always picks the highest version regardless
49
45
  * of major version distance. Guarantees maximum CVE coverage but may produce
50
46
  * large version jumps. This is the original behavior before pluggable strategies.
51
- * @type {{selectVersion: function(string[], string): string, resolveConflict: function(object, object): string}}
47
+ * @type {VersionStrategy}
52
48
  */
53
- export const highestStrategy: {
54
- selectVersion: (arg0: string[], arg1: string) => string;
55
- resolveConflict: (arg0: object, arg1: object) => string;
56
- };
49
+ export const highestStrategy: VersionStrategy;
57
50
  export const SEVERITY_ORDER: string[];
51
+ /**
52
+ * A single per-CVE vulnerability carried by a remediation: one CVE with its own severity and the
53
+ * advisories attributed to it.
54
+ */
55
+ export type Vulnerability = {
56
+ id: string;
57
+ severity: string;
58
+ advisories: Array<{
59
+ id: string;
60
+ url: string;
61
+ }>;
62
+ };
63
+ /**
64
+ * A single applicable remediation as produced by {@link extractRemediations}: a dependency, the
65
+ * version that fixes it, and the per-CVE `vulnerabilities` it resolves.
66
+ */
67
+ export type Remediation = {
68
+ purl: string;
69
+ groupId: string;
70
+ artifactId: string;
71
+ currentVersion: string;
72
+ fixedInVersion: string;
73
+ fixedInPurl: string;
74
+ provider: string;
75
+ source: string;
76
+ vulnerabilities: Vulnerability[];
77
+ };
78
+ export type VersionStrategy = {
79
+ selectVersion: (fixedInVersions: string[], currentVersion: string) => string;
80
+ resolveConflict: (existing: ConflictCandidate, candidate: ConflictCandidate) => "existing" | "candidate";
81
+ };