@trustify-da/trustify-da-javascript-client 0.3.0-ea.dcb2120 → 0.3.0-ea.de62188

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 (70) hide show
  1. package/README.md +1 -1
  2. package/dist/package.json +25 -22
  3. package/dist/src/analysis.d.ts +14 -7
  4. package/dist/src/analysis.js +29 -13
  5. package/dist/src/cli.js +58 -1
  6. package/dist/src/cyclone_dx_sbom.d.ts +7 -1
  7. package/dist/src/cyclone_dx_sbom.js +29 -8
  8. package/dist/src/index.d.ts +72 -11
  9. package/dist/src/index.js +76 -7
  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/provider.d.ts +4 -3
  18. package/dist/src/provider.js +8 -2
  19. package/dist/src/providers/base_java.d.ts +14 -12
  20. package/dist/src/providers/base_java.js +18 -44
  21. package/dist/src/providers/base_javascript.d.ts +20 -9
  22. package/dist/src/providers/base_javascript.js +10 -3
  23. package/dist/src/providers/base_pyproject.d.ts +22 -32
  24. package/dist/src/providers/base_pyproject.js +60 -74
  25. package/dist/src/providers/containerfile_parser.d.ts +5 -0
  26. package/dist/src/providers/containerfile_parser.js +20 -0
  27. package/dist/src/providers/golang_gomodules.d.ts +11 -1
  28. package/dist/src/providers/golang_gomodules.js +65 -8
  29. package/dist/src/providers/java_gradle.d.ts +19 -0
  30. package/dist/src/providers/java_gradle.js +116 -2
  31. package/dist/src/providers/java_gradle_groovy.d.ts +1 -1
  32. package/dist/src/providers/java_gradle_kotlin.d.ts +1 -1
  33. package/dist/src/providers/java_maven.d.ts +10 -2
  34. package/dist/src/providers/java_maven.js +165 -1
  35. package/dist/src/providers/javascript_bun.d.ts +10 -0
  36. package/dist/src/providers/javascript_bun.js +100 -0
  37. package/dist/src/providers/javascript_npm.d.ts +6 -0
  38. package/dist/src/providers/javascript_npm.js +21 -0
  39. package/dist/src/providers/javascript_pnpm.js +6 -2
  40. package/dist/src/providers/marker_evaluator.d.ts +14 -0
  41. package/dist/src/providers/marker_evaluator.js +191 -0
  42. package/dist/src/providers/oci_dockerfile.d.ts +51 -0
  43. package/dist/src/providers/oci_dockerfile.js +177 -0
  44. package/dist/src/providers/processors/yarn_berry_processor.d.ts +2 -2
  45. package/dist/src/providers/processors/yarn_berry_processor.js +6 -2
  46. package/dist/src/providers/processors/yarn_classic_processor.d.ts +2 -2
  47. package/dist/src/providers/python_controller.d.ts +5 -1
  48. package/dist/src/providers/python_controller.js +65 -7
  49. package/dist/src/providers/python_pip.d.ts +5 -0
  50. package/dist/src/providers/python_pip.js +5 -5
  51. package/dist/src/providers/python_pip_pyproject.d.ts +61 -0
  52. package/dist/src/providers/python_pip_pyproject.js +146 -0
  53. package/dist/src/providers/python_poetry.d.ts +64 -8
  54. package/dist/src/providers/python_poetry.js +139 -22
  55. package/dist/src/providers/python_uv.d.ts +20 -1
  56. package/dist/src/providers/python_uv.js +126 -3
  57. package/dist/src/providers/rust_cargo.d.ts +6 -2
  58. package/dist/src/providers/rust_cargo.js +31 -4
  59. package/dist/src/providers/tree-sitter-containerfile.wasm +0 -0
  60. package/dist/src/sbom.d.ts +7 -1
  61. package/dist/src/sbom.js +4 -2
  62. package/dist/src/tools.d.ts +28 -2
  63. package/dist/src/tools.js +58 -0
  64. package/dist/src/updaters/maven_updater.d.ts +26 -0
  65. package/dist/src/updaters/maven_updater.js +322 -0
  66. package/dist/src/updaters/toml_updater.d.ts +33 -0
  67. package/dist/src/updaters/toml_updater.js +244 -0
  68. package/dist/src/workspace.d.ts +9 -0
  69. package/dist/src/workspace.js +1 -1
  70. package/package.json +26 -23
package/README.md CHANGED
@@ -695,7 +695,7 @@ By default, The API algorithm will use native commands of PIP installer as data
695
695
  It's also possible to use the lightweight Python PIP utility [pipdeptree](https://pypi.org/project/pipdeptree/) as data source instead. In order to activate this, you need to set the environment variable/option `TRUSTIFY_DA_PIP_USE_DEP_TREE` to 'true'.
696
696
 
697
697
  #### Toggle Red Hat Trusted Content recommendations
698
- Both the HTML-based report and JSON response will by default contain recommendations for migrating to Red Hat-based Trusted Content repositories. This feature can be disabled by setting `TRUSTIFY_DA_RECOMMENDATIONS_ENABLED` to 'false' via environment variables or options.
698
+ Both the HTML-based report and JSON response will by default contain recommendations for migrating to Red Hat-based Trusted Content repositories. This feature can be disabled by setting `TRUSTIFY_DA_RECOMMEND` to 'false' via environment variables or options.
699
699
 
700
700
  #### Additional CLI arguments
701
701
  For some ecosystems we support passing additional CLI arguments to the underlying tools. The following table outlines the supported ecosystems and the environment variable/option that configures this. Note that the arguments are expected to be in the format of a JSON array.
package/dist/package.json CHANGED
@@ -35,32 +35,33 @@
35
35
  "config/**/*"
36
36
  ],
37
37
  "scripts": {
38
- "lint": "eslint src test --ext js",
39
- "lint:fix": "eslint src test --ext js --fix",
38
+ "lint": "eslint src test",
39
+ "lint:fix": "eslint src test --fix",
40
40
  "test": "c8 npm run tests",
41
41
  "tests": "mocha --config .mocharc.json",
42
42
  "tests:rep": "mocha --reporter-option maxDiffSize=0 --reporter json > unit-tests-result.json",
43
- "pretest": "cp node_modules/tree-sitter-requirements/tree-sitter-requirements.wasm src/providers/tree-sitter-requirements.wasm && cp node_modules/tree-sitter-gomod/tree-sitter-gomod.wasm src/providers/tree-sitter-gomod.wasm",
43
+ "pretest": "cp node_modules/tree-sitter-requirements/tree-sitter-requirements.wasm src/providers/tree-sitter-requirements.wasm && cp node_modules/tree-sitter-gomod/tree-sitter-gomod.wasm src/providers/tree-sitter-gomod.wasm && cp node_modules/tree-sitter-containerfile/tree-sitter-containerfile.wasm src/providers/tree-sitter-containerfile.wasm",
44
44
  "precompile": "rm -rf dist",
45
45
  "compile": "tsc -p tsconfig.json",
46
46
  "compile:dev": "tsc -p tsconfig.dev.json",
47
- "postcompile": "cp node_modules/tree-sitter-requirements/tree-sitter-requirements.wasm dist/src/providers/tree-sitter-requirements.wasm && cp node_modules/tree-sitter-gomod/tree-sitter-gomod.wasm dist/src/providers/tree-sitter-gomod.wasm"
47
+ "postcompile": "cp node_modules/tree-sitter-requirements/tree-sitter-requirements.wasm dist/src/providers/tree-sitter-requirements.wasm && cp node_modules/tree-sitter-gomod/tree-sitter-gomod.wasm dist/src/providers/tree-sitter-gomod.wasm && cp node_modules/tree-sitter-containerfile/tree-sitter-containerfile.wasm dist/src/providers/tree-sitter-containerfile.wasm"
48
48
  },
49
49
  "dependencies": {
50
50
  "@babel/core": "^7.23.2",
51
- "@cyclonedx/cyclonedx-library": "^6.13.0",
51
+ "@cyclonedx/cyclonedx-library": "^10.0.0",
52
52
  "eslint-import-resolver-typescript": "^4.4.4",
53
53
  "fast-glob": "^3.3.3",
54
- "fast-toml": "^0.5.4",
55
54
  "fast-xml-parser": "^5.3.4",
56
55
  "help": "^3.0.2",
57
- "https-proxy-agent": "^7.0.6",
56
+ "https-proxy-agent": "^9.0.0",
58
57
  "js-yaml": "^4.1.1",
58
+ "jsonc-parser": "^3.3.1",
59
59
  "micromatch": "^4.0.8",
60
60
  "node-fetch": "^3.3.2",
61
- "p-limit": "^4.0.0",
61
+ "p-limit": "^7.3.0",
62
62
  "packageurl-js": "~1.0.2",
63
63
  "smol-toml": "^1.6.0",
64
+ "tree-sitter-containerfile": "^0.8.1",
64
65
  "tree-sitter-gomod": "github:strum355/tree-sitter-go-mod#56326f2ad478892ace58ff247a97d492a3cbcdda",
65
66
  "tree-sitter-requirements": "github:Strum355/tree-sitter-requirements#d0261ee76b84253997fe70d7d397e78c006c3801",
66
67
  "web-tree-sitter": "^0.26.7",
@@ -68,23 +69,24 @@
68
69
  },
69
70
  "devDependencies": {
70
71
  "@babel/core": "^7.23.2",
71
- "@trustify-da/trustify-da-api-model": "^2.0.7",
72
- "@types/node": "^20.17.30",
72
+ "@eslint/js": "^10.0.0",
73
+ "@trustify-da/trustify-da-api-model": "^2.0.12",
74
+ "@types/node": "^25.9.1",
73
75
  "@types/which": "^3.0.4",
74
76
  "babel-plugin-rewire": "^1.2.0",
75
77
  "c8": "^11.0.0",
76
- "chai": "^4.3.7",
77
- "eslint": "^8.42.0",
78
+ "chai": "^6.2.2",
79
+ "eslint": "^10.4.1",
78
80
  "eslint-import-resolver-typescript": "^4.4.4",
79
- "eslint-plugin-editorconfig": "^4.0.3",
80
- "eslint-plugin-import": "^2.29.1",
81
+ "eslint-plugin-import-x": "^4.16.2",
81
82
  "esmock": "^2.6.2",
82
- "mocha": "^10.2.0",
83
+ "globals": "^17.6.0",
84
+ "mocha": "^11.7.6",
83
85
  "msw": "^2.12.7",
84
- "sinon": "^15.1.2",
85
- "sinon-chai": "^3.7.0",
86
- "typescript": "^5.1.3",
87
- "which": "^5.0.0"
86
+ "sinon": "^22.0.0",
87
+ "sinon-chai": "^4.0.1",
88
+ "typescript": "^6.0.3",
89
+ "which": "^7.0.0"
88
90
  },
89
91
  "mocha": {
90
92
  "check-leaks": false,
@@ -112,12 +114,13 @@
112
114
  "reporter": [
113
115
  "html",
114
116
  "json",
117
+ "lcov",
115
118
  "text"
116
119
  ]
117
120
  },
118
- "eslintIgnore": [
119
- "index.js"
120
- ],
121
+ "overrides": {
122
+ "tree-sitter": "0.22.4"
123
+ },
121
124
  "resolutions": {
122
125
  "@hapi/joi": "17.1.1"
123
126
  }
@@ -6,6 +6,7 @@ declare namespace _default {
6
6
  export { requestStackBatch };
7
7
  export { requestImages };
8
8
  export { validateToken };
9
+ export { appendAnalysisQueryParams };
9
10
  }
10
11
  export default _default;
11
12
  /**
@@ -16,7 +17,7 @@ export default _default;
16
17
  * @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
17
18
  * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
18
19
  */
19
- declare function requestComponent(provider: import("./provider").Provider, manifest: string, url: string, opts?: import("index.js").Options): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport>;
20
+ declare function requestComponent(provider: any, manifest: string, url: string, opts?: any): Promise<any>;
20
21
  /**
21
22
  * Send a stack analysis request and get the report as 'text/html' or 'application/json'.
22
23
  * @param {import('./provider').Provider} provider - the provided data for constructing the request
@@ -26,7 +27,7 @@ declare function requestComponent(provider: import("./provider").Provider, manif
26
27
  * @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
27
28
  * @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
28
29
  */
29
- declare function requestStack(provider: import("./provider").Provider, manifest: string, url: string, html?: boolean, opts?: import("index.js").Options): Promise<string | import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport>;
30
+ declare function requestStack(provider: any, manifest: string, url: string, html?: boolean, opts?: any): Promise<string | any>;
30
31
  /**
31
32
  * Send a batch stack analysis request for multiple manifests (SBOMs keyed by purl).
32
33
  * @param {Object.<string, object>} sbomByPurl - Map of root purl to CycloneDX SBOM object
@@ -36,9 +37,9 @@ declare function requestStack(provider: import("./provider").Provider, manifest:
36
37
  * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
37
38
  */
38
39
  declare function requestStackBatch(sbomByPurl: {
40
+ [x: string]: object;
41
+ }, url: string, html?: boolean, opts?: any): Promise<string | {
39
42
  [x: string]: any;
40
- }, url: string, html?: boolean, opts?: import("index.js").Options): Promise<string | {
41
- [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
42
43
  }>;
43
44
  /**
44
45
  *
@@ -47,8 +48,8 @@ declare function requestStackBatch(sbomByPurl: {
47
48
  * @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
48
49
  * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
49
50
  */
50
- declare function requestImages(imageRefs: Array<string>, url: string, html?: boolean, opts?: import("index.js").Options): Promise<string | {
51
- [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
51
+ declare function requestImages(imageRefs: Array<string>, url: string, html?: boolean, opts?: any): Promise<string | {
52
+ [x: string]: any;
52
53
  }>;
53
54
  /**
54
55
  *
@@ -56,4 +57,10 @@ declare function requestImages(imageRefs: Array<string>, url: string, html?: boo
56
57
  * @param {import("index.js").Options} [opts={}] - optional various options to pass headers for t he validateToken Request
57
58
  * @return {Promise<number>} return the HTTP status Code of the response from the validate token request.
58
59
  */
59
- declare function validateToken(url: any, opts?: import("index.js").Options): Promise<number>;
60
+ declare function validateToken(url: any, opts?: any): Promise<number>;
61
+ /**
62
+ * Append common analysis query parameters (recommend, providers, sources) to a URL.
63
+ * @param {URL} url - the URL object to append query parameters to
64
+ * @param {import("index.js").Options} [opts={}] - options containing parameter overrides
65
+ */
66
+ declare function appendAnalysisQueryParams(url: URL, opts?: any): void;
@@ -6,7 +6,7 @@ import { generateImageSBOM, parseImageRef } from "./oci_image/utils.js";
6
6
  import { addProxyAgent, getCustom, getTokenHeaders, TRUSTIFY_DA_OPERATION_TYPE_HEADER, TRUSTIFY_DA_PACKAGE_MANAGER_HEADER } from "./tools.js";
7
7
  /** Media type for CycloneDX JSON batch payloads (batch-analysis API). */
8
8
  export const CYCLONEDX_JSON_MEDIA_TYPE = 'application/vnd.cyclonedx+json';
9
- export default { requestComponent, requestStack, requestStackBatch, requestImages, validateToken };
9
+ export default { requestComponent, requestStack, requestStackBatch, requestImages, validateToken, appendAnalysisQueryParams };
10
10
  /**
11
11
  * Send a stack analysis request and get the report as 'text/html' or 'application/json'.
12
12
  * @param {import('./provider').Provider} provider - the provided data for constructing the request
@@ -20,6 +20,9 @@ async function requestStack(provider, manifest, url, html = false, opts = {}) {
20
20
  opts["source-manifest"] = Buffer.from(fs.readFileSync(manifest).toString()).toString('base64');
21
21
  opts["manifest-type"] = path.parse(manifest).base;
22
22
  let provided = await provider.provideStack(manifest, opts); // throws error if content providing failed
23
+ if (provided.batch) {
24
+ return requestStackBatch(JSON.parse(provided.content), url, html, opts);
25
+ }
23
26
  opts["source-manifest"] = "";
24
27
  opts[TRUSTIFY_DA_OPERATION_TYPE_HEADER.toUpperCase().replaceAll("-", "_")] = "stack-analysis";
25
28
  let startTime = new Date();
@@ -38,9 +41,7 @@ async function requestStack(provider, manifest, url, html = false, opts = {}) {
38
41
  body: provided.content
39
42
  }, opts);
40
43
  const finalUrl = new URL(`${url}/api/v5/analysis`);
41
- if (opts['TRUSTIFY_DA_RECOMMENDATIONS_ENABLED'] === 'false') {
42
- finalUrl.searchParams.append('recommend', 'false');
43
- }
44
+ appendAnalysisQueryParams(finalUrl, opts);
44
45
  let resp = await fetch(finalUrl, fetchOptions);
45
46
  let result;
46
47
  if (resp.status === 200) {
@@ -79,6 +80,9 @@ async function requestStack(provider, manifest, url, html = false, opts = {}) {
79
80
  async function requestComponent(provider, manifest, url, opts = {}) {
80
81
  opts["source-manifest"] = Buffer.from(fs.readFileSync(manifest).toString()).toString('base64');
81
82
  let provided = await provider.provideComponent(manifest, opts); // throws error if content providing failed
83
+ if (provided.batch) {
84
+ return requestStackBatch(JSON.parse(provided.content), url, false, opts);
85
+ }
82
86
  opts["source-manifest"] = "";
83
87
  opts[TRUSTIFY_DA_OPERATION_TYPE_HEADER.toUpperCase().replaceAll("-", "_")] = "component-analysis";
84
88
  if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
@@ -95,9 +99,7 @@ async function requestComponent(provider, manifest, url, opts = {}) {
95
99
  body: provided.content
96
100
  }, opts);
97
101
  const finalUrl = new URL(`${url}/api/v5/analysis`);
98
- if (opts['TRUSTIFY_DA_RECOMMENDATIONS_ENABLED'] === 'false') {
99
- finalUrl.searchParams.append('recommend', 'false');
100
- }
102
+ appendAnalysisQueryParams(finalUrl, opts);
101
103
  let resp = await fetch(finalUrl, fetchOptions);
102
104
  let result;
103
105
  if (resp.status === 200) {
@@ -136,9 +138,7 @@ async function requestComponent(provider, manifest, url, opts = {}) {
136
138
  */
137
139
  async function requestStackBatch(sbomByPurl, url, html = false, opts = {}) {
138
140
  const finalUrl = new URL(`${url}/api/v5/batch-analysis`);
139
- if (opts['TRUSTIFY_DA_RECOMMENDATIONS_ENABLED'] === 'false') {
140
- finalUrl.searchParams.append('recommend', 'false');
141
- }
141
+ appendAnalysisQueryParams(finalUrl, opts);
142
142
  const fetchOptions = addProxyAgent({
143
143
  method: 'POST',
144
144
  headers: {
@@ -186,9 +186,7 @@ async function requestImages(imageRefs, url, html = false, opts = {}) {
186
186
  imageSboms[parsedImageRef.getPackageURL().toString()] = generateImageSBOM(parsedImageRef, opts);
187
187
  }
188
188
  const finalUrl = new URL(`${url}/api/v5/batch-analysis`);
189
- if (opts['TRUSTIFY_DA_RECOMMENDATIONS_ENABLED'] === 'false') {
190
- finalUrl.searchParams.append('recommend', 'false');
191
- }
189
+ appendAnalysisQueryParams(finalUrl, opts);
192
190
  const fetchOptions = addProxyAgent({
193
191
  method: 'POST',
194
192
  headers: {
@@ -222,6 +220,24 @@ async function requestImages(imageRefs, url, html = false, opts = {}) {
222
220
  throw new Error(`Got error response from Trustify DA backend - http return code : ${resp.status}, ex-request-id: ${resp.headers.get("ex-request-id")} error message => ${await resp.text()}`);
223
221
  }
224
222
  }
223
+ /**
224
+ * Append common analysis query parameters (recommend, providers, sources) to a URL.
225
+ * @param {URL} url - the URL object to append query parameters to
226
+ * @param {import("index.js").Options} [opts={}] - options containing parameter overrides
227
+ */
228
+ function appendAnalysisQueryParams(url, opts = {}) {
229
+ if (getCustom('TRUSTIFY_DA_RECOMMEND', 'true', opts) === 'false') {
230
+ url.searchParams.append('recommend', 'false');
231
+ }
232
+ const providers = getCustom('TRUSTIFY_DA_PROVIDERS', null, opts);
233
+ if (providers) {
234
+ url.searchParams.append('providers', providers);
235
+ }
236
+ const sources = getCustom('TRUSTIFY_DA_SOURCES', null, opts);
237
+ if (sources) {
238
+ url.searchParams.append('sources', sources);
239
+ }
240
+ }
225
241
  /**
226
242
  *
227
243
  * @param url the backend url to send the request to
package/dist/src/cli.js CHANGED
@@ -19,11 +19,25 @@ const component = {
19
19
  desc: 'Workspace root directory (for monorepos; lock file is expected here)',
20
20
  type: 'string',
21
21
  normalize: true,
22
+ },
23
+ providers: {
24
+ desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
25
+ type: 'string',
26
+ },
27
+ sources: {
28
+ desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
29
+ type: 'string',
22
30
  }
23
31
  }),
24
32
  handler: async (args) => {
25
33
  let manifestName = args['/path/to/manifest'];
26
34
  const opts = args.workspaceDir ? { TRUSTIFY_DA_WORKSPACE_DIR: args.workspaceDir } : {};
35
+ if (args.providers) {
36
+ opts.TRUSTIFY_DA_PROVIDERS = args.providers;
37
+ }
38
+ if (args.sources) {
39
+ opts.TRUSTIFY_DA_SOURCES = args.sources;
40
+ }
27
41
  let res = await client.componentAnalysis(manifestName, opts);
28
42
  console.log(JSON.stringify(res, null, 2));
29
43
  }
@@ -72,6 +86,14 @@ const image = {
72
86
  desc: 'For JSON report, get only the \'summary\'',
73
87
  type: 'boolean',
74
88
  conflicts: 'html'
89
+ },
90
+ providers: {
91
+ desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
92
+ type: 'string',
93
+ },
94
+ sources: {
95
+ desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
96
+ type: 'string',
75
97
  }
76
98
  }),
77
99
  handler: async (args) => {
@@ -81,7 +103,14 @@ const image = {
81
103
  }
82
104
  let html = args['html'];
83
105
  let summary = args['summary'];
84
- let res = await client.imageAnalysis(imageRefs, html);
106
+ const opts = {};
107
+ if (args.providers) {
108
+ opts.TRUSTIFY_DA_PROVIDERS = args.providers;
109
+ }
110
+ if (args.sources) {
111
+ opts.TRUSTIFY_DA_SOURCES = args.sources;
112
+ }
113
+ let res = await client.imageAnalysis(imageRefs, html, opts);
85
114
  if (summary && !html) {
86
115
  let summaries = {};
87
116
  for (let [imageRef, report] of Object.entries(res)) {
@@ -132,6 +161,14 @@ const stack = {
132
161
  desc: 'Workspace root directory (for monorepos; lock file is expected here)',
133
162
  type: 'string',
134
163
  normalize: true,
164
+ },
165
+ providers: {
166
+ desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
167
+ type: 'string',
168
+ },
169
+ sources: {
170
+ desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
171
+ type: 'string',
135
172
  }
136
173
  }),
137
174
  handler: async (args) => {
@@ -139,6 +176,12 @@ const stack = {
139
176
  let html = args['html'];
140
177
  let summary = args['summary'];
141
178
  const opts = args.workspaceDir ? { TRUSTIFY_DA_WORKSPACE_DIR: args.workspaceDir } : {};
179
+ if (args.providers) {
180
+ opts.TRUSTIFY_DA_PROVIDERS = args.providers;
181
+ }
182
+ if (args.sources) {
183
+ opts.TRUSTIFY_DA_SOURCES = args.sources;
184
+ }
142
185
  let theProvidersSummary = new Map();
143
186
  let theProvidersObject = {};
144
187
  let res = await client.stackAnalysis(manifest, html, opts);
@@ -201,6 +244,14 @@ const stackBatch = {
201
244
  desc: 'Stop on first invalid package.json or SBOM error (env: TRUSTIFY_DA_CONTINUE_ON_ERROR=false)',
202
245
  type: 'boolean',
203
246
  default: false,
247
+ },
248
+ providers: {
249
+ desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
250
+ type: 'string',
251
+ },
252
+ sources: {
253
+ desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
254
+ type: 'string',
204
255
  }
205
256
  }),
206
257
  handler: async (args) => {
@@ -221,6 +272,12 @@ const stackBatch = {
221
272
  if (args.failFast) {
222
273
  opts.continueOnError = false;
223
274
  }
275
+ if (args.providers) {
276
+ opts.TRUSTIFY_DA_PROVIDERS = args.providers;
277
+ }
278
+ if (args.sources) {
279
+ opts.TRUSTIFY_DA_SOURCES = args.sources;
280
+ }
224
281
  let res = await client.stackAnalysisBatch(workspaceRoot, html, opts);
225
282
  const batchAnalysis = res && typeof res === 'object' && res != null && 'analysis' in res ? res.analysis : res;
226
283
  if (summary && !html && typeof batchAnalysis === 'object') {
@@ -18,9 +18,14 @@ export default class CycloneDxSbom {
18
18
  * Adds a dependency relationship between two components in the SBOM
19
19
  * @param {PackageURL} sourceRef - The source component (parent)
20
20
  * @param {PackageURL} targetRef - The target component (dependency)
21
+ * @param {string} [scope] - Scope of the dependency
22
+ * @param {Array<{alg: string, content: string}>} [targetHashes] - Optional hashes for the target component
21
23
  * @return {CycloneDxSbom} The updated SBOM
22
24
  */
23
- addDependency(sourceRef: PackageURL, targetRef: PackageURL, scope: any): CycloneDxSbom;
25
+ addDependency(sourceRef: PackageURL, targetRef: PackageURL, scope?: string, targetHashes?: Array<{
26
+ alg: string;
27
+ content: string;
28
+ }>): CycloneDxSbom;
24
29
  /** @param {{}} opts - various options, settings and configuration of application.
25
30
  * @return String CycloneDx Sbom json object in a string format
26
31
  */
@@ -50,6 +55,7 @@ export default class CycloneDxSbom {
50
55
  version: any;
51
56
  scope: any;
52
57
  licenses?: any;
58
+ hashes?: any;
53
59
  };
54
60
  /**
55
61
  * This method gets an array of dependencies to be ignored, and remove all of them from CycloneDx Sbom
@@ -6,10 +6,11 @@ import { PackageURL } from "packageurl-js";
6
6
  * @param type type of package - application or library
7
7
  * @param scope scope of the component - runtime or compile
8
8
  * @param licenses optional license string or array of licenses for the component
9
- * @return {{"bom-ref": string, name, purl: string, type, version, scope, licenses?}}
9
+ * @param hashes optional array of hash objects for the component, e.g. [{alg: "SHA-256", content: "..."}]
10
+ * @return {{"bom-ref": string, name, purl: string, type, version, scope, licenses?, hashes?}}
10
11
  * @private
11
12
  */
12
- function getComponent(component, type, scope, licenses) {
13
+ function getComponent(component, type, scope, licenses, hashes) {
13
14
  let componentObject;
14
15
  if (component instanceof PackageURL) {
15
16
  if (component.namespace) {
@@ -40,12 +41,24 @@ function getComponent(component, type, scope, licenses) {
40
41
  // Add licenses if provided (CycloneDX format). Callers must provide valid SPDX identifiers.
41
42
  if (licenses) {
42
43
  const licenseArray = Array.isArray(licenses) ? licenses : [licenses];
43
- componentObject.licenses = licenseArray.map(lic => {
44
+ const validLicenses = licenseArray
45
+ .map(lic => {
44
46
  if (typeof lic === 'string') {
45
47
  return { license: { id: lic } };
46
48
  }
47
- return lic;
48
- });
49
+ if (typeof lic === 'object' && lic !== null && ('license' in lic || 'expression' in lic)) {
50
+ return lic;
51
+ }
52
+ return null;
53
+ })
54
+ .filter(Boolean);
55
+ if (validLicenses.length > 0) {
56
+ componentObject.licenses = validLicenses;
57
+ }
58
+ }
59
+ // Add hashes if provided (CycloneDX 1.4 format).
60
+ if (hashes && hashes.length > 0) {
61
+ componentObject.hashes = hashes;
49
62
  }
50
63
  return componentObject;
51
64
  }
@@ -86,16 +99,24 @@ export default class CycloneDxSbom {
86
99
  * Adds a dependency relationship between two components in the SBOM
87
100
  * @param {PackageURL} sourceRef - The source component (parent)
88
101
  * @param {PackageURL} targetRef - The target component (dependency)
102
+ * @param {string} [scope] - Scope of the dependency
103
+ * @param {Array<{alg: string, content: string}>} [targetHashes] - Optional hashes for the target component
89
104
  * @return {CycloneDxSbom} The updated SBOM
90
105
  */
91
- addDependency(sourceRef, targetRef, scope) {
106
+ addDependency(sourceRef, targetRef, scope, targetHashes) {
92
107
  const sourcePurl = sourceRef.toString();
93
108
  const targetPurl = targetRef.toString();
94
109
  // Ensure both components exist in the components list
95
110
  [sourceRef, targetRef].forEach((ref, index) => {
96
111
  const purl = index === 0 ? sourcePurl : targetPurl;
97
- if (this.getComponentIndex(purl) < 0) {
98
- this.components.push(getComponent(ref, "library", scope));
112
+ const existingIndex = this.getComponentIndex(purl);
113
+ if (existingIndex < 0) {
114
+ const hashes = index === 1 ? targetHashes : undefined;
115
+ this.components.push(getComponent(ref, "library", scope, undefined, hashes));
116
+ }
117
+ else if (index === 1 && targetHashes && targetHashes.length > 0 && !this.components[existingIndex].hashes) {
118
+ // Update hashes if the component was first seen without them (e.g. as a source)
119
+ this.components[existingIndex].hashes = targetHashes;
99
120
  }
100
121
  });
101
122
  // Ensure source dependency exists
@@ -53,7 +53,9 @@ export type Options = {
53
53
  TRUSTIFY_DA_PYTHON_PATH?: string | undefined;
54
54
  TRUSTIFY_DA_PYTHON_VIRTUAL_ENV?: string | undefined;
55
55
  TRUSTIFY_DA_PYTHON3_PATH?: string | undefined;
56
- TRUSTIFY_DA_RECOMMENDATIONS_ENABLED?: string | undefined;
56
+ TRUSTIFY_DA_PROVIDERS?: string | undefined;
57
+ TRUSTIFY_DA_RECOMMEND?: string | undefined;
58
+ TRUSTIFY_DA_SOURCES?: string | undefined;
57
59
  TRUSTIFY_DA_SKOPEO_CONFIG_PATH?: string | undefined;
58
60
  TRUSTIFY_DA_SKOPEO_PATH?: string | undefined;
59
61
  TRUSTIFY_DA_SYFT_CONFIG_PATH?: string | undefined;
@@ -80,7 +82,7 @@ export type Options = {
80
82
  };
81
83
  export type BatchAnalysisMetadata = {
82
84
  workspaceRoot: string;
83
- ecosystem: "javascript" | "cargo" | "unknown";
85
+ ecosystem: "javascript" | "cargo" | "pyproject" | "unknown";
84
86
  total: number;
85
87
  successful: number;
86
88
  failed: number;
@@ -106,7 +108,7 @@ export type SbomResult = {
106
108
  * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
107
109
  * @throws {Error} if no matching provider, failed to get create content, or backend request failed
108
110
  */
109
- declare function componentAnalysis(manifest: string, opts?: Options): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport>;
111
+ declare function componentAnalysis(manifest: string, opts?: Options): Promise<any>;
110
112
  /**
111
113
  * @overload
112
114
  * @param {string} manifest
@@ -124,7 +126,7 @@ declare function stackAnalysis(manifest: string, html: true, opts?: Options | un
124
126
  * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
125
127
  * @throws {Error}
126
128
  */
127
- declare function stackAnalysis(manifest: string, html: false, opts?: Options | undefined): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport>;
129
+ declare function stackAnalysis(manifest: string, html: false, opts?: Options | undefined): Promise<any>;
128
130
  /**
129
131
  * Get stack analysis report for a manifest file.
130
132
  * @overload
@@ -135,24 +137,79 @@ declare function stackAnalysis(manifest: string, html: false, opts?: Options | u
135
137
  * @throws {Error} if manifest inaccessible, no matching provider, failed to get create content,
136
138
  * or backend request failed
137
139
  */
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>;
140
+ declare function stackAnalysis(manifest: string, html?: boolean | undefined, opts?: Options | undefined): Promise<string | any>;
141
+ /**
142
+ * @overload
143
+ * @param {string} workspaceRoot
144
+ * @param {true} html
145
+ * @param {Options & { batchMetadata: true }} opts
146
+ * @returns {Promise<{ analysis: string, metadata: BatchAnalysisMetadata }>}
147
+ * @throws {Error}
148
+ */
149
+ declare function stackAnalysisBatch(workspaceRoot: string, html: true, opts: Options & {
150
+ batchMetadata: true;
151
+ }): Promise<{
152
+ analysis: string;
153
+ metadata: BatchAnalysisMetadata;
154
+ }>;
155
+ /**
156
+ * @overload
157
+ * @param {string} workspaceRoot
158
+ * @param {true} html
159
+ * @param {Options & { batchMetadata?: false }} [opts={}]
160
+ * @returns {Promise<string>}
161
+ * @throws {Error}
162
+ */
163
+ declare function stackAnalysisBatch(workspaceRoot: string, html: true, opts?: (Options & {
164
+ batchMetadata?: false;
165
+ }) | undefined): Promise<string>;
166
+ /**
167
+ * @overload
168
+ * @param {string} workspaceRoot
169
+ * @param {false} html
170
+ * @param {Options & { batchMetadata: true }} opts
171
+ * @returns {Promise<{ analysis: Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>, metadata: BatchAnalysisMetadata }>}
172
+ * @throws {Error}
173
+ */
174
+ declare function stackAnalysisBatch(workspaceRoot: string, html: false, opts: Options & {
175
+ batchMetadata: true;
176
+ }): Promise<{
177
+ analysis: {
178
+ [x: string]: any;
179
+ };
180
+ metadata: BatchAnalysisMetadata;
181
+ }>;
182
+ /**
183
+ * @overload
184
+ * @param {string} workspaceRoot
185
+ * @param {false} html
186
+ * @param {Options & { batchMetadata?: false }} [opts={}]
187
+ * @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
188
+ * @throws {Error}
189
+ */
190
+ declare function stackAnalysisBatch(workspaceRoot: string, html: false, opts?: (Options & {
191
+ batchMetadata?: false;
192
+ }) | undefined): Promise<{
193
+ [x: string]: any;
194
+ }>;
139
195
  /**
140
196
  * Get stack analysis for all workspace packages/crates (batch).
141
197
  * Detects ecosystem from workspace root: Cargo (Cargo.toml + Cargo.lock) or JS/TS (package.json + lock file).
142
198
  * SBOMs are generated in parallel (see `batchConcurrency`) unless `continueOnError: false` (fail-fast sequential).
143
199
  * With `opts.batchMetadata` / `TRUSTIFY_DA_BATCH_METADATA`, returns `{ analysis, metadata }` including validation and SBOM errors.
144
200
  *
201
+ * @overload
145
202
  * @param {string} workspaceRoot - Path to workspace root (containing lock file and workspace config)
146
203
  * @param {boolean} [html=false] - true returns HTML, false returns JSON report
147
204
  * @param {Options} [opts={}] - `batchConcurrency`, discovery ignores, `continueOnError` (default true), `batchMetadata` (default false)
148
205
  * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>|{ analysis: string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>, metadata: BatchAnalysisMetadata }>}
149
206
  * @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.
150
207
  */
151
- declare function stackAnalysisBatch(workspaceRoot: string, html?: boolean, opts?: Options): Promise<string | {
152
- [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
208
+ declare function stackAnalysisBatch(workspaceRoot: string, html?: boolean | undefined, opts?: Options | undefined): Promise<string | {
209
+ [x: string]: any;
153
210
  } | {
154
211
  analysis: string | {
155
- [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
212
+ [x: string]: any;
156
213
  };
157
214
  metadata: BatchAnalysisMetadata;
158
215
  }>;
@@ -174,7 +231,7 @@ declare function imageAnalysis(imageRefs: Array<string>, html: true, opts?: Opti
174
231
  * @throws {Error}
175
232
  */
176
233
  declare function imageAnalysis(imageRefs: Array<string>, html: false, opts?: Options | undefined): Promise<{
177
- [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
234
+ [x: string]: any;
178
235
  }>;
179
236
  /**
180
237
  * Get image analysis report for a set of OCI image references.
@@ -187,7 +244,7 @@ declare function imageAnalysis(imageRefs: Array<string>, html: false, opts?: Opt
187
244
  * or backend request failed
188
245
  */
189
246
  declare function imageAnalysis(imageRefs: Array<string>, html?: boolean | undefined, opts?: Options | undefined): Promise<string | {
190
- [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
247
+ [x: string]: any;
191
248
  }>;
192
249
  /**
193
250
  * Validates the Exhort token.
@@ -196,6 +253,10 @@ declare function imageAnalysis(imageRefs: Array<string>, html?: boolean | undefi
196
253
  * @throws {Error} if the backend request failed.
197
254
  */
198
255
  declare function validateToken(opts?: Options): Promise<object>;
256
+ import { discoverMavenModules } from './providers/java_maven.js';
257
+ import { discoverGradleSubprojects } from './providers/java_gradle.js';
258
+ import { discoverGoWorkspaceModules } from './providers/golang_gomodules.js';
259
+ import { discoverUvWorkspaceMembers } from './providers/python_uv.js';
199
260
  import { discoverWorkspacePackages } from './workspace.js';
200
261
  import { discoverWorkspaceCrates } from './workspace.js';
201
262
  import { validatePackageJson } from './workspace.js';
@@ -203,5 +264,5 @@ import { resolveWorkspaceDiscoveryIgnore } from './workspace.js';
203
264
  import { filterManifestPathsByDiscoveryIgnore } from './workspace.js';
204
265
  import { resolveContinueOnError } from './batch_opts.js';
205
266
  import { resolveBatchMetadata } from './batch_opts.js';
206
- export { discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson, resolveWorkspaceDiscoveryIgnore, filterManifestPathsByDiscoveryIgnore, resolveContinueOnError, resolveBatchMetadata };
267
+ export { discoverMavenModules, discoverGradleSubprojects, discoverGoWorkspaceModules, discoverUvWorkspaceMembers, discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson, resolveWorkspaceDiscoveryIgnore, filterManifestPathsByDiscoveryIgnore, resolveContinueOnError, resolveBatchMetadata };
207
268
  export { getProjectLicense, findLicenseFilePath, identifyLicense, getLicenseDetails, licensesFromReport, normalizeLicensesResponse, runLicenseCheck, getCompatibility } from "./license/index.js";