@trustify-da/trustify-da-javascript-client 0.3.0-ea.3aa725a → 0.3.0-ea.41ca049

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 (99) hide show
  1. package/README.md +388 -20
  2. package/dist/package.json +48 -31
  3. package/dist/src/analysis.d.ts +58 -14
  4. package/dist/src/analysis.js +136 -90
  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 +427 -12
  8. package/dist/src/config.d.ts +120 -0
  9. package/dist/src/config.js +260 -0
  10. package/dist/src/cyclone_dx_sbom.d.ts +30 -3
  11. package/dist/src/cyclone_dx_sbom.js +95 -9
  12. package/dist/src/index.d.ts +208 -17
  13. package/dist/src/index.js +376 -39
  14. package/dist/src/license/index.d.ts +28 -0
  15. package/dist/src/license/index.js +106 -0
  16. package/dist/src/license/license_utils.d.ts +40 -0
  17. package/dist/src/license/license_utils.js +137 -0
  18. package/dist/src/license/licenses_api.d.ts +34 -0
  19. package/dist/src/license/licenses_api.js +98 -0
  20. package/dist/src/license/project_license.d.ts +20 -0
  21. package/dist/src/license/project_license.js +62 -0
  22. package/dist/src/oci_image/images.d.ts +4 -5
  23. package/dist/src/oci_image/images.js +15 -1
  24. package/dist/src/oci_image/utils.d.ts +7 -7
  25. package/dist/src/oci_image/utils.js +11 -2
  26. package/dist/src/package_version.d.ts +8 -0
  27. package/dist/src/package_version.js +31 -0
  28. package/dist/src/provider.d.ts +18 -5
  29. package/dist/src/provider.js +33 -5
  30. package/dist/src/providers/base_java.d.ts +68 -15
  31. package/dist/src/providers/base_java.js +82 -61
  32. package/dist/src/providers/base_javascript.d.ts +99 -12
  33. package/dist/src/providers/base_javascript.js +244 -28
  34. package/dist/src/providers/base_pyproject.d.ts +158 -0
  35. package/dist/src/providers/base_pyproject.js +322 -0
  36. package/dist/src/providers/containerfile_parser.d.ts +5 -0
  37. package/dist/src/providers/containerfile_parser.js +20 -0
  38. package/dist/src/providers/golang_gomodules.d.ts +30 -13
  39. package/dist/src/providers/golang_gomodules.js +219 -122
  40. package/dist/src/providers/gomod_parser.d.ts +4 -0
  41. package/dist/src/providers/gomod_parser.js +16 -0
  42. package/dist/src/providers/java_gradle.d.ts +76 -3
  43. package/dist/src/providers/java_gradle.js +323 -21
  44. package/dist/src/providers/java_gradle_groovy.d.ts +1 -1
  45. package/dist/src/providers/java_gradle_kotlin.d.ts +1 -1
  46. package/dist/src/providers/java_maven.d.ts +45 -10
  47. package/dist/src/providers/java_maven.js +300 -14
  48. package/dist/src/providers/javascript_bun.d.ts +22 -0
  49. package/dist/src/providers/javascript_bun.js +141 -0
  50. package/dist/src/providers/javascript_npm.d.ts +19 -0
  51. package/dist/src/providers/javascript_npm.js +61 -1
  52. package/dist/src/providers/javascript_pnpm.d.ts +14 -1
  53. package/dist/src/providers/javascript_pnpm.js +55 -5
  54. package/dist/src/providers/javascript_yarn.d.ts +12 -0
  55. package/dist/src/providers/javascript_yarn.js +75 -2
  56. package/dist/src/providers/manifest.d.ts +2 -0
  57. package/dist/src/providers/manifest.js +28 -7
  58. package/dist/src/providers/marker_evaluator.d.ts +14 -0
  59. package/dist/src/providers/marker_evaluator.js +191 -0
  60. package/dist/src/providers/oci_dockerfile.d.ts +51 -0
  61. package/dist/src/providers/oci_dockerfile.js +177 -0
  62. package/dist/src/providers/processors/yarn_berry_processor.d.ts +6 -2
  63. package/dist/src/providers/processors/yarn_berry_processor.js +91 -7
  64. package/dist/src/providers/processors/yarn_classic_processor.d.ts +6 -2
  65. package/dist/src/providers/processors/yarn_classic_processor.js +8 -6
  66. package/dist/src/providers/python_controller.d.ts +10 -3
  67. package/dist/src/providers/python_controller.js +122 -63
  68. package/dist/src/providers/python_pip.d.ts +16 -4
  69. package/dist/src/providers/python_pip.js +51 -58
  70. package/dist/src/providers/python_pip_pyproject.d.ts +61 -0
  71. package/dist/src/providers/python_pip_pyproject.js +146 -0
  72. package/dist/src/providers/python_poetry.d.ts +98 -0
  73. package/dist/src/providers/python_poetry.js +286 -0
  74. package/dist/src/providers/python_uv.d.ts +46 -0
  75. package/dist/src/providers/python_uv.js +269 -0
  76. package/dist/src/providers/requirements_parser.d.ts +6 -0
  77. package/dist/src/providers/requirements_parser.js +24 -0
  78. package/dist/src/providers/rust_cargo.d.ts +56 -0
  79. package/dist/src/providers/rust_cargo.js +682 -0
  80. package/dist/src/providers/tree-sitter-containerfile.wasm +0 -0
  81. package/dist/src/providers/tree-sitter-gomod.wasm +0 -0
  82. package/dist/src/providers/tree-sitter-requirements.wasm +0 -0
  83. package/dist/src/remediate.d.ts +120 -0
  84. package/dist/src/remediate.js +253 -0
  85. package/dist/src/remediation.d.ts +58 -0
  86. package/dist/src/remediation.js +425 -0
  87. package/dist/src/remediation_report.d.ts +40 -0
  88. package/dist/src/remediation_report.js +159 -0
  89. package/dist/src/sbom.d.ts +28 -2
  90. package/dist/src/sbom.js +26 -4
  91. package/dist/src/tools.d.ts +57 -17
  92. package/dist/src/tools.js +148 -14
  93. package/dist/src/updaters/maven_updater.d.ts +58 -0
  94. package/dist/src/updaters/maven_updater.js +345 -0
  95. package/dist/src/updaters/toml_updater.d.ts +62 -0
  96. package/dist/src/updaters/toml_updater.js +276 -0
  97. package/dist/src/workspace.d.ts +71 -0
  98. package/dist/src/workspace.js +257 -0
  99. package/package.json +49 -32
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,57 @@
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
- "@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",
52
+ "@yarnpkg/parsers": "^3.1.0",
53
+ "eslint-import-resolver-typescript": "^4.4.4",
54
+ "fast-glob": "^3.3.3",
55
+ "fast-xml-parser": "^5.3.4",
51
56
  "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"
57
+ "https-proxy-agent": "^9.0.0",
58
+ "js-yaml": "^4.1.1",
59
+ "jsonc-parser": "^3.3.1",
60
+ "micromatch": "^4.0.8",
61
+ "node-fetch": "^3.3.2",
62
+ "p-limit": "^7.3.0",
63
+ "packageurl-js": "~1.0.2",
64
+ "smol-toml": "^1.6.0",
65
+ "tree-sitter-containerfile": "^0.8.1",
66
+ "tree-sitter-gomod": "github:strum355/tree-sitter-go-mod#56326f2ad478892ace58ff247a97d492a3cbcdda",
67
+ "tree-sitter-requirements": "github:Strum355/tree-sitter-requirements#d0261ee76b84253997fe70d7d397e78c006c3801",
68
+ "web-tree-sitter": "^0.26.7",
69
+ "yargs": "^18.0.0"
56
70
  },
57
71
  "devDependencies": {
58
- "@babel/core": "^7.23.2",
59
- "@trustify-da/trustify-da-api-model": "^2.0.1",
60
- "@types/node": "^20.17.30",
72
+ "@cyclonedx/cyclonedx-library": "^10.2.0",
73
+ "@eslint/js": "^10.0.0",
74
+ "@trustify-da/trustify-da-api-model": "^2.0.12",
75
+ "@types/node": "^25.9.1",
61
76
  "@types/which": "^3.0.4",
62
- "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",
77
+ "ajv": "^8.20.0",
78
+ "c8": "^11.0.0",
79
+ "chai": "^6.2.2",
80
+ "eslint": "^10.4.1",
81
+ "eslint-import-resolver-typescript": "^4.4.4",
82
+ "eslint-plugin-import-x": "^4.16.2",
68
83
  "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"
84
+ "globals": "^17.6.0",
85
+ "mocha": "^11.7.6",
86
+ "msw": "^2.12.7",
87
+ "sinon": "^22.0.0",
88
+ "sinon-chai": "^4.0.1",
89
+ "typescript": "^6.0.3",
90
+ "which": "^7.0.0"
75
91
  },
76
92
  "mocha": {
77
93
  "check-leaks": false,
@@ -99,12 +115,13 @@
99
115
  "reporter": [
100
116
  "html",
101
117
  "json",
118
+ "lcov",
102
119
  "text"
103
120
  ]
104
121
  },
105
- "eslintIgnore": [
106
- "index.js"
107
- ],
122
+ "overrides": {
123
+ "tree-sitter": "0.22.4"
124
+ },
108
125
  "resolutions": {
109
126
  "@hapi/joi": "17.1.1"
110
127
  }
@@ -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
  /**
@@ -11,28 +15,62 @@ export default _default;
11
15
  * @param {string} manifest - path for the manifest
12
16
  * @param {string} url - the backend url to send the request to
13
17
  * @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
14
- * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
18
+ * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').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<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport>;
17
21
  /**
18
- * Send a stack analysis request and get the report as 'text/html' or 'application/json'.
19
- * @param {import('./provider').Provider} provider - the provided data for constructing the request
20
- * @param {string} manifest - path for the manifest
21
- * @param {string} url - the backend url to send the request to
22
- * @param {boolean} [html=false] - true will return 'text/html', false will return 'application/json'
23
- * @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
24
- * @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
22
+ * @overload
23
+ * @param {import('./provider').Provider} provider
24
+ * @param {string} manifest
25
+ * @param {string} url
26
+ * @param {true} [html]
27
+ * @param {import("index.js").Options} [opts={}]
28
+ * @returns {Promise<string>}
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?: true | undefined, opts?: any): Promise<string>;
31
+ /**
32
+ * @overload
33
+ * @param {import('./provider').Provider} provider
34
+ * @param {string} manifest
35
+ * @param {string} url
36
+ * @param {false} [html]
37
+ * @param {import("index.js").Options} [opts={}]
38
+ * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
39
+ */
40
+ declare function requestStack(provider: any, manifest: string, url: string, html?: false | undefined, opts?: any): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport>;
41
+ /**
42
+ * @overload
43
+ * @param {Object.<string, object>} sbomByPurl
44
+ * @param {string} url
45
+ * @param {true} [html=false]
46
+ * @param {import("index.js").Options} [opts={}]
47
+ * @returns {Promise<string>}
48
+ */
49
+ declare function requestStackBatch(sbomByPurl: {
50
+ [x: string]: object;
51
+ }, url: string, html?: true | undefined, opts?: any): Promise<string>;
52
+ /**
53
+ * @overload
54
+ * @param {Object.<string, object>} sbomByPurl
55
+ * @param {string} url
56
+ * @param {false} [html=false]
57
+ * @param {import("index.js").Options} [opts={}]
58
+ * @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
59
+ */
60
+ declare function requestStackBatch(sbomByPurl: {
61
+ [x: string]: object;
62
+ }, url: string, html?: false | undefined, opts?: any): Promise<{
63
+ [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
64
+ }>;
27
65
  /**
28
66
  *
29
67
  * @param {Array<string>} imageRefs
30
68
  * @param {string} url
31
69
  * @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
32
- * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
70
+ * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
33
71
  */
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;
72
+ declare function requestImages(imageRefs: Array<string>, url: string, html?: boolean, opts?: any): Promise<string | {
73
+ [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
36
74
  }>;
37
75
  /**
38
76
  *
@@ -40,4 +78,10 @@ declare function requestImages(imageRefs: Array<string>, url: string, html?: boo
40
78
  * @param {import("index.js").Options} [opts={}] - optional various options to pass headers for t he validateToken Request
41
79
  * @return {Promise<number>} return the HTTP status Code of the response from the validate token request.
42
80
  */
43
- declare function validateToken(url: any, opts?: import("index.js").Options | undefined): Promise<number>;
81
+ declare function validateToken(url: any, opts?: any): Promise<number>;
82
+ /**
83
+ * Append common analysis query parameters (recommend, providers, sources) to a URL.
84
+ * @param {URL} url - the URL object to append query parameters to
85
+ * @param {import("index.js").Options} [opts={}] - options containing parameter overrides
86
+ */
87
+ declare function appendAnalysisQueryParams(url: URL, opts?: any): void;
@@ -1,28 +1,30 @@
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";
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 };
13
10
  /**
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
11
+ * @overload
12
+ * @param {import('./provider').Provider} provider
13
+ * @param {string} manifest
14
+ * @param {string} url
15
+ * @param {true} [html]
16
+ * @param {import("index.js").Options} [opts={}]
17
+ * @returns {Promise<string>}
18
+ */
19
+ /**
20
+ * @overload
21
+ * @param {import('./provider').Provider} provider
22
+ * @param {string} manifest
23
+ * @param {string} url
24
+ * @param {false} [html]
25
+ * @param {import("index.js").Options} [opts={}]
26
+ * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
18
27
  */
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
- }
26
28
  /**
27
29
  * Send a stack analysis request and get the report as 'text/html' or 'application/json'.
28
30
  * @param {import('./provider').Provider} provider - the provided data for constructing the request
@@ -30,20 +32,23 @@ function addProxyAgent(options, opts) {
30
32
  * @param {string} url - the backend url to send the request to
31
33
  * @param {boolean} [html=false] - true will return 'text/html', false will return 'application/json'
32
34
  * @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
33
- * @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
35
+ * @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
34
36
  */
35
37
  async function requestStack(provider, manifest, url, html = false, opts = {}) {
36
38
  opts["source-manifest"] = Buffer.from(fs.readFileSync(manifest).toString()).toString('base64');
37
39
  opts["manifest-type"] = path.parse(manifest).base;
38
- let provided = provider.provideStack(manifest, opts); // throws error if content providing failed
40
+ let provided = await provider.provideStack(manifest, opts); // throws error if content providing failed
41
+ if (provided.batch) {
42
+ return requestStackBatch(JSON.parse(provided.content), url, html, opts);
43
+ }
39
44
  opts["source-manifest"] = "";
40
- opts[rhdaOperationTypeHeader.toUpperCase().replaceAll("-", "_")] = "stack-analysis";
45
+ opts[TRUSTIFY_DA_OPERATION_TYPE_HEADER.toUpperCase().replaceAll("-", "_")] = "stack-analysis";
41
46
  let startTime = new Date();
42
47
  let endTime;
43
48
  if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
44
49
  console.log("Starting time of sending stack analysis request to the dependency analytics server= " + startTime);
45
50
  }
46
- opts[rhdaPackageManagerHeader.toUpperCase().replaceAll("-", "_")] = provided.ecosystem;
51
+ opts[TRUSTIFY_DA_PACKAGE_MANAGER_HEADER.toUpperCase().replaceAll("-", "_")] = provided.ecosystem;
47
52
  const fetchOptions = addProxyAgent({
48
53
  method: 'POST',
49
54
  headers: {
@@ -53,10 +58,8 @@ async function requestStack(provider, manifest, url, html = false, opts = {}) {
53
58
  },
54
59
  body: provided.content
55
60
  }, 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
- }
61
+ const finalUrl = new URL(`${url}/api/v5/analysis`);
62
+ appendAnalysisQueryParams(finalUrl, opts);
60
63
  let resp = await fetch(finalUrl, fetchOptions);
61
64
  let result;
62
65
  if (resp.status === 200) {
@@ -90,17 +93,20 @@ async function requestStack(provider, manifest, url, html = false, opts = {}) {
90
93
  * @param {string} manifest - path for the manifest
91
94
  * @param {string} url - the backend url to send the request to
92
95
  * @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
93
- * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
96
+ * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
94
97
  */
95
98
  async function requestComponent(provider, manifest, url, opts = {}) {
96
99
  opts["source-manifest"] = Buffer.from(fs.readFileSync(manifest).toString()).toString('base64');
97
- let provided = provider.provideComponent(manifest, opts); // throws error if content providing failed
100
+ let provided = await provider.provideComponent(manifest, opts); // throws error if content providing failed
101
+ if (provided.batch) {
102
+ return requestStackBatch(JSON.parse(provided.content), url, false, opts);
103
+ }
98
104
  opts["source-manifest"] = "";
99
- opts[rhdaOperationTypeHeader.toUpperCase().replaceAll("-", "_")] = "component-analysis";
105
+ opts[TRUSTIFY_DA_OPERATION_TYPE_HEADER.toUpperCase().replaceAll("-", "_")] = "component-analysis";
100
106
  if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
101
107
  console.log("Starting time of sending component analysis request to Trustify DA backend server= " + new Date());
102
108
  }
103
- opts[rhdaPackageManagerHeader.toUpperCase().replaceAll("-", "_")] = provided.ecosystem;
109
+ opts[TRUSTIFY_DA_PACKAGE_MANAGER_HEADER.toUpperCase().replaceAll("-", "_")] = provided.ecosystem;
104
110
  const fetchOptions = addProxyAgent({
105
111
  method: 'POST',
106
112
  headers: {
@@ -110,10 +116,8 @@ async function requestComponent(provider, manifest, url, opts = {}) {
110
116
  },
111
117
  body: provided.content
112
118
  }, 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
- }
119
+ const finalUrl = new URL(`${url}/api/v5/analysis`);
120
+ appendAnalysisQueryParams(finalUrl, opts);
117
121
  let resp = await fetch(finalUrl, fetchOptions);
118
122
  let result;
119
123
  if (resp.status === 200) {
@@ -127,18 +131,89 @@ async function requestComponent(provider, manifest, url, opts = {}) {
127
131
  console.log(JSON.stringify(result, null, 4));
128
132
  console.log("Ending time of sending component analysis request to Trustify DA backend server= " + new Date());
129
133
  }
134
+ const licenseCheckEnabled = typeof opts.licenseCheck === 'boolean'
135
+ ? opts.licenseCheck
136
+ : getCustom('TRUSTIFY_DA_LICENSE_CHECK', 'true', opts) !== 'false';
137
+ if (licenseCheckEnabled) {
138
+ try {
139
+ result.licenseSummary = await runLicenseCheck(provided.content, manifest, url, opts, result);
140
+ }
141
+ catch (licenseErr) {
142
+ result.licenseSummary = { error: licenseErr.message };
143
+ }
144
+ }
130
145
  }
131
146
  else {
132
147
  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
148
  }
134
149
  return Promise.resolve(result);
135
150
  }
151
+ /**
152
+ * @overload
153
+ * @param {Object.<string, object>} sbomByPurl
154
+ * @param {string} url
155
+ * @param {true} [html=false]
156
+ * @param {import("index.js").Options} [opts={}]
157
+ * @returns {Promise<string>}
158
+ */
159
+ /**
160
+ * @overload
161
+ * @param {Object.<string, object>} sbomByPurl
162
+ * @param {string} url
163
+ * @param {false} [html=false]
164
+ * @param {import("index.js").Options} [opts={}]
165
+ * @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
166
+ */
167
+ /**
168
+ * Send a batch stack analysis request for multiple manifests (SBOMs keyed by purl).
169
+ * @param {Object.<string, object>} sbomByPurl - Map of root purl to CycloneDX SBOM object
170
+ * @param {string} url - the backend url
171
+ * @param {boolean} [html=false] - true returns HTML, false returns JSON
172
+ * @param {import("index.js").Options} [opts={}]
173
+ * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
174
+ */
175
+ async function requestStackBatch(sbomByPurl, url, html = false, opts = {}) {
176
+ const finalUrl = new URL(`${url}/api/v5/batch-analysis`);
177
+ appendAnalysisQueryParams(finalUrl, opts);
178
+ const fetchOptions = addProxyAgent({
179
+ method: 'POST',
180
+ headers: {
181
+ 'Accept': html ? 'text/html' : 'application/json',
182
+ 'Content-Type': CYCLONEDX_JSON_MEDIA_TYPE,
183
+ ...getTokenHeaders(opts)
184
+ },
185
+ body: JSON.stringify(sbomByPurl)
186
+ }, opts);
187
+ const resp = await fetch(finalUrl, fetchOptions);
188
+ if (resp.status === 200) {
189
+ let result;
190
+ if (!html) {
191
+ result = await resp.json();
192
+ }
193
+ else {
194
+ result = await resp.text();
195
+ }
196
+ if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
197
+ const exRequestId = resp.headers.get("ex-request-id");
198
+ if (exRequestId) {
199
+ console.log("Unique Identifier associated with this request - ex-request-id=" + exRequestId);
200
+ }
201
+ console.log("Response body received from Trustify DA backend server : " + EOL + EOL);
202
+ console.log(JSON.stringify(result, null, 4));
203
+ console.log("Ending time of sending batch stack analysis request to Trustify DA backend server= " + new Date());
204
+ }
205
+ return result;
206
+ }
207
+ else {
208
+ 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()}`);
209
+ }
210
+ }
136
211
  /**
137
212
  *
138
213
  * @param {Array<string>} imageRefs
139
214
  * @param {string} url
140
215
  * @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
141
- * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
216
+ * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
142
217
  */
143
218
  async function requestImages(imageRefs, url, html = false, opts = {}) {
144
219
  const imageSboms = {};
@@ -146,19 +221,18 @@ async function requestImages(imageRefs, url, html = false, opts = {}) {
146
221
  const parsedImageRef = parseImageRef(image, opts);
147
222
  imageSboms[parsedImageRef.getPackageURL().toString()] = generateImageSBOM(parsedImageRef, opts);
148
223
  }
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, {
224
+ const finalUrl = new URL(`${url}/api/v5/batch-analysis`);
225
+ appendAnalysisQueryParams(finalUrl, opts);
226
+ const fetchOptions = addProxyAgent({
154
227
  method: 'POST',
155
228
  headers: {
156
229
  'Accept': html ? 'text/html' : 'application/json',
157
- 'Content-Type': 'application/vnd.cyclonedx+json',
230
+ 'Content-Type': CYCLONEDX_JSON_MEDIA_TYPE,
158
231
  ...getTokenHeaders(opts)
159
232
  },
160
233
  body: JSON.stringify(imageSboms),
161
- });
234
+ }, opts);
235
+ const resp = await fetch(finalUrl, fetchOptions);
162
236
  if (resp.status === 200) {
163
237
  let result;
164
238
  if (!html) {
@@ -182,6 +256,24 @@ async function requestImages(imageRefs, url, html = false, opts = {}) {
182
256
  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
257
  }
184
258
  }
259
+ /**
260
+ * Append common analysis query parameters (recommend, providers, sources) to a URL.
261
+ * @param {URL} url - the URL object to append query parameters to
262
+ * @param {import("index.js").Options} [opts={}] - options containing parameter overrides
263
+ */
264
+ function appendAnalysisQueryParams(url, opts = {}) {
265
+ if (getCustom('TRUSTIFY_DA_RECOMMEND', 'true', opts) === 'false') {
266
+ url.searchParams.append('recommend', 'false');
267
+ }
268
+ const providers = getCustom('TRUSTIFY_DA_PROVIDERS', null, opts);
269
+ if (providers) {
270
+ url.searchParams.append('providers', providers);
271
+ }
272
+ const sources = getCustom('TRUSTIFY_DA_SOURCES', null, opts);
273
+ if (sources) {
274
+ url.searchParams.append('sources', sources);
275
+ }
276
+ }
185
277
  /**
186
278
  *
187
279
  * @param url the backend url to send the request to
@@ -195,7 +287,7 @@ async function validateToken(url, opts = {}) {
195
287
  ...getTokenHeaders(opts),
196
288
  }
197
289
  }, opts);
198
- let resp = await fetch(`${url}/api/v4/token`, fetchOptions);
290
+ let resp = await fetch(`${url}/api/v5/token`, fetchOptions);
199
291
  if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
200
292
  let exRequestId = resp.headers.get("ex-request-id");
201
293
  if (exRequestId) {
@@ -204,49 +296,3 @@ async function validateToken(url, opts = {}) {
204
296
  }
205
297
  return resp.status;
206
298
  }
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
+ }