@trustify-da/trustify-da-javascript-client 0.3.0-ea.e660b02 → 0.3.0-ea.efdc87d

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 (77) hide show
  1. package/README.md +199 -11
  2. package/dist/package.json +29 -25
  3. package/dist/src/analysis.d.ts +50 -22
  4. package/dist/src/analysis.js +70 -18
  5. package/dist/src/cli.js +188 -6
  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 +27 -22
  11. package/dist/src/index.js +27 -38
  12. package/dist/src/license/index.d.ts +4 -4
  13. package/dist/src/license/index.js +13 -7
  14. package/dist/src/license/license_utils.js +4 -1
  15. package/dist/src/license/licenses_api.d.ts +3 -3
  16. package/dist/src/license/licenses_api.js +1 -1
  17. package/dist/src/oci_image/images.d.ts +1 -1
  18. package/dist/src/oci_image/images.js +15 -1
  19. package/dist/src/oci_image/utils.d.ts +5 -5
  20. package/dist/src/package_version.d.ts +8 -0
  21. package/dist/src/package_version.js +31 -0
  22. package/dist/src/provider.d.ts +2 -2
  23. package/dist/src/provider.js +3 -1
  24. package/dist/src/providers/base_java.d.ts +55 -2
  25. package/dist/src/providers/base_java.js +64 -17
  26. package/dist/src/providers/base_javascript.d.ts +63 -9
  27. package/dist/src/providers/base_javascript.js +106 -4
  28. package/dist/src/providers/base_pyproject.d.ts +5 -5
  29. package/dist/src/providers/containerfile_parser.d.ts +5 -0
  30. package/dist/src/providers/containerfile_parser.js +20 -0
  31. package/dist/src/providers/golang_gomodules.d.ts +1 -1
  32. package/dist/src/providers/golang_gomodules.js +49 -7
  33. package/dist/src/providers/java_gradle.d.ts +48 -0
  34. package/dist/src/providers/java_gradle.js +196 -18
  35. package/dist/src/providers/java_gradle_groovy.d.ts +1 -1
  36. package/dist/src/providers/java_gradle_kotlin.d.ts +1 -1
  37. package/dist/src/providers/java_maven.d.ts +29 -9
  38. package/dist/src/providers/java_maven.js +104 -10
  39. package/dist/src/providers/javascript_bun.d.ts +12 -0
  40. package/dist/src/providers/javascript_bun.js +42 -1
  41. package/dist/src/providers/javascript_npm.d.ts +19 -1
  42. package/dist/src/providers/javascript_npm.js +40 -1
  43. package/dist/src/providers/javascript_pnpm.d.ts +13 -0
  44. package/dist/src/providers/javascript_pnpm.js +47 -1
  45. package/dist/src/providers/javascript_yarn.d.ts +12 -0
  46. package/dist/src/providers/javascript_yarn.js +75 -2
  47. package/dist/src/providers/manifest.js +6 -3
  48. package/dist/src/providers/oci_dockerfile.d.ts +51 -0
  49. package/dist/src/providers/oci_dockerfile.js +177 -0
  50. package/dist/src/providers/processors/yarn_berry_processor.d.ts +6 -2
  51. package/dist/src/providers/processors/yarn_berry_processor.js +3 -2
  52. package/dist/src/providers/processors/yarn_classic_processor.d.ts +6 -2
  53. package/dist/src/providers/processors/yarn_classic_processor.js +8 -6
  54. package/dist/src/providers/python_controller.js +4 -1
  55. package/dist/src/providers/python_poetry.d.ts +1 -1
  56. package/dist/src/providers/python_uv.d.ts +1 -1
  57. package/dist/src/providers/requirements_parser.js +1 -1
  58. package/dist/src/providers/rust_cargo.d.ts +1 -1
  59. package/dist/src/providers/rust_cargo.js +70 -29
  60. package/dist/src/providers/tree-sitter-containerfile.wasm +0 -0
  61. package/dist/src/remediate.d.ts +120 -0
  62. package/dist/src/remediate.js +253 -0
  63. package/dist/src/remediation.d.ts +58 -0
  64. package/dist/src/remediation.js +425 -0
  65. package/dist/src/remediation_report.d.ts +40 -0
  66. package/dist/src/remediation_report.js +159 -0
  67. package/dist/src/sbom.d.ts +11 -0
  68. package/dist/src/sbom.js +10 -0
  69. package/dist/src/tools.d.ts +11 -13
  70. package/dist/src/tools.js +34 -13
  71. package/dist/src/updaters/maven_updater.d.ts +58 -0
  72. package/dist/src/updaters/maven_updater.js +345 -0
  73. package/dist/src/updaters/toml_updater.d.ts +62 -0
  74. package/dist/src/updaters/toml_updater.js +276 -0
  75. package/dist/src/workspace.d.ts +2 -1
  76. package/dist/src/workspace.js +2 -1
  77. package/package.json +30 -26
@@ -50,8 +50,9 @@ export default class Yarn_classic_processor extends Yarn_processor {
50
50
  * Adds dependencies to the SBOM
51
51
  * @param {Sbom} sbom - The SBOM object to add dependencies to
52
52
  * @param {Object} depTree - The dependency tree object
53
+ * @param {function(PackageURL): (Array<{alg: string, content: string}>|undefined)} [hashesForPurl] - Resolves lock-file hashes for a purl
53
54
  */
54
- addDependenciesToSbom(sbom, depTree) {
55
+ addDependenciesToSbom(sbom, depTree, hashesForPurl = () => undefined) {
55
56
  if (!depTree?.data?.trees) {
56
57
  return;
57
58
  }
@@ -60,12 +61,12 @@ export default class Yarn_classic_processor extends Yarn_processor {
60
61
  depTree.data.trees.forEach(n => {
61
62
  const dep = new NodeMetaData(n);
62
63
  if (this._manifest.dependencies.includes(dep.name)) {
63
- sbom.addDependency(rootPurl, dep.purl);
64
+ sbom.addDependency(rootPurl, dep.purl, undefined, hashesForPurl(dep.purl));
64
65
  }
65
66
  purls.set(dep.name, dep.purl);
66
67
  });
67
68
  depTree.data.trees.forEach(n => {
68
- this.#addChildrenToSbom(sbom, n, purls);
69
+ this.#addChildrenToSbom(sbom, n, purls, hashesForPurl);
69
70
  });
70
71
  }
71
72
  /**
@@ -73,9 +74,10 @@ export default class Yarn_classic_processor extends Yarn_processor {
73
74
  * @param {Sbom} sbom - The SBOM object to add dependencies to
74
75
  * @param {Object} node - The current dependency node
75
76
  * @param {Map<string, PackageURL>} purls - Map of dependency names to their PackageURL objects
77
+ * @param {function(PackageURL): (Array<{alg: string, content: string}>|undefined)} hashesForPurl - Resolves lock-file hashes for a purl
76
78
  * @private
77
79
  */
78
- #addChildrenToSbom(sbom, node, purls) {
80
+ #addChildrenToSbom(sbom, node, purls, hashesForPurl) {
79
81
  const dep = new NodeMetaData(node);
80
82
  const children = node.children ? node.children : [];
81
83
  children.forEach(c => {
@@ -83,9 +85,9 @@ export default class Yarn_classic_processor extends Yarn_processor {
83
85
  const from = dep.shadow ? purls.get(dep.name) : dep.purl;
84
86
  const to = child.shadow ? purls.get(child.name) : child.purl;
85
87
  if (from && to) {
86
- sbom.addDependency(from, to);
88
+ sbom.addDependency(from, to, undefined, hashesForPurl(to));
87
89
  }
88
- this.#addChildrenToSbom(sbom, c, purls);
90
+ this.#addChildrenToSbom(sbom, c, purls, hashesForPurl);
89
91
  });
90
92
  }
91
93
  }
@@ -178,8 +178,11 @@ export default class Python_controller {
178
178
  startingTime = new Date();
179
179
  console.log("Starting time to get requirements.txt dependency tree = " + startingTime);
180
180
  }
181
+ let installBestEfforts = getCustom("TRUSTIFY_DA_PYTHON_INSTALL_BEST_EFFORTS", "false", this.options);
182
+ if (installBestEfforts === "true" && this.realEnvironment) {
183
+ throw new Error("Conflicting settings, TRUSTIFY_DA_PYTHON_INSTALL_BEST_EFFORTS=true requires TRUSTIFY_DA_PYTHON_VIRTUAL_ENV=true");
184
+ }
181
185
  if (!this.realEnvironment) {
182
- let installBestEfforts = getCustom("TRUSTIFY_DA_PYTHON_INSTALL_BEST_EFFORTS", "false", this.options);
183
186
  if (installBestEfforts === "false") {
184
187
  try {
185
188
  invokeCommand(this.pathToPipBin, ['install', '-r', this.pathToRequirements]);
@@ -7,7 +7,7 @@ export default class Python_poetry extends Base_pyproject {
7
7
  * @param {Object} opts
8
8
  * @returns {Promise<{directDeps: string[], graph: Map<string, {name: string, version: string, children: string[]}>}>}
9
9
  */
10
- _getDependencyData(manifestDir: string, _workspaceDir: string, parsed: object, opts: any): Promise<{
10
+ _getDependencyData(manifestDir: string, _workspaceDir: string, parsed: object, opts: Object): Promise<{
11
11
  directDeps: string[];
12
12
  graph: Map<string, {
13
13
  name: string;
@@ -24,7 +24,7 @@ export default class Python_uv extends Base_pyproject {
24
24
  * @param {Object} opts
25
25
  * @returns {string}
26
26
  */
27
- _getUvExportOutput(manifestDir: string, opts: any): string;
27
+ _getUvExportOutput(manifestDir: string, opts: Object): string;
28
28
  /**
29
29
  * Parse uv export output into a dependency graph using tree-sitter-requirements
30
30
  * for package/version extraction and string parsing for "# via" comments.
@@ -16,7 +16,7 @@ export async function getRequirementQuery() {
16
16
  }
17
17
  export async function getIgnoreQuery() {
18
18
  const language = await init();
19
- return new Query(language, '((requirement (package) @name) @req . (comment) @comment (#match? @comment "^#[\\t ]*exhortignore"))');
19
+ return new Query(language, '((requirement (package) @name) @req . (comment) @comment (#match? @comment "^#[\\t ]*(exhortignore|trustify-da-ignore)(?![\\w-])"))');
20
20
  }
21
21
  export async function getPinnedVersionQuery() {
22
22
  const language = await init();
@@ -7,7 +7,7 @@ declare namespace _default {
7
7
  export function packageManagerName(): string;
8
8
  }
9
9
  export default _default;
10
- export type Provided = import("../provider").Provided;
10
+ export type Provided = any;
11
11
  /**
12
12
  * @param {string} manifestName - the subject manifest name-type
13
13
  * @returns {boolean} - return true if `Cargo.toml` is the manifest name-type
@@ -116,30 +116,25 @@ function resolveWorkspaceLicense(parsed, metadata) {
116
116
  return parsed.workspace?.package?.license || null;
117
117
  }
118
118
  /**
119
- * Validates that Cargo.lock exists in the manifest directory or in a parent
120
- * workspace root directory. In Cargo workspaces the lock file always lives at
121
- * the workspace root, so when a member crate's Cargo.toml is provided we walk
122
- * up the directory tree looking for Cargo.lock (stopping when we find a
123
- * Cargo.toml that contains a [workspace] section, or when we reach the
124
- * filesystem root).
125
- * When TRUSTIFY_DA_WORKSPACE_DIR is provided (via env var or opts),
126
- * checks only that directory for Cargo.lock — no walk-up.
119
+ * Finds the directory containing Cargo.lock, honoring TRUSTIFY_DA_WORKSPACE_DIR.
120
+ * Walks up from manifestDir looking for Cargo.lock, stopping at workspace root or filesystem root.
127
121
  * @param {string} manifestDir - the directory where the manifest lies
128
122
  * @param {{TRUSTIFY_DA_WORKSPACE_DIR?: string}} [opts={}] - optional workspace root
129
- * @returns {boolean} true if Cargo.lock is found
123
+ * @returns {string|null} the directory path containing Cargo.lock, or null if not found
124
+ * @private
130
125
  */
131
- function validateLockFile(manifestDir, opts = {}) {
126
+ function _findCargoLockDir(manifestDir, opts = {}) {
132
127
  const workspaceDir = getCustom('TRUSTIFY_DA_WORKSPACE_DIR', null, opts);
133
128
  if (workspaceDir) {
134
129
  const dir = path.resolve(workspaceDir);
135
- return fs.existsSync(path.join(dir, 'Cargo.lock'));
130
+ return fs.existsSync(path.join(dir, 'Cargo.lock')) ? dir : null;
136
131
  }
137
132
  let dir = path.resolve(manifestDir);
138
133
  let parent = dir;
139
134
  do {
140
135
  dir = parent;
141
136
  if (fs.existsSync(path.join(dir, 'Cargo.lock'))) {
142
- return true;
137
+ return dir;
143
138
  }
144
139
  // If this directory has a Cargo.toml with [workspace], the lock file
145
140
  // should have been here — stop searching.
@@ -148,7 +143,7 @@ function validateLockFile(manifestDir, opts = {}) {
148
143
  try {
149
144
  let content = fs.readFileSync(cargoToml, 'utf-8');
150
145
  if (/\[workspace\]/.test(content)) {
151
- return false;
146
+ return null;
152
147
  }
153
148
  }
154
149
  catch (_) {
@@ -157,7 +152,23 @@ function validateLockFile(manifestDir, opts = {}) {
157
152
  }
158
153
  parent = path.dirname(dir);
159
154
  } while (parent !== dir);
160
- return false;
155
+ return null;
156
+ }
157
+ /**
158
+ * Validates that Cargo.lock exists in the manifest directory or in a parent
159
+ * workspace root directory. In Cargo workspaces the lock file always lives at
160
+ * the workspace root, so when a member crate's Cargo.toml is provided we walk
161
+ * up the directory tree looking for Cargo.lock (stopping when we find a
162
+ * Cargo.toml that contains a [workspace] section, or when we reach the
163
+ * filesystem root).
164
+ * When TRUSTIFY_DA_WORKSPACE_DIR is provided (via env var or opts),
165
+ * checks only that directory for Cargo.lock — no walk-up.
166
+ * @param {string} manifestDir - the directory where the manifest lies
167
+ * @param {{TRUSTIFY_DA_WORKSPACE_DIR?: string}} [opts={}] - optional workspace root
168
+ * @returns {boolean} true if Cargo.lock is found
169
+ */
170
+ function validateLockFile(manifestDir, opts = {}) {
171
+ return _findCargoLockDir(manifestDir, opts) !== null;
161
172
  }
162
173
  /**
163
174
  * Provide content and content type for Cargo stack analysis.
@@ -201,12 +212,13 @@ function getSBOM(manifest, opts = {}, includeTransitive) {
201
212
  let ignoredDeps = getIgnoredDeps(manifest, metadata);
202
213
  let crateType = detectCrateType(metadata);
203
214
  let license = readLicenseFromManifest(manifest, metadata);
215
+ let hashMap = parseCargoLockHashes(manifestDir, opts);
204
216
  let sbom;
205
217
  if (crateType === CrateType.WORKSPACE_VIRTUAL) {
206
- sbom = handleVirtualWorkspace(manifest, metadata, ignoredDeps, includeTransitive, opts, license);
218
+ sbom = handleVirtualWorkspace(manifest, metadata, ignoredDeps, includeTransitive, opts, license, hashMap);
207
219
  }
208
220
  else {
209
- sbom = handleSingleCrate(metadata, ignoredDeps, includeTransitive, opts, license);
221
+ sbom = handleSingleCrate(metadata, ignoredDeps, includeTransitive, opts, license, hashMap);
210
222
  }
211
223
  return sbom;
212
224
  }
@@ -244,6 +256,34 @@ function executeCargoMetadata(cargoBin, manifestDir) {
244
256
  throw new Error('failed to execute cargo metadata', { cause: error });
245
257
  }
246
258
  }
259
+ /**
260
+ * Parses Cargo.lock to extract SHA-256 checksums for each package.
261
+ * @param {string} manifestDir - directory containing Cargo.toml (Cargo.lock is searched upward)
262
+ * @param {{TRUSTIFY_DA_WORKSPACE_DIR?: string}} [opts={}] - optional workspace root
263
+ * @returns {Map<string, Array<{alg: string, content: string}>>} map of "name@version" to CycloneDX hashes
264
+ * @private
265
+ */
266
+ function parseCargoLockHashes(manifestDir, opts = {}) {
267
+ let hashMap = new Map();
268
+ let lockDir = _findCargoLockDir(manifestDir, opts);
269
+ if (!lockDir) {
270
+ return hashMap;
271
+ }
272
+ let lockPath = path.join(lockDir, 'Cargo.lock');
273
+ try {
274
+ let content = fs.readFileSync(lockPath, 'utf-8');
275
+ let parsed = parseToml(content);
276
+ for (let pkg of (parsed.package || [])) {
277
+ if (pkg.name && pkg.version && pkg.checksum) {
278
+ hashMap.set(`${pkg.name}@${pkg.version}`, [{ alg: 'SHA-256', content: pkg.checksum }]);
279
+ }
280
+ }
281
+ }
282
+ catch (error) {
283
+ console.warn(`Failed to parse Cargo.lock at ${lockPath} for hashes: ${error.message}. SBOM will be generated without hashes.`);
284
+ }
285
+ return hashMap;
286
+ }
247
287
  /**
248
288
  * Detects the type of Cargo project from metadata.
249
289
  * @param {object} metadata - parsed cargo metadata
@@ -275,7 +315,7 @@ function detectCrateType(metadata) {
275
315
  * @returns {string} SBOM json string
276
316
  * @private
277
317
  */
278
- function handleSingleCrate(metadata, ignoredDeps, includeTransitive, opts, license) {
318
+ function handleSingleCrate(metadata, ignoredDeps, includeTransitive, opts, license, hashMap) {
279
319
  let rootPackageId = metadata.resolve.root;
280
320
  let rootPackage = findPackageById(metadata, rootPackageId);
281
321
  let rootPurl = toPurl(rootPackage.name, rootPackage.version);
@@ -286,10 +326,10 @@ function handleSingleCrate(metadata, ignoredDeps, includeTransitive, opts, licen
286
326
  return sbom.getAsJsonString(opts);
287
327
  }
288
328
  if (includeTransitive) {
289
- addTransitiveDeps(sbom, metadata, rootPackageId, ignoredDeps, new Set(), rootPurl);
329
+ addTransitiveDeps(sbom, metadata, rootPackageId, ignoredDeps, new Set(), hashMap, rootPurl);
290
330
  }
291
331
  else {
292
- addDirectDeps(sbom, metadata, rootPackageId, rootPurl, ignoredDeps);
332
+ addDirectDeps(sbom, metadata, rootPackageId, rootPurl, ignoredDeps, hashMap);
293
333
  }
294
334
  return sbom.getAsJsonString(opts);
295
335
  }
@@ -315,7 +355,7 @@ function handleSingleCrate(metadata, ignoredDeps, includeTransitive, opts, licen
315
355
  * @returns {string} SBOM json string
316
356
  * @private
317
357
  */
318
- function handleVirtualWorkspace(manifest, metadata, ignoredDeps, includeTransitive, opts, license) {
358
+ function handleVirtualWorkspace(manifest, metadata, ignoredDeps, includeTransitive, opts, license, hashMap) {
319
359
  let workspaceRoot = metadata.workspace_root;
320
360
  let rootName = path.basename(workspaceRoot);
321
361
  let workspaceVersion = getWorkspaceVersion(metadata);
@@ -323,7 +363,6 @@ function handleVirtualWorkspace(manifest, metadata, ignoredDeps, includeTransiti
323
363
  let sbom = new Sbom();
324
364
  sbom.addRoot(rootPurl, license);
325
365
  if (includeTransitive) {
326
- // Stack analysis: walk all members and their full dependency trees
327
366
  let workspaceMembers = metadata.workspace_members || [];
328
367
  for (let memberId of workspaceMembers) {
329
368
  let memberPackage = findPackageById(metadata, memberId);
@@ -334,11 +373,10 @@ function handleVirtualWorkspace(manifest, metadata, ignoredDeps, includeTransiti
334
373
  ? toPathDepPurl(memberPackage.name, memberPackage.version)
335
374
  : toPurl(memberPackage.name, memberPackage.version);
336
375
  sbom.addDependency(rootPurl, memberPurl);
337
- addTransitiveDeps(sbom, metadata, memberId, ignoredDeps, new Set(), memberPurl);
376
+ addTransitiveDeps(sbom, metadata, memberId, ignoredDeps, new Set(), hashMap, memberPurl);
338
377
  }
339
378
  }
340
379
  else {
341
- // Component analysis: only [workspace.dependencies] from root Cargo.toml
342
380
  let workspaceDeps = getWorkspaceDepsFromManifest(manifest);
343
381
  for (let depName of workspaceDeps) {
344
382
  if (isDepIgnored(depName, ignoredDeps)) {
@@ -355,7 +393,8 @@ function handleVirtualWorkspace(manifest, metadata, ignoredDeps, includeTransiti
355
393
  let depPurl = pkg.source == null
356
394
  ? toPathDepPurl(pkg.name, pkg.version)
357
395
  : toPurl(pkg.name, pkg.version);
358
- sbom.addDependency(rootPurl, depPurl);
396
+ let hashes = hashMap.get(`${pkg.name}@${pkg.version}`);
397
+ sbom.addDependency(rootPurl, depPurl, undefined, hashes);
359
398
  }
360
399
  }
361
400
  return sbom.getAsJsonString(opts);
@@ -374,7 +413,7 @@ function handleVirtualWorkspace(manifest, metadata, ignoredDeps, includeTransiti
374
413
  * so callers can ensure it matches the purl already added to the SBOM
375
414
  * @private
376
415
  */
377
- function addTransitiveDeps(sbom, metadata, packageId, ignoredDeps, visited, startingPurl) {
416
+ function addTransitiveDeps(sbom, metadata, packageId, ignoredDeps, visited, hashMap, startingPurl) {
378
417
  if (visited.has(packageId)) {
379
418
  return;
380
419
  }
@@ -402,8 +441,9 @@ function addTransitiveDeps(sbom, metadata, packageId, ignoredDeps, visited, star
402
441
  let depPurl = depPackage.source == null
403
442
  ? toPathDepPurl(depPackage.name, depPackage.version)
404
443
  : toPurl(depPackage.name, depPackage.version);
405
- sbom.addDependency(sourcePurl, depPurl);
406
- addTransitiveDeps(sbom, metadata, depId, ignoredDeps, visited);
444
+ let hashes = hashMap.get(`${depPackage.name}@${depPackage.version}`);
445
+ sbom.addDependency(sourcePurl, depPurl, undefined, hashes);
446
+ addTransitiveDeps(sbom, metadata, depId, ignoredDeps, visited, hashMap);
407
447
  }
408
448
  }
409
449
  /**
@@ -416,7 +456,7 @@ function addTransitiveDeps(sbom, metadata, packageId, ignoredDeps, visited, star
416
456
  * @param {Set<string>} ignoredDeps - set of ignored dependency names
417
457
  * @private
418
458
  */
419
- function addDirectDeps(sbom, metadata, packageId, parentPurl, ignoredDeps) {
459
+ function addDirectDeps(sbom, metadata, packageId, parentPurl, ignoredDeps, hashMap) {
420
460
  let resolveNode = findResolveNode(metadata, packageId);
421
461
  if (!resolveNode) {
422
462
  return;
@@ -433,7 +473,8 @@ function addDirectDeps(sbom, metadata, packageId, parentPurl, ignoredDeps) {
433
473
  let depPurl = depPackage.source == null
434
474
  ? toPathDepPurl(depPackage.name, depPackage.version)
435
475
  : toPurl(depPackage.name, depPackage.version);
436
- sbom.addDependency(parentPurl, depPurl);
476
+ let hashes = hashMap.get(`${depPackage.name}@${depPackage.version}`);
477
+ sbom.addDependency(parentPurl, depPurl, undefined, hashes);
437
478
  }
438
479
  }
439
480
  /**
@@ -0,0 +1,120 @@
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[];
12
+ /**
13
+ * Orchestrates the full remediation pipeline for a single manifest or directory:
14
+ * discover manifests → scan via DA backend → extract remediations → apply or preview.
15
+ *
16
+ * @param {string} targetPath - path to a manifest file or directory
17
+ * @param {object} [options]
18
+ * @param {boolean} [options.dryRun=false] - preview changes without modifying files (applies by default)
19
+ * @param {string} [options.providers] - comma-separated provider list
20
+ * @param {string} [options.sources] - comma-separated source list
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, output: string, remediations: Remediation[], 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".
31
+ */
32
+ export function runRemediation(targetPath: string, options?: {
33
+ dryRun?: boolean | undefined;
34
+ providers?: string | undefined;
35
+ sources?: string | undefined;
36
+ backendUrl?: string | undefined;
37
+ perDependencyChanges?: boolean | undefined;
38
+ }): Promise<{
39
+ exitCode: number;
40
+ output: string;
41
+ remediations: Remediation[];
42
+ manifests: string[];
43
+ appliedFiles: string[];
44
+ }>;
45
+ /**
46
+ * A single version change requested from an updater: bump `groupId:artifactId` to `newVersion`.
47
+ * The input side of every updater; each updater's output side (its `applied` entries) is its own
48
+ * type — see {@link AppliedChange}.
49
+ */
50
+ export type VersionChangeRequest = {
51
+ groupId: string;
52
+ artifactId: string;
53
+ newVersion: string;
54
+ };
55
+ /**
56
+ * One entry from an updater's `applied` list, describing where and how a version was changed.
57
+ * Owned by the updater layer: the union of each updater's applied-entry shape. `changeKey`
58
+ * (per {@link ManifestType}) turns one of these into a stable edit-site identifier.
59
+ */
60
+ export type AppliedChange = import("./updaters/maven_updater.js").MavenAppliedChange | import("./updaters/toml_updater.js").TomlAppliedChange;
61
+ /**
62
+ * The result of running an updater over a manifest's raw content.
63
+ */
64
+ export type UpdaterResult = {
65
+ content: string;
66
+ applied: AppliedChange[];
67
+ skipped: Array<{
68
+ groupId: string;
69
+ artifactId: string;
70
+ newVersion: string;
71
+ reason: string;
72
+ }>;
73
+ };
74
+ /**
75
+ * A supported manifest type and the operations that act on it.
76
+ * `changeKey` builds a stable edit-site key from a single `applied` entry, so callers can detect
77
+ * inseparable remediations (same key => same commit/PR).
78
+ */
79
+ export type ManifestType = {
80
+ test: (basename: string) => boolean;
81
+ updater: (content: string, versionChanges: VersionChangeRequest[]) => UpdaterResult;
82
+ label: ("maven" | "toml");
83
+ changeKey: (manifestPath: string, applied: AppliedChange) => string;
84
+ };
85
+ /**
86
+ * An isolated, single-dependency edit to one manifest file.
87
+ * - `after` is the *original* manifest content with only this dependency's fix applied, so a caller
88
+ * can create an isolated commit by writing `after` to `path` on a branch cut from the base.
89
+ * - `changeKey` is a stable identifier for the underlying edit site. Two remediations that share a
90
+ * `changeKey` are inseparable (e.g. two Maven deps whose versions resolve to the same `${property}`,
91
+ * or two Gradle libraries sharing one `version.ref`) and MUST land in the same commit/PR — the
92
+ * caller should union their CVEs/advisories.
93
+ */
94
+ export type DependencyFix = {
95
+ path: string;
96
+ after: string;
97
+ changeKey: string;
98
+ };
99
+ /**
100
+ * A single applicable remediation, as produced by `extractRemediations` and enriched by
101
+ * `runRemediation` with the originating manifest path(s) and (optionally) per-dependency changes.
102
+ */
103
+ export type Remediation = {
104
+ purl: string;
105
+ groupId: string;
106
+ artifactId: string;
107
+ currentVersion: string;
108
+ fixedInVersion: string;
109
+ fixedInPurl: string;
110
+ provider: string;
111
+ source: string;
112
+ advisories: Array<{
113
+ id: string;
114
+ url: string;
115
+ }>;
116
+ severity: string;
117
+ cves: string[];
118
+ files: string[];
119
+ changes?: DependencyFix[];
120
+ };