@trustify-da/trustify-da-javascript-client 0.3.0-ea.6645ba5 → 0.3.0-ea.68905d3

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 (73) hide show
  1. package/README.md +189 -3
  2. package/dist/package.json +29 -25
  3. package/dist/src/analysis.d.ts +14 -7
  4. package/dist/src/analysis.js +29 -13
  5. package/dist/src/cli.js +117 -2
  6. package/dist/src/cyclone_dx_sbom.d.ts +13 -0
  7. package/dist/src/cyclone_dx_sbom.js +39 -1
  8. package/dist/src/index.d.ts +15 -10
  9. package/dist/src/index.js +13 -25
  10. package/dist/src/license/index.d.ts +3 -3
  11. package/dist/src/license/index.js +4 -1
  12. package/dist/src/license/license_utils.js +4 -1
  13. package/dist/src/license/licenses_api.d.ts +2 -2
  14. package/dist/src/oci_image/images.d.ts +1 -1
  15. package/dist/src/oci_image/images.js +15 -1
  16. package/dist/src/oci_image/utils.d.ts +5 -5
  17. package/dist/src/package_version.d.ts +8 -0
  18. package/dist/src/package_version.js +31 -0
  19. package/dist/src/provider.d.ts +2 -2
  20. package/dist/src/provider.js +3 -1
  21. package/dist/src/providers/base_java.d.ts +55 -2
  22. package/dist/src/providers/base_java.js +64 -17
  23. package/dist/src/providers/base_javascript.d.ts +63 -9
  24. package/dist/src/providers/base_javascript.js +105 -3
  25. package/dist/src/providers/base_pyproject.d.ts +5 -5
  26. package/dist/src/providers/containerfile_parser.d.ts +5 -0
  27. package/dist/src/providers/containerfile_parser.js +20 -0
  28. package/dist/src/providers/golang_gomodules.d.ts +1 -1
  29. package/dist/src/providers/golang_gomodules.js +49 -7
  30. package/dist/src/providers/java_gradle.d.ts +48 -0
  31. package/dist/src/providers/java_gradle.js +196 -18
  32. package/dist/src/providers/java_gradle_groovy.d.ts +1 -1
  33. package/dist/src/providers/java_gradle_kotlin.d.ts +1 -1
  34. package/dist/src/providers/java_maven.d.ts +29 -9
  35. package/dist/src/providers/java_maven.js +104 -10
  36. package/dist/src/providers/javascript_bun.d.ts +12 -0
  37. package/dist/src/providers/javascript_bun.js +42 -1
  38. package/dist/src/providers/javascript_npm.d.ts +19 -1
  39. package/dist/src/providers/javascript_npm.js +40 -1
  40. package/dist/src/providers/javascript_pnpm.d.ts +13 -0
  41. package/dist/src/providers/javascript_pnpm.js +47 -1
  42. package/dist/src/providers/javascript_yarn.d.ts +12 -0
  43. package/dist/src/providers/javascript_yarn.js +75 -2
  44. package/dist/src/providers/manifest.js +6 -3
  45. package/dist/src/providers/oci_dockerfile.d.ts +51 -0
  46. package/dist/src/providers/oci_dockerfile.js +177 -0
  47. package/dist/src/providers/processors/yarn_berry_processor.d.ts +6 -2
  48. package/dist/src/providers/processors/yarn_berry_processor.js +3 -2
  49. package/dist/src/providers/processors/yarn_classic_processor.d.ts +6 -2
  50. package/dist/src/providers/processors/yarn_classic_processor.js +8 -6
  51. package/dist/src/providers/python_controller.js +4 -1
  52. package/dist/src/providers/python_poetry.d.ts +6 -5
  53. package/dist/src/providers/python_poetry.js +23 -8
  54. package/dist/src/providers/python_uv.d.ts +1 -1
  55. package/dist/src/providers/requirements_parser.js +1 -1
  56. package/dist/src/providers/rust_cargo.d.ts +1 -1
  57. package/dist/src/providers/rust_cargo.js +70 -29
  58. package/dist/src/providers/tree-sitter-containerfile.wasm +0 -0
  59. package/dist/src/remediate.d.ts +117 -0
  60. package/dist/src/remediate.js +249 -0
  61. package/dist/src/remediation.d.ts +57 -0
  62. package/dist/src/remediation.js +436 -0
  63. package/dist/src/remediation_report.d.ts +40 -0
  64. package/dist/src/remediation_report.js +159 -0
  65. package/dist/src/sbom.d.ts +11 -0
  66. package/dist/src/sbom.js +10 -0
  67. package/dist/src/tools.d.ts +2 -2
  68. package/dist/src/tools.js +24 -1
  69. package/dist/src/updaters/maven_updater.d.ts +58 -0
  70. package/dist/src/updaters/maven_updater.js +345 -0
  71. package/dist/src/updaters/toml_updater.d.ts +62 -0
  72. package/dist/src/updaters/toml_updater.js +276 -0
  73. package/package.json +30 -26
package/dist/src/cli.js CHANGED
@@ -4,6 +4,8 @@ import * as path from "path";
4
4
  import yargs from 'yargs';
5
5
  import { hideBin } from 'yargs/helpers';
6
6
  import { getProjectLicense, getLicenseDetails } from './license/index.js';
7
+ import { runRemediation } from './remediate.js';
8
+ import { generateReport } from './remediation_report.js';
7
9
  import client, { selectTrustifyDABackend, generateSbom } from './index.js';
8
10
  // command for component analysis take manifest type and content
9
11
  const component = {
@@ -19,11 +21,25 @@ const component = {
19
21
  desc: 'Workspace root directory (for monorepos; lock file is expected here)',
20
22
  type: 'string',
21
23
  normalize: true,
24
+ },
25
+ providers: {
26
+ desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
27
+ type: 'string',
28
+ },
29
+ sources: {
30
+ desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
31
+ type: 'string',
22
32
  }
23
33
  }),
24
34
  handler: async (args) => {
25
35
  let manifestName = args['/path/to/manifest'];
26
36
  const opts = args.workspaceDir ? { TRUSTIFY_DA_WORKSPACE_DIR: args.workspaceDir } : {};
37
+ if (args.providers) {
38
+ opts.TRUSTIFY_DA_PROVIDERS = args.providers;
39
+ }
40
+ if (args.sources) {
41
+ opts.TRUSTIFY_DA_SOURCES = args.sources;
42
+ }
27
43
  let res = await client.componentAnalysis(manifestName, opts);
28
44
  console.log(JSON.stringify(res, null, 2));
29
45
  }
@@ -72,6 +88,14 @@ const image = {
72
88
  desc: 'For JSON report, get only the \'summary\'',
73
89
  type: 'boolean',
74
90
  conflicts: 'html'
91
+ },
92
+ providers: {
93
+ desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
94
+ type: 'string',
95
+ },
96
+ sources: {
97
+ desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
98
+ type: 'string',
75
99
  }
76
100
  }),
77
101
  handler: async (args) => {
@@ -81,7 +105,14 @@ const image = {
81
105
  }
82
106
  let html = args['html'];
83
107
  let summary = args['summary'];
84
- let res = await client.imageAnalysis(imageRefs, html);
108
+ const opts = {};
109
+ if (args.providers) {
110
+ opts.TRUSTIFY_DA_PROVIDERS = args.providers;
111
+ }
112
+ if (args.sources) {
113
+ opts.TRUSTIFY_DA_SOURCES = args.sources;
114
+ }
115
+ let res = await client.imageAnalysis(imageRefs, html, opts);
85
116
  if (summary && !html) {
86
117
  let summaries = {};
87
118
  for (let [imageRef, report] of Object.entries(res)) {
@@ -132,6 +163,14 @@ const stack = {
132
163
  desc: 'Workspace root directory (for monorepos; lock file is expected here)',
133
164
  type: 'string',
134
165
  normalize: true,
166
+ },
167
+ providers: {
168
+ desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
169
+ type: 'string',
170
+ },
171
+ sources: {
172
+ desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
173
+ type: 'string',
135
174
  }
136
175
  }),
137
176
  handler: async (args) => {
@@ -139,6 +178,12 @@ const stack = {
139
178
  let html = args['html'];
140
179
  let summary = args['summary'];
141
180
  const opts = args.workspaceDir ? { TRUSTIFY_DA_WORKSPACE_DIR: args.workspaceDir } : {};
181
+ if (args.providers) {
182
+ opts.TRUSTIFY_DA_PROVIDERS = args.providers;
183
+ }
184
+ if (args.sources) {
185
+ opts.TRUSTIFY_DA_SOURCES = args.sources;
186
+ }
142
187
  let theProvidersSummary = new Map();
143
188
  let theProvidersObject = {};
144
189
  let res = await client.stackAnalysis(manifest, html, opts);
@@ -201,6 +246,14 @@ const stackBatch = {
201
246
  desc: 'Stop on first invalid package.json or SBOM error (env: TRUSTIFY_DA_CONTINUE_ON_ERROR=false)',
202
247
  type: 'boolean',
203
248
  default: false,
249
+ },
250
+ providers: {
251
+ desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
252
+ type: 'string',
253
+ },
254
+ sources: {
255
+ desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
256
+ type: 'string',
204
257
  }
205
258
  }),
206
259
  handler: async (args) => {
@@ -221,6 +274,12 @@ const stackBatch = {
221
274
  if (args.failFast) {
222
275
  opts.continueOnError = false;
223
276
  }
277
+ if (args.providers) {
278
+ opts.TRUSTIFY_DA_PROVIDERS = args.providers;
279
+ }
280
+ if (args.sources) {
281
+ opts.TRUSTIFY_DA_SOURCES = args.sources;
282
+ }
224
283
  let res = await client.stackAnalysisBatch(workspaceRoot, html, opts);
225
284
  const batchAnalysis = res && typeof res === 'object' && res != null && 'analysis' in res ? res.analysis : res;
226
285
  if (summary && !html && typeof batchAnalysis === 'object') {
@@ -373,9 +432,64 @@ const sbom = {
373
432
  }
374
433
  }
375
434
  };
435
+ const remediate = {
436
+ command: 'remediate <path>',
437
+ desc: 'Scan and apply vulnerability remediations to manifest files',
438
+ builder: yargs => yargs.positional('path', {
439
+ desc: 'Path to manifest file or directory',
440
+ type: 'string',
441
+ normalize: true,
442
+ }).options({
443
+ 'dry-run': {
444
+ alias: 'd',
445
+ type: 'boolean',
446
+ desc: 'Preview changes without modifying files',
447
+ },
448
+ providers: {
449
+ desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
450
+ type: 'string',
451
+ },
452
+ sources: {
453
+ desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
454
+ type: 'string',
455
+ },
456
+ 'group-by': {
457
+ type: 'string',
458
+ choices: ['dependency', 'bundle'],
459
+ default: 'dependency',
460
+ desc: 'Report grouping strategy',
461
+ },
462
+ }),
463
+ handler: async (args) => {
464
+ try {
465
+ const result = await runRemediation(args.path, {
466
+ dryRun: args['dry-run'],
467
+ providers: args.providers,
468
+ sources: args.sources,
469
+ });
470
+ if (result.remediations.length === 0 && result.manifests.length === 0) {
471
+ console.log('No supported manifest files found.');
472
+ process.exit(result.exitCode);
473
+ }
474
+ if (!args['dry-run'] && result.appliedFiles.length > 0) {
475
+ console.log(`Updated ${result.appliedFiles.length} file(s):`);
476
+ for (const file of result.appliedFiles) {
477
+ console.log(` ${file}`);
478
+ }
479
+ console.log('');
480
+ }
481
+ console.log(generateReport(result.remediations, { groupBy: args['group-by'], dryRun: args['dry-run'] }));
482
+ process.exit(result.exitCode);
483
+ }
484
+ catch (err) {
485
+ console.error(err.message);
486
+ process.exit(1);
487
+ }
488
+ }
489
+ };
376
490
  // parse and invoke the command
377
491
  yargs(hideBin(process.argv))
378
- .usage(`Usage: ${process.argv[0].includes("node") ? path.parse(process.argv[1]).base : path.parse(process.argv[0]).base} {component|stack|stack-batch|image|validate-token|license|sbom}`)
492
+ .usage(`Usage: ${process.argv[0].includes("node") ? path.parse(process.argv[1]).base : path.parse(process.argv[0]).base} {component|stack|stack-batch|image|validate-token|license|sbom|remediate}`)
379
493
  .command(stack)
380
494
  .command(stackBatch)
381
495
  .command(component)
@@ -383,6 +497,7 @@ yargs(hideBin(process.argv))
383
497
  .command(validateToken)
384
498
  .command(license)
385
499
  .command(sbom)
500
+ .command(remediate)
386
501
  .scriptName('')
387
502
  .version(false)
388
503
  .demandCommand(1)
@@ -26,6 +26,19 @@ export default class CycloneDxSbom {
26
26
  alg: string;
27
27
  content: string;
28
28
  }>): CycloneDxSbom;
29
+ /**
30
+ * Attach hashes to already-added components by matching their PURL. This is a
31
+ * post-processing step so ecosystem-specific hash sources (e.g. Maven reading
32
+ * the local .m2 cache) can enrich the SBOM without threading their concern
33
+ * through the shared dependency-tree parser. Components without a matching
34
+ * entry, or whose hashes are already set, are left untouched.
35
+ * @param {Map<string, Array<{alg: string, content: string}>>} hashMap - PURL→hashes map
36
+ * @return {CycloneDxSbom} the updated SBOM
37
+ */
38
+ attachHashes(hashMap: Map<string, Array<{
39
+ alg: string;
40
+ content: string;
41
+ }>>): CycloneDxSbom;
29
42
  /** @param {{}} opts - various options, settings and configuration of application.
30
43
  * @return String CycloneDx Sbom json object in a string format
31
44
  */
@@ -1,5 +1,12 @@
1
1
  import { EOL } from "os";
2
2
  import { PackageURL } from "packageurl-js";
3
+ import { getPackageVersion } from "./package_version.js";
4
+ /**
5
+ * This client's version, read once at module load, used to populate
6
+ * metadata.tools.components[].version in the generated SBOM.
7
+ * @type {string|undefined}
8
+ */
9
+ const PACKAGE_VERSION = getPackageVersion();
3
10
  /**
4
11
  *
5
12
  * @param component {PackageURL}
@@ -135,6 +142,30 @@ export default class CycloneDxSbom {
135
142
  }
136
143
  return this;
137
144
  }
145
+ /**
146
+ * Attach hashes to already-added components by matching their PURL. This is a
147
+ * post-processing step so ecosystem-specific hash sources (e.g. Maven reading
148
+ * the local .m2 cache) can enrich the SBOM without threading their concern
149
+ * through the shared dependency-tree parser. Components without a matching
150
+ * entry, or whose hashes are already set, are left untouched.
151
+ * @param {Map<string, Array<{alg: string, content: string}>>} hashMap - PURL→hashes map
152
+ * @return {CycloneDxSbom} the updated SBOM
153
+ */
154
+ attachHashes(hashMap) {
155
+ if (!hashMap || hashMap.size === 0) {
156
+ return this;
157
+ }
158
+ for (const component of this.components) {
159
+ if (component.hashes) {
160
+ continue;
161
+ }
162
+ const hashes = hashMap.get(component.purl);
163
+ if (hashes && hashes.length > 0) {
164
+ component.hashes = hashes;
165
+ }
166
+ }
167
+ return this;
168
+ }
138
169
  /** @param {{}} opts - various options, settings and configuration of application.
139
170
  * @return String CycloneDx Sbom json object in a string format
140
171
  */
@@ -144,11 +175,18 @@ export default class CycloneDxSbom {
144
175
  const rootPurl = this.rootComponent?.purl;
145
176
  this.sbomObject = {
146
177
  "bomFormat": "CycloneDX",
147
- "specVersion": "1.4",
178
+ "specVersion": "1.6",
148
179
  "version": 1,
149
180
  "metadata": {
150
181
  "timestamp": new Date(),
151
182
  "component": this.rootComponent,
183
+ "tools": {
184
+ "components": [{
185
+ "type": "application",
186
+ "name": "trustify-da-javascript-client",
187
+ "version": PACKAGE_VERSION
188
+ }]
189
+ },
152
190
  "properties": new Array()
153
191
  },
154
192
  "components": this.components.filter(c => c.purl !== rootPurl),
@@ -24,6 +24,7 @@ export function selectTrustifyDABackend(opts?: {
24
24
  export function generateSbom(manifestPath: string, opts?: Options): Promise<object>;
25
25
  export { parseImageRef } from "./oci_image/utils.js";
26
26
  export { ImageRef } from "./oci_image/images.js";
27
+ export { extractRemediations } from "./remediation.js";
27
28
  declare namespace _default {
28
29
  export { componentAnalysis };
29
30
  export { stackAnalysis };
@@ -53,7 +54,9 @@ export type Options = {
53
54
  TRUSTIFY_DA_PYTHON_PATH?: string | undefined;
54
55
  TRUSTIFY_DA_PYTHON_VIRTUAL_ENV?: string | undefined;
55
56
  TRUSTIFY_DA_PYTHON3_PATH?: string | undefined;
56
- TRUSTIFY_DA_RECOMMENDATIONS_ENABLED?: string | undefined;
57
+ TRUSTIFY_DA_PROVIDERS?: string | undefined;
58
+ TRUSTIFY_DA_RECOMMEND?: string | undefined;
59
+ TRUSTIFY_DA_SOURCES?: string | undefined;
57
60
  TRUSTIFY_DA_SKOPEO_CONFIG_PATH?: string | undefined;
58
61
  TRUSTIFY_DA_SKOPEO_PATH?: string | undefined;
59
62
  TRUSTIFY_DA_SYFT_CONFIG_PATH?: string | undefined;
@@ -106,7 +109,7 @@ export type SbomResult = {
106
109
  * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
107
110
  * @throws {Error} if no matching provider, failed to get create content, or backend request failed
108
111
  */
109
- declare function componentAnalysis(manifest: string, opts?: Options): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport>;
112
+ declare function componentAnalysis(manifest: string, opts?: Options): Promise<any>;
110
113
  /**
111
114
  * @overload
112
115
  * @param {string} manifest
@@ -124,7 +127,7 @@ declare function stackAnalysis(manifest: string, html: true, opts?: Options | un
124
127
  * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
125
128
  * @throws {Error}
126
129
  */
127
- declare function stackAnalysis(manifest: string, html: false, opts?: Options | undefined): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport>;
130
+ declare function stackAnalysis(manifest: string, html: false, opts?: Options | undefined): Promise<any>;
128
131
  /**
129
132
  * Get stack analysis report for a manifest file.
130
133
  * @overload
@@ -135,7 +138,7 @@ declare function stackAnalysis(manifest: string, html: false, opts?: Options | u
135
138
  * @throws {Error} if manifest inaccessible, no matching provider, failed to get create content,
136
139
  * or backend request failed
137
140
  */
138
- declare function stackAnalysis(manifest: string, html?: boolean | undefined, opts?: Options | undefined): Promise<string | import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport>;
141
+ declare function stackAnalysis(manifest: string, html?: boolean | undefined, opts?: Options | undefined): Promise<string | any>;
139
142
  /**
140
143
  * @overload
141
144
  * @param {string} workspaceRoot
@@ -173,7 +176,7 @@ declare function stackAnalysisBatch(workspaceRoot: string, html: false, opts: Op
173
176
  batchMetadata: true;
174
177
  }): Promise<{
175
178
  analysis: {
176
- [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
179
+ [x: string]: any;
177
180
  };
178
181
  metadata: BatchAnalysisMetadata;
179
182
  }>;
@@ -188,7 +191,7 @@ declare function stackAnalysisBatch(workspaceRoot: string, html: false, opts: Op
188
191
  declare function stackAnalysisBatch(workspaceRoot: string, html: false, opts?: (Options & {
189
192
  batchMetadata?: false;
190
193
  }) | undefined): Promise<{
191
- [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
194
+ [x: string]: any;
192
195
  }>;
193
196
  /**
194
197
  * Get stack analysis for all workspace packages/crates (batch).
@@ -204,10 +207,10 @@ declare function stackAnalysisBatch(workspaceRoot: string, html: false, opts?: (
204
207
  * @throws {Error} if workspace root invalid, no manifests found, no packages pass validation, no SBOMs produced, or backend request failed. When `opts.batchMetadata` is set, `error.batchMetadata` may be set on thrown errors.
205
208
  */
206
209
  declare function stackAnalysisBatch(workspaceRoot: string, html?: boolean | undefined, opts?: Options | undefined): Promise<string | {
207
- [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
210
+ [x: string]: any;
208
211
  } | {
209
212
  analysis: string | {
210
- [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
213
+ [x: string]: any;
211
214
  };
212
215
  metadata: BatchAnalysisMetadata;
213
216
  }>;
@@ -229,7 +232,7 @@ declare function imageAnalysis(imageRefs: Array<string>, html: true, opts?: Opti
229
232
  * @throws {Error}
230
233
  */
231
234
  declare function imageAnalysis(imageRefs: Array<string>, html: false, opts?: Options | undefined): Promise<{
232
- [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
235
+ [x: string]: any;
233
236
  }>;
234
237
  /**
235
238
  * Get image analysis report for a set of OCI image references.
@@ -242,7 +245,7 @@ declare function imageAnalysis(imageRefs: Array<string>, html: false, opts?: Opt
242
245
  * or backend request failed
243
246
  */
244
247
  declare function imageAnalysis(imageRefs: Array<string>, html?: boolean | undefined, opts?: Options | undefined): Promise<string | {
245
- [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
248
+ [x: string]: any;
246
249
  }>;
247
250
  /**
248
251
  * Validates the Exhort token.
@@ -264,3 +267,5 @@ import { resolveContinueOnError } from './batch_opts.js';
264
267
  import { resolveBatchMetadata } from './batch_opts.js';
265
268
  export { discoverMavenModules, discoverGradleSubprojects, discoverGoWorkspaceModules, discoverUvWorkspaceMembers, discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson, resolveWorkspaceDiscoveryIgnore, filterManifestPathsByDiscoveryIgnore, resolveContinueOnError, resolveBatchMetadata };
266
269
  export { getProjectLicense, findLicenseFilePath, identifyLicense, getLicenseDetails, licensesFromReport, normalizeLicensesResponse, runLicenseCheck, getCompatibility } from "./license/index.js";
270
+ export { generateReport, generateDeduplicationKey } from "./remediation_report.js";
271
+ export { runRemediation, findManifests } from "./remediate.js";
package/dist/src/index.js CHANGED
@@ -1,21 +1,23 @@
1
+ import fs from 'node:fs';
1
2
  import path from "node:path";
2
3
  import { EOL } from "os";
3
4
  import pLimit from 'p-limit';
4
- import { availableProviders, match } from './provider.js';
5
5
  import analysis from './analysis.js';
6
- import fs from 'node:fs';
7
- import { getCustom } from "./tools.js";
8
6
  import { resolveBatchMetadata, resolveContinueOnError } from './batch_opts.js';
9
- import { discoverMavenModules } from './providers/java_maven.js';
10
- import { discoverGradleSubprojects } from './providers/java_gradle.js';
7
+ import { getPackageVersion } from './package_version.js';
8
+ import { availableProviders, match } from './provider.js';
11
9
  import { discoverGoWorkspaceModules } from './providers/golang_gomodules.js';
10
+ import { discoverGradleSubprojects } from './providers/java_gradle.js';
11
+ import { discoverMavenModules } from './providers/java_maven.js';
12
12
  import { discoverUvWorkspaceMembers } from './providers/python_uv.js';
13
+ import { getCustom } from "./tools.js";
13
14
  import { discoverWorkspaceCrates, discoverWorkspacePackages, filterManifestPathsByDiscoveryIgnore, resolveWorkspaceDiscoveryIgnore, validatePackageJson, } from './workspace.js';
14
- import.meta.dirname;
15
- import * as url from 'url';
16
15
  export { parseImageRef } from "./oci_image/utils.js";
17
16
  export { ImageRef } from "./oci_image/images.js";
18
17
  export { getProjectLicense, findLicenseFilePath, identifyLicense, getLicenseDetails, licensesFromReport, normalizeLicensesResponse, runLicenseCheck, getCompatibility } from "./license/index.js";
18
+ export { extractRemediations } from "./remediation.js";
19
+ export { generateReport, generateDeduplicationKey } from './remediation_report.js';
20
+ export { runRemediation, findManifests } from './remediate.js';
19
21
  export default { componentAnalysis, stackAnalysis, stackAnalysisBatch, imageAnalysis, validateToken, generateSbom };
20
22
  export { discoverMavenModules, discoverGradleSubprojects, discoverGoWorkspaceModules, discoverUvWorkspaceMembers, discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson, resolveWorkspaceDiscoveryIgnore, filterManifestPathsByDiscoveryIgnore, resolveContinueOnError, resolveBatchMetadata, };
21
23
  /**
@@ -39,7 +41,9 @@ export { discoverMavenModules, discoverGradleSubprojects, discoverGoWorkspaceMod
39
41
  * TRUSTIFY_DA_PYTHON_PATH?: string | undefined,
40
42
  * TRUSTIFY_DA_PYTHON_VIRTUAL_ENV?: string | undefined,
41
43
  * TRUSTIFY_DA_PYTHON3_PATH?: string | undefined,
42
- * TRUSTIFY_DA_RECOMMENDATIONS_ENABLED?: string | undefined,
44
+ * TRUSTIFY_DA_PROVIDERS?: string | undefined,
45
+ * TRUSTIFY_DA_RECOMMEND?: string | undefined,
46
+ * TRUSTIFY_DA_SOURCES?: string | undefined,
43
47
  * TRUSTIFY_DA_SKOPEO_CONFIG_PATH?: string | undefined,
44
48
  * TRUSTIFY_DA_SKOPEO_PATH?: string | undefined,
45
49
  * TRUSTIFY_DA_SYFT_CONFIG_PATH?: string | undefined,
@@ -91,23 +95,7 @@ function logOptionsAndEnvironmentsVariables(alongsideText, valueToBePrinted) {
91
95
  * @private
92
96
  */
93
97
  function readAndPrintVersionFromPackageJson() {
94
- let dirName;
95
- // new ESM way in nodeJS ( since node version 22 ) to bring module directory.
96
- dirName = import.meta.dirname;
97
- // old ESM way in nodeJS ( before node versions 22.00 to bring module directory)
98
- if (!dirName) {
99
- dirName = url.fileURLToPath(new URL('.', import.meta.url));
100
- }
101
- try {
102
- if (__dirname) {
103
- dirName = __dirname;
104
- }
105
- }
106
- catch (e) {
107
- console.log("__dirname is not defined, continue with fileUrlPath");
108
- }
109
- let packageJson = JSON.parse(fs.readFileSync(path.join(dirName, "..", "package.json")).toString());
110
- logOptionsAndEnvironmentsVariables("trustify-da-javascript-client analysis started, version: ", packageJson.version);
98
+ logOptionsAndEnvironmentsVariables("trustify-da-javascript-client analysis started, version: ", getPackageVersion());
111
99
  }
112
100
  /**
113
101
  * This function is used to determine the Trustify DA backend URL.
@@ -9,10 +9,10 @@
9
9
  * @param {import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport} [analysisResult] - analysis result that includes licenses array from backend
10
10
  * @returns {Promise<{ projectLicense: { manifest: Object|null, file: Object|null, mismatch: boolean }, incompatibleDependencies: Array<{ purl: string, licenses: string[], category?: string, reason: string }>, error?: string }>}
11
11
  */
12
- export function runLicenseCheck(sbomContent: string, manifestPath: string, url: string, opts?: import("../index.js").Options, analysisResult?: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport): Promise<{
12
+ export function runLicenseCheck(sbomContent: string, manifestPath: string, url: string, opts?: import("../index.js").Options, analysisResult?: any): Promise<{
13
13
  projectLicense: {
14
- manifest: any | null;
15
- file: any | null;
14
+ manifest: Object | null;
15
+ file: Object | null;
16
16
  mismatch: boolean;
17
17
  };
18
18
  incompatibleDependencies: Array<{
@@ -85,11 +85,14 @@ export async function runLicenseCheck(sbomContent, manifestPath, url, opts = {},
85
85
  continue;
86
86
  const status = getCompatibility(projectCategory, entry.category);
87
87
  if (status === 'incompatible') {
88
+ const reason = entry.category?.toUpperCase() === 'UNKNOWN'
89
+ ? 'License not recognized as a standard SPDX identifier. Manual review recommended to verify compatibility.'
90
+ : 'Dependency license(s) are incompatible with the project license.';
88
91
  incompatibleDependencies.push({
89
92
  purl,
90
93
  licenses: entry.licenses,
91
94
  category: entry.category,
92
- reason: 'Dependency license(s) are incompatible with the project license.'
95
+ reason
93
96
  });
94
97
  }
95
98
  }
@@ -114,9 +114,12 @@ export function getCompatibility(projectCategory, dependencyCategory) {
114
114
  }
115
115
  const proj = projectCategory.toUpperCase();
116
116
  const dep = dependencyCategory.toUpperCase();
117
- if (proj === 'UNKNOWN' || dep === 'UNKNOWN') {
117
+ if (proj === 'UNKNOWN') {
118
118
  return 'unknown';
119
119
  }
120
+ if (dep === 'UNKNOWN') {
121
+ return 'incompatible';
122
+ }
120
123
  const restrictiveness = {
121
124
  'PERMISSIVE': 1,
122
125
  'WEAK_COPYLEFT': 2,
@@ -6,7 +6,7 @@
6
6
  * @param {import('../index.js').Options} [opts={}] - options (proxy, token, TRUSTIFY_DA_BACKEND_URL, etc.)
7
7
  * @returns {Promise<Object|null>} License details or null if not found
8
8
  */
9
- export function getLicenseDetails(spdxId: string, opts?: import("../index.js").Options): Promise<any | null>;
9
+ export function getLicenseDetails(spdxId: string, opts?: import("../index.js").Options): Promise<Object | null>;
10
10
  /**
11
11
  * Normalize the LicensesResponse shape (array of LicenseProviderResult) into a map of purl -> license info.
12
12
  * Each provider result has { status, summary, packages } where packages is { [purl]: { concluded, evidence } }.
@@ -28,7 +28,7 @@ export function normalizeLicensesResponse(data: unknown, purls?: string[]): Map<
28
28
  * @param {string[]} [purls] - optional list of purls to restrict to
29
29
  * @returns {Map<string, { licenses: string[], category?: string }>}
30
30
  */
31
- export function licensesFromReport(analysisReport: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport, purls?: string[]): Map<string, {
31
+ export function licensesFromReport(analysisReport: any, purls?: string[]): Map<string, {
32
32
  licenses: string[];
33
33
  category?: string;
34
34
  }>;
@@ -78,7 +78,7 @@ export class ImageRef {
78
78
  * @param {string} [platform]
79
79
  * @param {import("index.js").Options} [opts={}]
80
80
  */
81
- constructor(image: string, platform?: string, opts?: import("index.js").Options);
81
+ constructor(image: string, platform?: string, opts?: any);
82
82
  /** @type {Image} */
83
83
  image: Image;
84
84
  /** @type {Platform} */
@@ -242,8 +242,22 @@ export class ImageRef {
242
242
  getPackageURL() {
243
243
  /** @type {Object.<string, string>} */
244
244
  const qualifiers = {};
245
- const repositoryUrl = this.image.getNameWithoutTag();
245
+ let repositoryUrl = this.image.getNameWithoutTag();
246
246
  const simpleName = this.image.getSimpleName();
247
+ // Normalize Docker Hub image references so all forms produce the same PURL:
248
+ // node → docker.io/node
249
+ // docker.io/library/node → docker.io/node
250
+ if (repositoryUrl != null) {
251
+ const lower = repositoryUrl.toLowerCase();
252
+ if (lower === simpleName.toLowerCase()) {
253
+ // Bare name (e.g. "node") — default to docker.io
254
+ repositoryUrl = `docker.io/${simpleName}`;
255
+ }
256
+ else if (lower.startsWith('docker.io/library/')) {
257
+ // Official image with library/ prefix — strip it
258
+ repositoryUrl = `docker.io/${lower.substring('docker.io/library/'.length)}`;
259
+ }
260
+ }
247
261
  if (repositoryUrl != null && repositoryUrl.toLowerCase() !== simpleName.toLowerCase()) {
248
262
  qualifiers[ImageRef.REPOSITORY_QUALIFIER] = repositoryUrl.toLowerCase();
249
263
  }
@@ -4,7 +4,7 @@
4
4
  * @param {import("../index.js").Options} [opts={}] - optional various options to pass along the application
5
5
  * @returns {{}}
6
6
  */
7
- export function generateImageSBOM(imageRef: import("./images").ImageRef, opts?: import("../index.js").Options): {};
7
+ export function generateImageSBOM(imageRef: any, opts?: import("../index.js").Options): {};
8
8
  /**
9
9
  *
10
10
  * @param {string} image
@@ -25,7 +25,7 @@ export function getImagePlatform(opts?: import("../index.js").Options): Platform
25
25
  * @returns {Object.<string, string>} - The image digests
26
26
  * @throws {Error} If the image info is invalid
27
27
  */
28
- export function getImageDigests(imageRef: import("./images").ImageRef, opts?: import("../index.js").Options): {
28
+ export function getImageDigests(imageRef: any, opts?: import("../index.js").Options): {
29
29
  [x: string]: string;
30
30
  };
31
31
  export type SyftImageSource = {
@@ -34,9 +34,9 @@ export type SyftImageSource = {
34
34
  getVariant: () => string;
35
35
  };
36
36
  export type SyftImageSourceType = {
37
- getOs: (arg0: any) => string;
38
- getArch: (arg0: any) => string;
39
- getVariant: (arg0: any) => string;
37
+ getOs: (arg0: Object) => string;
38
+ getArch: (arg0: Object) => string;
39
+ getVariant: (arg0: Object) => string;
40
40
  };
41
41
  import { ImageRef } from './images.js';
42
42
  import { Platform } from './platform.js';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Reads this package's version from package.json, resolving the path relative
3
+ * to this module. Works under ESM (node >= 22 import.meta.dirname, older via
4
+ * import.meta.url) and CommonJS (__dirname). Returns undefined if the file
5
+ * cannot be read, so callers never fail on a missing version.
6
+ * @return {string|undefined} the package version, or undefined
7
+ */
8
+ export function getPackageVersion(): string | undefined;
@@ -0,0 +1,31 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import * as url from "url";
4
+ /**
5
+ * Reads this package's version from package.json, resolving the path relative
6
+ * to this module. Works under ESM (node >= 22 import.meta.dirname, older via
7
+ * import.meta.url) and CommonJS (__dirname). Returns undefined if the file
8
+ * cannot be read, so callers never fail on a missing version.
9
+ * @return {string|undefined} the package version, or undefined
10
+ */
11
+ export function getPackageVersion() {
12
+ try {
13
+ let dirName = import.meta.dirname;
14
+ if (!dirName) {
15
+ dirName = url.fileURLToPath(new URL('.', import.meta.url));
16
+ }
17
+ try {
18
+ if (__dirname) {
19
+ dirName = __dirname;
20
+ }
21
+ }
22
+ catch {
23
+ // __dirname not defined under ESM, continue with fileURLToPath
24
+ }
25
+ const packageJson = JSON.parse(fs.readFileSync(path.join(dirName, "..", "package.json")).toString());
26
+ return packageJson.version;
27
+ }
28
+ catch {
29
+ return undefined;
30
+ }
31
+ }
@@ -34,9 +34,9 @@ export type Provided = {
34
34
  };
35
35
  export type Provider = {
36
36
  isSupported: (arg0: string) => boolean;
37
- validateLockFile: (arg0: string, arg1: any) => void;
37
+ validateLockFile: (arg0: string, arg1: Object) => void;
38
38
  provideComponent: (arg0: string, arg1: {}) => Provided | Promise<Provided>;
39
39
  provideStack: (arg0: string, arg1: {}) => Provided | Promise<Provided>;
40
- readLicenseFromManifest: (arg0: string, arg1: any | undefined) => string | null;
40
+ readLicenseFromManifest: (arg0: string, arg1: Object | undefined) => string | null;
41
41
  packageManagerName: () => string;
42
42
  };
@@ -7,6 +7,7 @@ import Javascript_bun from './providers/javascript_bun.js';
7
7
  import Javascript_npm from './providers/javascript_npm.js';
8
8
  import Javascript_pnpm from './providers/javascript_pnpm.js';
9
9
  import Javascript_yarn from './providers/javascript_yarn.js';
10
+ import dockerfileProvider from './providers/oci_dockerfile.js';
10
11
  import pythonPipProvider from './providers/python_pip.js';
11
12
  import Python_pip_pyproject from './providers/python_pip_pyproject.js';
12
13
  import Python_poetry from './providers/python_poetry.js';
@@ -31,7 +32,8 @@ export const availableProviders = [
31
32
  new Python_poetry(),
32
33
  new Python_uv(),
33
34
  new Python_pip_pyproject(),
34
- rustCargoProvider
35
+ rustCargoProvider,
36
+ dockerfileProvider
35
37
  ];
36
38
  /**
37
39
  * Match a provider by manifest type only (no lock file check). Used for license reading.