@trustify-da/trustify-da-javascript-client 0.3.0-ea.e12bc82 → 0.3.0-ea.e258fba

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 (97) hide show
  1. package/README.md +367 -13
  2. package/dist/package.json +48 -28
  3. package/dist/src/analysis.d.ts +28 -5
  4. package/dist/src/analysis.js +99 -89
  5. package/dist/src/batch_opts.d.ts +24 -0
  6. package/dist/src/batch_opts.js +35 -0
  7. package/dist/src/cli.js +346 -9
  8. package/dist/src/cyclone_dx_sbom.d.ts +30 -3
  9. package/dist/src/cyclone_dx_sbom.js +95 -9
  10. package/dist/src/index.d.ts +202 -12
  11. package/dist/src/index.js +365 -30
  12. package/dist/src/license/index.d.ts +28 -0
  13. package/dist/src/license/index.js +103 -0
  14. package/dist/src/license/license_utils.d.ts +40 -0
  15. package/dist/src/license/license_utils.js +137 -0
  16. package/dist/src/license/licenses_api.d.ts +34 -0
  17. package/dist/src/license/licenses_api.js +98 -0
  18. package/dist/src/license/project_license.d.ts +20 -0
  19. package/dist/src/license/project_license.js +62 -0
  20. package/dist/src/oci_image/images.d.ts +4 -5
  21. package/dist/src/oci_image/images.js +15 -1
  22. package/dist/src/oci_image/utils.d.ts +7 -7
  23. package/dist/src/oci_image/utils.js +11 -2
  24. package/dist/src/package_version.d.ts +8 -0
  25. package/dist/src/package_version.js +31 -0
  26. package/dist/src/provider.d.ts +18 -5
  27. package/dist/src/provider.js +33 -5
  28. package/dist/src/providers/base_java.d.ts +68 -15
  29. package/dist/src/providers/base_java.js +82 -61
  30. package/dist/src/providers/base_javascript.d.ts +99 -12
  31. package/dist/src/providers/base_javascript.js +243 -27
  32. package/dist/src/providers/base_pyproject.d.ts +158 -0
  33. package/dist/src/providers/base_pyproject.js +322 -0
  34. package/dist/src/providers/containerfile_parser.d.ts +5 -0
  35. package/dist/src/providers/containerfile_parser.js +20 -0
  36. package/dist/src/providers/golang_gomodules.d.ts +30 -13
  37. package/dist/src/providers/golang_gomodules.js +219 -122
  38. package/dist/src/providers/gomod_parser.d.ts +4 -0
  39. package/dist/src/providers/gomod_parser.js +16 -0
  40. package/dist/src/providers/java_gradle.d.ts +76 -3
  41. package/dist/src/providers/java_gradle.js +323 -21
  42. package/dist/src/providers/java_gradle_groovy.d.ts +1 -1
  43. package/dist/src/providers/java_gradle_kotlin.d.ts +1 -1
  44. package/dist/src/providers/java_maven.d.ts +45 -10
  45. package/dist/src/providers/java_maven.js +300 -14
  46. package/dist/src/providers/javascript_bun.d.ts +22 -0
  47. package/dist/src/providers/javascript_bun.js +141 -0
  48. package/dist/src/providers/javascript_npm.d.ts +19 -0
  49. package/dist/src/providers/javascript_npm.js +61 -1
  50. package/dist/src/providers/javascript_pnpm.d.ts +14 -1
  51. package/dist/src/providers/javascript_pnpm.js +55 -5
  52. package/dist/src/providers/javascript_yarn.d.ts +12 -0
  53. package/dist/src/providers/javascript_yarn.js +75 -2
  54. package/dist/src/providers/manifest.d.ts +2 -0
  55. package/dist/src/providers/manifest.js +28 -7
  56. package/dist/src/providers/marker_evaluator.d.ts +14 -0
  57. package/dist/src/providers/marker_evaluator.js +191 -0
  58. package/dist/src/providers/oci_dockerfile.d.ts +51 -0
  59. package/dist/src/providers/oci_dockerfile.js +177 -0
  60. package/dist/src/providers/processors/yarn_berry_processor.d.ts +6 -2
  61. package/dist/src/providers/processors/yarn_berry_processor.js +91 -7
  62. package/dist/src/providers/processors/yarn_classic_processor.d.ts +6 -2
  63. package/dist/src/providers/processors/yarn_classic_processor.js +8 -6
  64. package/dist/src/providers/python_controller.d.ts +10 -3
  65. package/dist/src/providers/python_controller.js +122 -63
  66. package/dist/src/providers/python_pip.d.ts +16 -4
  67. package/dist/src/providers/python_pip.js +51 -58
  68. package/dist/src/providers/python_pip_pyproject.d.ts +61 -0
  69. package/dist/src/providers/python_pip_pyproject.js +146 -0
  70. package/dist/src/providers/python_poetry.d.ts +98 -0
  71. package/dist/src/providers/python_poetry.js +286 -0
  72. package/dist/src/providers/python_uv.d.ts +46 -0
  73. package/dist/src/providers/python_uv.js +269 -0
  74. package/dist/src/providers/requirements_parser.d.ts +6 -0
  75. package/dist/src/providers/requirements_parser.js +24 -0
  76. package/dist/src/providers/rust_cargo.d.ts +56 -0
  77. package/dist/src/providers/rust_cargo.js +682 -0
  78. package/dist/src/providers/tree-sitter-containerfile.wasm +0 -0
  79. package/dist/src/providers/tree-sitter-gomod.wasm +0 -0
  80. package/dist/src/providers/tree-sitter-requirements.wasm +0 -0
  81. package/dist/src/remediate.d.ts +21 -0
  82. package/dist/src/remediate.js +149 -0
  83. package/dist/src/remediation.d.ts +57 -0
  84. package/dist/src/remediation.js +436 -0
  85. package/dist/src/remediation_report.d.ts +40 -0
  86. package/dist/src/remediation_report.js +159 -0
  87. package/dist/src/sbom.d.ts +28 -2
  88. package/dist/src/sbom.js +26 -4
  89. package/dist/src/tools.d.ts +48 -6
  90. package/dist/src/tools.js +138 -2
  91. package/dist/src/updaters/maven_updater.d.ts +26 -0
  92. package/dist/src/updaters/maven_updater.js +322 -0
  93. package/dist/src/updaters/toml_updater.d.ts +33 -0
  94. package/dist/src/updaters/toml_updater.js +244 -0
  95. package/dist/src/workspace.d.ts +70 -0
  96. package/dist/src/workspace.js +256 -0
  97. package/package.json +49 -29
package/dist/package.json CHANGED
@@ -25,7 +25,9 @@
25
25
  "provenance": true
26
26
  },
27
27
  "type": "module",
28
- "bin": "dist/src/cli.js",
28
+ "bin": {
29
+ "trustify-da": "dist/src/cli.js"
30
+ },
29
31
  "main": "dist/src/index.js",
30
32
  "module": "dist/src/index.js",
31
33
  "types": "dist/src/index.d.ts",
@@ -35,43 +37,60 @@
35
37
  "config/**/*"
36
38
  ],
37
39
  "scripts": {
38
- "lint": "eslint src test --ext js",
39
- "lint:fix": "eslint src test --ext js --fix",
40
+ "lint": "eslint src test",
41
+ "lint:fix": "eslint src test --fix",
40
42
  "test": "c8 npm run tests",
41
- "tests": "mocha --config .mocharc.json --grep \".*analysis module.*\" --invert",
43
+ "tests": "mocha --config .mocharc.json",
42
44
  "tests:rep": "mocha --reporter-option maxDiffSize=0 --reporter json > unit-tests-result.json",
45
+ "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",
43
46
  "precompile": "rm -rf dist",
44
- "compile": "tsc -p tsconfig.json"
47
+ "compile": "tsc -p tsconfig.json",
48
+ "compile:dev": "tsc -p tsconfig.dev.json",
49
+ "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"
45
50
  },
46
51
  "dependencies": {
47
52
  "@babel/core": "^7.23.2",
48
- "@cyclonedx/cyclonedx-library": "~1.13.3",
49
- "fast-toml": "^0.5.4",
50
- "fast-xml-parser": "^4.5.3",
53
+ "@yarnpkg/parsers": "^3.1.0",
54
+ "eslint-import-resolver-typescript": "^4.4.4",
55
+ "fast-glob": "^3.3.3",
56
+ "fast-xml-parser": "^5.3.4",
51
57
  "help": "^3.0.2",
52
- "https-proxy-agent": "^7.0.6",
53
- "node-fetch": "^2.7.0",
54
- "packageurl-js": "^1.0.2",
55
- "yargs": "^17.7.2"
58
+ "https-proxy-agent": "^9.0.0",
59
+ "js-yaml": "^4.1.1",
60
+ "jsonc-parser": "^3.3.1",
61
+ "micromatch": "^4.0.8",
62
+ "node-fetch": "^3.3.2",
63
+ "p-limit": "^7.3.0",
64
+ "packageurl-js": "~1.0.2",
65
+ "smol-toml": "^1.6.0",
66
+ "tree-sitter-containerfile": "^0.8.1",
67
+ "tree-sitter-gomod": "github:strum355/tree-sitter-go-mod#56326f2ad478892ace58ff247a97d492a3cbcdda",
68
+ "tree-sitter-requirements": "github:Strum355/tree-sitter-requirements#d0261ee76b84253997fe70d7d397e78c006c3801",
69
+ "web-tree-sitter": "^0.26.7",
70
+ "yargs": "^18.0.0"
56
71
  },
57
72
  "devDependencies": {
58
73
  "@babel/core": "^7.23.2",
59
- "@trustify-da/trustify-da-api-model": "^2.0.1",
60
- "@types/node": "^20.17.30",
74
+ "@cyclonedx/cyclonedx-library": "^10.2.0",
75
+ "@eslint/js": "^10.0.0",
76
+ "@trustify-da/trustify-da-api-model": "^2.0.12",
77
+ "@types/node": "^25.9.1",
61
78
  "@types/which": "^3.0.4",
79
+ "ajv": "^8.20.0",
62
80
  "babel-plugin-rewire": "^1.2.0",
63
- "c8": "^8.0.0",
64
- "chai": "^4.3.7",
65
- "eslint": "^8.42.0",
66
- "eslint-plugin-editorconfig": "^4.0.3",
67
- "eslint-plugin-import": "^2.29.1",
81
+ "c8": "^11.0.0",
82
+ "chai": "^6.2.2",
83
+ "eslint": "^10.4.1",
84
+ "eslint-import-resolver-typescript": "^4.4.4",
85
+ "eslint-plugin-import-x": "^4.16.2",
68
86
  "esmock": "^2.6.2",
69
- "mocha": "^10.2.0",
70
- "msw": "^1.3.2",
71
- "sinon": "^15.1.2",
72
- "sinon-chai": "^3.7.0",
73
- "typescript": "^5.1.3",
74
- "which": "^5.0.0"
87
+ "globals": "^17.6.0",
88
+ "mocha": "^11.7.6",
89
+ "msw": "^2.12.7",
90
+ "sinon": "^22.0.0",
91
+ "sinon-chai": "^4.0.1",
92
+ "typescript": "^6.0.3",
93
+ "which": "^7.0.0"
75
94
  },
76
95
  "mocha": {
77
96
  "check-leaks": false,
@@ -99,12 +118,13 @@
99
118
  "reporter": [
100
119
  "html",
101
120
  "json",
121
+ "lcov",
102
122
  "text"
103
123
  ]
104
124
  },
105
- "eslintIgnore": [
106
- "index.js"
107
- ],
125
+ "overrides": {
126
+ "tree-sitter": "0.22.4"
127
+ },
108
128
  "resolutions": {
109
129
  "@hapi/joi": "17.1.1"
110
130
  }
@@ -1,8 +1,12 @@
1
+ /** Media type for CycloneDX JSON batch payloads (batch-analysis API). */
2
+ export const CYCLONEDX_JSON_MEDIA_TYPE: "application/vnd.cyclonedx+json";
1
3
  declare namespace _default {
2
4
  export { requestComponent };
3
5
  export { requestStack };
6
+ export { requestStackBatch };
4
7
  export { requestImages };
5
8
  export { validateToken };
9
+ export { appendAnalysisQueryParams };
6
10
  }
7
11
  export default _default;
8
12
  /**
@@ -13,7 +17,7 @@ export default _default;
13
17
  * @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
14
18
  * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
15
19
  */
16
- declare function requestComponent(provider: import('./provider').Provider, manifest: string, url: string, opts?: import("index.js").Options | undefined): 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>;
17
21
  /**
18
22
  * Send a stack analysis request and get the report as 'text/html' or 'application/json'.
19
23
  * @param {import('./provider').Provider} provider - the provided data for constructing the request
@@ -23,7 +27,20 @@ declare function requestComponent(provider: import('./provider').Provider, manif
23
27
  * @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
24
28
  * @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
25
29
  */
26
- declare function requestStack(provider: import('./provider').Provider, manifest: string, url: string, html?: boolean | undefined, opts?: import("index.js").Options | undefined): 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>;
31
+ /**
32
+ * Send a batch stack analysis request for multiple manifests (SBOMs keyed by purl).
33
+ * @param {Object.<string, object>} sbomByPurl - Map of root purl to CycloneDX SBOM object
34
+ * @param {string} url - the backend url
35
+ * @param {boolean} [html=false] - true returns HTML, false returns JSON
36
+ * @param {import("index.js").Options} [opts={}]
37
+ * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
38
+ */
39
+ declare function requestStackBatch(sbomByPurl: {
40
+ [x: string]: object;
41
+ }, url: string, html?: boolean, opts?: any): Promise<string | {
42
+ [x: string]: any;
43
+ }>;
27
44
  /**
28
45
  *
29
46
  * @param {Array<string>} imageRefs
@@ -31,8 +48,8 @@ declare function requestStack(provider: import('./provider').Provider, manifest:
31
48
  * @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
32
49
  * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
33
50
  */
34
- declare function requestImages(imageRefs: Array<string>, url: string, html?: boolean, opts?: import("index.js").Options | undefined): Promise<string | {
35
- [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;
36
53
  }>;
37
54
  /**
38
55
  *
@@ -40,4 +57,10 @@ declare function requestImages(imageRefs: Array<string>, url: string, html?: boo
40
57
  * @param {import("index.js").Options} [opts={}] - optional various options to pass headers for t he validateToken Request
41
58
  * @return {Promise<number>} return the HTTP status Code of the response from the validate token request.
42
59
  */
43
- declare function validateToken(url: any, opts?: import("index.js").Options | undefined): 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;
@@ -1,28 +1,12 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { EOL } from "os";
4
- import { HttpsProxyAgent } from "https-proxy-agent";
4
+ import { runLicenseCheck } from "./license/index.js";
5
5
  import { generateImageSBOM, parseImageRef } from "./oci_image/utils.js";
6
- import { RegexNotToBeLogged, getCustom } from "./tools.js";
7
- export default { requestComponent, requestStack, requestImages, validateToken };
8
- const rhdaTokenHeader = "rhda-token";
9
- const rhdaTelemetryId = "rhda-telemetry-id";
10
- const rhdaSourceHeader = "rhda-source";
11
- const rhdaOperationTypeHeader = "rhda-operation-type";
12
- const rhdaPackageManagerHeader = "rhda-pkg-manager";
13
- /**
14
- * Adds proxy agent configuration to fetch options if a proxy URL is specified
15
- * @param {RequestInit} options - The base fetch options
16
- * @param {import("index.js").Options} opts - The trustify DA options that may contain proxy configuration
17
- * @returns {RequestInit} The fetch options with proxy agent if applicable
18
- */
19
- function addProxyAgent(options, opts) {
20
- const proxyUrl = getCustom('TRUSTIFY_DA_PROXY_URL', null, opts);
21
- if (proxyUrl) {
22
- options.agent = new HttpsProxyAgent(proxyUrl);
23
- }
24
- return options;
25
- }
6
+ import { addProxyAgent, getCustom, getTokenHeaders, TRUSTIFY_DA_OPERATION_TYPE_HEADER, TRUSTIFY_DA_PACKAGE_MANAGER_HEADER } from "./tools.js";
7
+ /** Media type for CycloneDX JSON batch payloads (batch-analysis API). */
8
+ export const CYCLONEDX_JSON_MEDIA_TYPE = 'application/vnd.cyclonedx+json';
9
+ export default { requestComponent, requestStack, requestStackBatch, requestImages, validateToken, appendAnalysisQueryParams };
26
10
  /**
27
11
  * Send a stack analysis request and get the report as 'text/html' or 'application/json'.
28
12
  * @param {import('./provider').Provider} provider - the provided data for constructing the request
@@ -35,15 +19,18 @@ function addProxyAgent(options, opts) {
35
19
  async function requestStack(provider, manifest, url, html = false, opts = {}) {
36
20
  opts["source-manifest"] = Buffer.from(fs.readFileSync(manifest).toString()).toString('base64');
37
21
  opts["manifest-type"] = path.parse(manifest).base;
38
- let provided = provider.provideStack(manifest, opts); // throws error if content providing failed
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
+ }
39
26
  opts["source-manifest"] = "";
40
- opts[rhdaOperationTypeHeader.toUpperCase().replaceAll("-", "_")] = "stack-analysis";
27
+ opts[TRUSTIFY_DA_OPERATION_TYPE_HEADER.toUpperCase().replaceAll("-", "_")] = "stack-analysis";
41
28
  let startTime = new Date();
42
29
  let endTime;
43
30
  if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
44
31
  console.log("Starting time of sending stack analysis request to the dependency analytics server= " + startTime);
45
32
  }
46
- opts[rhdaPackageManagerHeader.toUpperCase().replaceAll("-", "_")] = provided.ecosystem;
33
+ opts[TRUSTIFY_DA_PACKAGE_MANAGER_HEADER.toUpperCase().replaceAll("-", "_")] = provided.ecosystem;
47
34
  const fetchOptions = addProxyAgent({
48
35
  method: 'POST',
49
36
  headers: {
@@ -53,10 +40,8 @@ async function requestStack(provider, manifest, url, html = false, opts = {}) {
53
40
  },
54
41
  body: provided.content
55
42
  }, opts);
56
- const finalUrl = new URL(`${url}/api/v4/analysis`);
57
- if (opts['TRUSTIFY_DA_RECOMMENDATIONS_ENABLED'] === 'false') {
58
- finalUrl.searchParams.append('recommend', 'false');
59
- }
43
+ const finalUrl = new URL(`${url}/api/v5/analysis`);
44
+ appendAnalysisQueryParams(finalUrl, opts);
60
45
  let resp = await fetch(finalUrl, fetchOptions);
61
46
  let result;
62
47
  if (resp.status === 200) {
@@ -94,13 +79,16 @@ async function requestStack(provider, manifest, url, html = false, opts = {}) {
94
79
  */
95
80
  async function requestComponent(provider, manifest, url, opts = {}) {
96
81
  opts["source-manifest"] = Buffer.from(fs.readFileSync(manifest).toString()).toString('base64');
97
- let provided = provider.provideComponent(manifest, opts); // throws error if content providing failed
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
+ }
98
86
  opts["source-manifest"] = "";
99
- opts[rhdaOperationTypeHeader.toUpperCase().replaceAll("-", "_")] = "component-analysis";
87
+ opts[TRUSTIFY_DA_OPERATION_TYPE_HEADER.toUpperCase().replaceAll("-", "_")] = "component-analysis";
100
88
  if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
101
89
  console.log("Starting time of sending component analysis request to Trustify DA backend server= " + new Date());
102
90
  }
103
- opts[rhdaPackageManagerHeader.toUpperCase().replaceAll("-", "_")] = provided.ecosystem;
91
+ opts[TRUSTIFY_DA_PACKAGE_MANAGER_HEADER.toUpperCase().replaceAll("-", "_")] = provided.ecosystem;
104
92
  const fetchOptions = addProxyAgent({
105
93
  method: 'POST',
106
94
  headers: {
@@ -110,10 +98,8 @@ async function requestComponent(provider, manifest, url, opts = {}) {
110
98
  },
111
99
  body: provided.content
112
100
  }, opts);
113
- const finalUrl = new URL(`${url}/api/v4/analysis`);
114
- if (opts['TRUSTIFY_DA_RECOMMENDATIONS_ENABLED'] === 'false') {
115
- finalUrl.searchParams.append('recommend', 'false');
116
- }
101
+ const finalUrl = new URL(`${url}/api/v5/analysis`);
102
+ appendAnalysisQueryParams(finalUrl, opts);
117
103
  let resp = await fetch(finalUrl, fetchOptions);
118
104
  let result;
119
105
  if (resp.status === 200) {
@@ -127,12 +113,65 @@ async function requestComponent(provider, manifest, url, opts = {}) {
127
113
  console.log(JSON.stringify(result, null, 4));
128
114
  console.log("Ending time of sending component analysis request to Trustify DA backend server= " + new Date());
129
115
  }
116
+ const licenseCheckEnabled = getCustom('TRUSTIFY_DA_LICENSE_CHECK', 'true', opts) !== 'false' && opts.licenseCheck !== false;
117
+ if (licenseCheckEnabled) {
118
+ try {
119
+ result.licenseSummary = await runLicenseCheck(provided.content, manifest, url, opts, result);
120
+ }
121
+ catch (licenseErr) {
122
+ result.licenseSummary = { error: licenseErr.message };
123
+ }
124
+ }
130
125
  }
131
126
  else {
132
127
  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()}`);
133
128
  }
134
129
  return Promise.resolve(result);
135
130
  }
131
+ /**
132
+ * Send a batch stack analysis request for multiple manifests (SBOMs keyed by purl).
133
+ * @param {Object.<string, object>} sbomByPurl - Map of root purl to CycloneDX SBOM object
134
+ * @param {string} url - the backend url
135
+ * @param {boolean} [html=false] - true returns HTML, false returns JSON
136
+ * @param {import("index.js").Options} [opts={}]
137
+ * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
138
+ */
139
+ async function requestStackBatch(sbomByPurl, url, html = false, opts = {}) {
140
+ const finalUrl = new URL(`${url}/api/v5/batch-analysis`);
141
+ appendAnalysisQueryParams(finalUrl, opts);
142
+ const fetchOptions = addProxyAgent({
143
+ method: 'POST',
144
+ headers: {
145
+ 'Accept': html ? 'text/html' : 'application/json',
146
+ 'Content-Type': CYCLONEDX_JSON_MEDIA_TYPE,
147
+ ...getTokenHeaders(opts)
148
+ },
149
+ body: JSON.stringify(sbomByPurl)
150
+ }, opts);
151
+ const resp = await fetch(finalUrl, fetchOptions);
152
+ if (resp.status === 200) {
153
+ let result;
154
+ if (!html) {
155
+ result = await resp.json();
156
+ }
157
+ else {
158
+ result = await resp.text();
159
+ }
160
+ if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
161
+ const exRequestId = resp.headers.get("ex-request-id");
162
+ if (exRequestId) {
163
+ console.log("Unique Identifier associated with this request - ex-request-id=" + exRequestId);
164
+ }
165
+ console.log("Response body received from Trustify DA backend server : " + EOL + EOL);
166
+ console.log(JSON.stringify(result, null, 4));
167
+ console.log("Ending time of sending batch stack analysis request to Trustify DA backend server= " + new Date());
168
+ }
169
+ return result;
170
+ }
171
+ else {
172
+ 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()}`);
173
+ }
174
+ }
136
175
  /**
137
176
  *
138
177
  * @param {Array<string>} imageRefs
@@ -146,19 +185,18 @@ async function requestImages(imageRefs, url, html = false, opts = {}) {
146
185
  const parsedImageRef = parseImageRef(image, opts);
147
186
  imageSboms[parsedImageRef.getPackageURL().toString()] = generateImageSBOM(parsedImageRef, opts);
148
187
  }
149
- const finalUrl = new URL(`${url}/api/v4/batch-analysis`);
150
- if (opts['TRUSTIFY_DA_RECOMMENDATIONS_ENABLED'] === 'false') {
151
- finalUrl.searchParams.append('recommend', 'false');
152
- }
153
- const resp = await fetch(finalUrl, {
188
+ const finalUrl = new URL(`${url}/api/v5/batch-analysis`);
189
+ appendAnalysisQueryParams(finalUrl, opts);
190
+ const fetchOptions = addProxyAgent({
154
191
  method: 'POST',
155
192
  headers: {
156
193
  'Accept': html ? 'text/html' : 'application/json',
157
- 'Content-Type': 'application/vnd.cyclonedx+json',
194
+ 'Content-Type': CYCLONEDX_JSON_MEDIA_TYPE,
158
195
  ...getTokenHeaders(opts)
159
196
  },
160
197
  body: JSON.stringify(imageSboms),
161
- });
198
+ }, opts);
199
+ const resp = await fetch(finalUrl, fetchOptions);
162
200
  if (resp.status === 200) {
163
201
  let result;
164
202
  if (!html) {
@@ -182,6 +220,24 @@ async function requestImages(imageRefs, url, html = false, opts = {}) {
182
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()}`);
183
221
  }
184
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
+ }
185
241
  /**
186
242
  *
187
243
  * @param url the backend url to send the request to
@@ -195,7 +251,7 @@ async function validateToken(url, opts = {}) {
195
251
  ...getTokenHeaders(opts),
196
252
  }
197
253
  }, opts);
198
- let resp = await fetch(`${url}/api/v4/token`, fetchOptions);
254
+ let resp = await fetch(`${url}/api/v5/token`, fetchOptions);
199
255
  if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
200
256
  let exRequestId = resp.headers.get("ex-request-id");
201
257
  if (exRequestId) {
@@ -204,49 +260,3 @@ async function validateToken(url, opts = {}) {
204
260
  }
205
261
  return resp.status;
206
262
  }
207
- /**
208
- *
209
- * @param {string} headerName - the header name to populate in request
210
- * @param headers
211
- * @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
212
- * @private
213
- */
214
- function setRhdaHeader(headerName, headers, opts) {
215
- let rhdaHeaderValue = getCustom(headerName.toUpperCase().replaceAll("-", "_"), null, opts);
216
- if (rhdaHeaderValue) {
217
- headers[headerName] = rhdaHeaderValue;
218
- }
219
- }
220
- /**
221
- * Utility function for fetching vendor tokens
222
- * @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
223
- * @returns {{}}
224
- */
225
- function getTokenHeaders(opts = {}) {
226
- let supportedTokens = ['snyk', 'oss-index'];
227
- let headers = {};
228
- supportedTokens.forEach(vendor => {
229
- let token = getCustom(`TRUSTIFY_DA_${vendor.replace("-", "_").toUpperCase()}_TOKEN`, null, opts);
230
- if (token) {
231
- headers[`ex-${vendor}-token`] = token;
232
- }
233
- let user = getCustom(`TRUSTIFY_DA_${vendor.replace("-", "_").toUpperCase()}_USER`, null, opts);
234
- if (user) {
235
- headers[`ex-${vendor}-user`] = user;
236
- }
237
- });
238
- setRhdaHeader(rhdaTokenHeader, headers, opts);
239
- setRhdaHeader(rhdaSourceHeader, headers, opts);
240
- setRhdaHeader(rhdaOperationTypeHeader, headers, opts);
241
- setRhdaHeader(rhdaPackageManagerHeader, headers, opts);
242
- setRhdaHeader(rhdaTelemetryId, headers, opts);
243
- if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
244
- console.log("Headers Values to be sent to Trustify DA backend:" + EOL);
245
- for (const headerKey in headers) {
246
- if (!headerKey.match(RegexNotToBeLogged)) {
247
- console.log(`${headerKey}: ${headers[headerKey]}`);
248
- }
249
- }
250
- }
251
- return headers;
252
- }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Whether to skip failed manifests and continue (default), or fail on first SBOM/validation error.
3
+ * `opts.continueOnError` overrides; env `TRUSTIFY_DA_CONTINUE_ON_ERROR=false` disables continuation.
4
+ *
5
+ * @param {{ continueOnError?: boolean, TRUSTIFY_DA_CONTINUE_ON_ERROR?: string, [key: string]: unknown }} [opts={}]
6
+ * @returns {boolean} true = collect errors (default), false = fail-fast
7
+ */
8
+ export function resolveContinueOnError(opts?: {
9
+ continueOnError?: boolean;
10
+ TRUSTIFY_DA_CONTINUE_ON_ERROR?: string;
11
+ [key: string]: unknown;
12
+ }): boolean;
13
+ /**
14
+ * When true, `stackAnalysisBatch` returns `{ analysis, metadata }` instead of the backend response only.
15
+ * `opts.batchMetadata` overrides; env `TRUSTIFY_DA_BATCH_METADATA=true` enables.
16
+ *
17
+ * @param {{ batchMetadata?: boolean, TRUSTIFY_DA_BATCH_METADATA?: string, [key: string]: unknown }} [opts={}]
18
+ * @returns {boolean}
19
+ */
20
+ export function resolveBatchMetadata(opts?: {
21
+ batchMetadata?: boolean;
22
+ TRUSTIFY_DA_BATCH_METADATA?: string;
23
+ [key: string]: unknown;
24
+ }): boolean;
@@ -0,0 +1,35 @@
1
+ import { getCustom } from './tools.js';
2
+ /**
3
+ * Whether to skip failed manifests and continue (default), or fail on first SBOM/validation error.
4
+ * `opts.continueOnError` overrides; env `TRUSTIFY_DA_CONTINUE_ON_ERROR=false` disables continuation.
5
+ *
6
+ * @param {{ continueOnError?: boolean, TRUSTIFY_DA_CONTINUE_ON_ERROR?: string, [key: string]: unknown }} [opts={}]
7
+ * @returns {boolean} true = collect errors (default), false = fail-fast
8
+ */
9
+ export function resolveContinueOnError(opts = {}) {
10
+ if (typeof opts.continueOnError === 'boolean') {
11
+ return opts.continueOnError;
12
+ }
13
+ const v = getCustom('TRUSTIFY_DA_CONTINUE_ON_ERROR', null, opts);
14
+ if (v != null && String(v).trim() !== '') {
15
+ return String(v).toLowerCase() !== 'false';
16
+ }
17
+ return true;
18
+ }
19
+ /**
20
+ * When true, `stackAnalysisBatch` returns `{ analysis, metadata }` instead of the backend response only.
21
+ * `opts.batchMetadata` overrides; env `TRUSTIFY_DA_BATCH_METADATA=true` enables.
22
+ *
23
+ * @param {{ batchMetadata?: boolean, TRUSTIFY_DA_BATCH_METADATA?: string, [key: string]: unknown }} [opts={}]
24
+ * @returns {boolean}
25
+ */
26
+ export function resolveBatchMetadata(opts = {}) {
27
+ if (typeof opts.batchMetadata === 'boolean') {
28
+ return opts.batchMetadata;
29
+ }
30
+ const v = getCustom('TRUSTIFY_DA_BATCH_METADATA', null, opts);
31
+ if (v != null && String(v).trim() !== '') {
32
+ return String(v).toLowerCase() === 'true';
33
+ }
34
+ return false;
35
+ }