@trustify-da/trustify-da-javascript-client 0.3.0-ea.320ec49 → 0.3.0-ea.328523e

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 (86) hide show
  1. package/README.md +199 -11
  2. package/dist/package.json +30 -26
  3. package/dist/src/analysis.d.ts +50 -22
  4. package/dist/src/analysis.js +70 -18
  5. package/dist/src/cli.js +188 -6
  6. package/dist/src/config.d.ts +120 -0
  7. package/dist/src/config.js +260 -0
  8. package/dist/src/cyclone_dx_sbom.d.ts +20 -1
  9. package/dist/src/cyclone_dx_sbom.js +68 -9
  10. package/dist/src/index.d.ts +85 -22
  11. package/dist/src/index.js +96 -41
  12. package/dist/src/license/index.d.ts +4 -4
  13. package/dist/src/license/index.js +13 -7
  14. package/dist/src/license/license_utils.js +4 -1
  15. package/dist/src/license/licenses_api.d.ts +3 -3
  16. package/dist/src/license/licenses_api.js +1 -1
  17. package/dist/src/oci_image/images.d.ts +1 -1
  18. package/dist/src/oci_image/images.js +15 -1
  19. package/dist/src/oci_image/utils.d.ts +5 -5
  20. package/dist/src/package_version.d.ts +8 -0
  21. package/dist/src/package_version.js +31 -0
  22. package/dist/src/provider.d.ts +4 -3
  23. package/dist/src/provider.js +6 -2
  24. package/dist/src/providers/base_java.d.ts +67 -12
  25. package/dist/src/providers/base_java.js +82 -61
  26. package/dist/src/providers/base_javascript.d.ts +74 -9
  27. package/dist/src/providers/base_javascript.js +116 -7
  28. package/dist/src/providers/base_pyproject.d.ts +15 -6
  29. package/dist/src/providers/base_pyproject.js +12 -4
  30. package/dist/src/providers/containerfile_parser.d.ts +5 -0
  31. package/dist/src/providers/containerfile_parser.js +20 -0
  32. package/dist/src/providers/golang_gomodules.d.ts +11 -1
  33. package/dist/src/providers/golang_gomodules.js +114 -15
  34. package/dist/src/providers/java_gradle.d.ts +67 -0
  35. package/dist/src/providers/java_gradle.js +320 -22
  36. package/dist/src/providers/java_gradle_groovy.d.ts +1 -1
  37. package/dist/src/providers/java_gradle_kotlin.d.ts +1 -1
  38. package/dist/src/providers/java_maven.d.ts +37 -9
  39. package/dist/src/providers/java_maven.js +269 -11
  40. package/dist/src/providers/javascript_bun.d.ts +22 -0
  41. package/dist/src/providers/javascript_bun.js +141 -0
  42. package/dist/src/providers/javascript_npm.d.ts +19 -0
  43. package/dist/src/providers/javascript_npm.js +61 -1
  44. package/dist/src/providers/javascript_pnpm.d.ts +13 -0
  45. package/dist/src/providers/javascript_pnpm.js +53 -3
  46. package/dist/src/providers/javascript_yarn.d.ts +12 -0
  47. package/dist/src/providers/javascript_yarn.js +75 -2
  48. package/dist/src/providers/manifest.js +6 -3
  49. package/dist/src/providers/marker_evaluator.d.ts +14 -0
  50. package/dist/src/providers/marker_evaluator.js +191 -0
  51. package/dist/src/providers/oci_dockerfile.d.ts +51 -0
  52. package/dist/src/providers/oci_dockerfile.js +177 -0
  53. package/dist/src/providers/processors/yarn_berry_processor.d.ts +6 -2
  54. package/dist/src/providers/processors/yarn_berry_processor.js +9 -4
  55. package/dist/src/providers/processors/yarn_classic_processor.d.ts +6 -2
  56. package/dist/src/providers/processors/yarn_classic_processor.js +8 -6
  57. package/dist/src/providers/python_controller.d.ts +5 -1
  58. package/dist/src/providers/python_controller.js +69 -8
  59. package/dist/src/providers/python_pip.d.ts +5 -0
  60. package/dist/src/providers/python_pip.js +5 -5
  61. package/dist/src/providers/python_pip_pyproject.js +3 -1
  62. package/dist/src/providers/python_poetry.d.ts +64 -8
  63. package/dist/src/providers/python_poetry.js +139 -22
  64. package/dist/src/providers/python_uv.d.ts +20 -1
  65. package/dist/src/providers/python_uv.js +132 -3
  66. package/dist/src/providers/requirements_parser.js +1 -1
  67. package/dist/src/providers/rust_cargo.d.ts +6 -2
  68. package/dist/src/providers/rust_cargo.js +101 -33
  69. package/dist/src/providers/tree-sitter-containerfile.wasm +0 -0
  70. package/dist/src/remediate.d.ts +107 -0
  71. package/dist/src/remediate.js +244 -0
  72. package/dist/src/remediation.d.ts +81 -0
  73. package/dist/src/remediation.js +475 -0
  74. package/dist/src/remediation_report.d.ts +23 -0
  75. package/dist/src/remediation_report.js +180 -0
  76. package/dist/src/sbom.d.ts +18 -1
  77. package/dist/src/sbom.js +14 -2
  78. package/dist/src/tools.d.ts +37 -13
  79. package/dist/src/tools.js +92 -13
  80. package/dist/src/updaters/maven_updater.d.ts +58 -0
  81. package/dist/src/updaters/maven_updater.js +345 -0
  82. package/dist/src/updaters/toml_updater.d.ts +62 -0
  83. package/dist/src/updaters/toml_updater.js +276 -0
  84. package/dist/src/workspace.d.ts +11 -1
  85. package/dist/src/workspace.js +3 -2
  86. package/package.json +31 -27
@@ -0,0 +1,260 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { load as yamlLoad } from 'js-yaml';
4
+ /**
5
+ * @typedef {{
6
+ * 'group-by'?: 'dependency' | 'bundle',
7
+ * exclude?: string[],
8
+ * labels?: string[],
9
+ * 'branch-prefix'?: string,
10
+ * [key: string]: unknown
11
+ * }} RemediationConfig
12
+ */
13
+ /** @typedef {{ critical?: number, high?: number, 'license-conflicts'?: number, [key: string]: unknown }} FailOnConfig */
14
+ /** @typedef {{ 'fail-on'?: FailOnConfig, [key: string]: unknown }} CheckConfig */
15
+ /** @typedef {{ format?: string, targets?: string[], [key: string]: unknown }} SbomConfig */
16
+ /**
17
+ * @typedef {{
18
+ * 'backend-url'?: string,
19
+ * providers?: string[] | string,
20
+ * sources?: string[] | string,
21
+ * remediation?: RemediationConfig,
22
+ * check?: CheckConfig,
23
+ * sbom?: SbomConfig,
24
+ * [key: string]: unknown
25
+ * }} FileConfig
26
+ */
27
+ /**
28
+ * @typedef {{
29
+ * backendUrl: string | null,
30
+ * backendUrlSource: 'cli' | 'environment' | 'file' | 'default',
31
+ * providers: string[],
32
+ * sources: string[],
33
+ * groupBy: string,
34
+ * remediation: object,
35
+ * check: object,
36
+ * sbom: object
37
+ * }} ResolvedConfig
38
+ */
39
+ /** Config file names discovered by walking up the directory tree, in precedence order. */
40
+ export const CONFIG_FILENAMES = ['.trustify-da.yml', '.trustify-da.yaml'];
41
+ function isMapping(value) {
42
+ if (value == null || typeof value !== 'object' || Array.isArray(value)) {
43
+ return false;
44
+ }
45
+ const prototype = Object.getPrototypeOf(value);
46
+ return prototype === Object.prototype || prototype == null;
47
+ }
48
+ function assertMapping(value, field, source) {
49
+ if (!isMapping(value)) {
50
+ throw new Error(`Invalid config file ${source}: ${field} must be a mapping`);
51
+ }
52
+ }
53
+ function assertString(value, field, source) {
54
+ if (typeof value !== 'string') {
55
+ throw new Error(`Invalid config file ${source}: ${field} must be a string`);
56
+ }
57
+ }
58
+ function assertStringArray(value, field, source) {
59
+ if (!Array.isArray(value) || value.some(item => typeof item !== 'string')) {
60
+ throw new Error(`Invalid config file ${source}: ${field} must be an array of strings`);
61
+ }
62
+ }
63
+ function assertStringOrArray(value, field, source) {
64
+ if (typeof value !== 'string') {
65
+ assertStringArray(value, field, source);
66
+ }
67
+ }
68
+ /**
69
+ * Validates all fields in the initial configuration schema. Unknown fields are preserved.
70
+ * @param {unknown} config
71
+ * @param {string} [source]
72
+ * @returns {FileConfig}
73
+ */
74
+ function validateConfig(config, source = '<config>') {
75
+ assertMapping(config, 'root', source);
76
+ if (config['backend-url'] !== undefined) {
77
+ assertString(config['backend-url'], 'backend-url', source);
78
+ }
79
+ if (config.providers !== undefined) {
80
+ assertStringOrArray(config.providers, 'providers', source);
81
+ }
82
+ if (config.sources !== undefined) {
83
+ assertStringOrArray(config.sources, 'sources', source);
84
+ }
85
+ if (config.remediation !== undefined) {
86
+ assertMapping(config.remediation, 'remediation', source);
87
+ const remediation = config.remediation;
88
+ if (remediation['group-by'] !== undefined && !['dependency', 'bundle'].includes(remediation['group-by'])) {
89
+ throw new Error(`Invalid config file ${source}: remediation.group-by must be dependency or bundle`);
90
+ }
91
+ if (remediation.exclude !== undefined) {
92
+ assertStringArray(remediation.exclude, 'remediation.exclude', source);
93
+ }
94
+ if (remediation.labels !== undefined) {
95
+ assertStringArray(remediation.labels, 'remediation.labels', source);
96
+ }
97
+ if (remediation['branch-prefix'] !== undefined) {
98
+ assertString(remediation['branch-prefix'], 'remediation.branch-prefix', source);
99
+ }
100
+ }
101
+ if (config.check !== undefined) {
102
+ assertMapping(config.check, 'check', source);
103
+ if (config.check['fail-on'] !== undefined) {
104
+ assertMapping(config.check['fail-on'], 'check.fail-on', source);
105
+ for (const field of ['critical', 'high', 'license-conflicts']) {
106
+ const value = config.check['fail-on'][field];
107
+ if (value !== undefined && (typeof value !== 'number' || !Number.isFinite(value))) {
108
+ throw new Error(`Invalid config file ${source}: check.fail-on.${field} must be a finite number`);
109
+ }
110
+ }
111
+ }
112
+ }
113
+ if (config.sbom !== undefined) {
114
+ assertMapping(config.sbom, 'sbom', source);
115
+ if (config.sbom.format !== undefined) {
116
+ assertString(config.sbom.format, 'sbom.format', source);
117
+ }
118
+ if (config.sbom.targets !== undefined) {
119
+ assertStringArray(config.sbom.targets, 'sbom.targets', source);
120
+ }
121
+ }
122
+ return config;
123
+ }
124
+ /**
125
+ * Walks up from `startPath` looking for a `.trustify-da.yml` (or `.yaml`) file,
126
+ * similar to how `.eslintrc` discovery works. If `startPath` points at a file,
127
+ * discovery begins in its containing directory. Within a directory, `.yml` takes
128
+ * precedence over `.yaml`.
129
+ * @param {string} startPath - manifest file or directory to start the search from
130
+ * @returns {string | null} absolute path to the config file, or null if none found
131
+ */
132
+ function findConfigFile(startPath) {
133
+ let dir = path.resolve(startPath || '.');
134
+ try {
135
+ if (fs.statSync(dir).isFile()) {
136
+ dir = path.dirname(dir);
137
+ }
138
+ }
139
+ catch {
140
+ // startPath may not exist yet — walk up from its resolved location anyway
141
+ }
142
+ // Walk up until the filesystem root (where dirname(dir) === dir)
143
+ for (;;) {
144
+ for (const name of CONFIG_FILENAMES) {
145
+ const candidate = path.join(dir, name);
146
+ try {
147
+ if (fs.statSync(candidate).isFile()) {
148
+ return candidate;
149
+ }
150
+ }
151
+ catch {
152
+ // Ignore inaccessible or disappearing candidates and continue discovery.
153
+ }
154
+ }
155
+ const parent = path.dirname(dir);
156
+ if (parent === dir) {
157
+ return null;
158
+ }
159
+ dir = parent;
160
+ }
161
+ }
162
+ /**
163
+ * Discovers and parses `.trustify-da.yml` by walking up from `startPath`.
164
+ * A missing config file is not an error — an empty object is returned so callers
165
+ * can fall back to defaults.
166
+ * @param {string} [startPath] - manifest file or directory to start the search from
167
+ * @returns {FileConfig} the parsed config object, or `{}` when no file is found
168
+ * @throws {Error} when the config file exists but contains malformed YAML
169
+ */
170
+ export function loadConfig(startPath) {
171
+ const configPath = findConfigFile(startPath);
172
+ if (!configPath) {
173
+ return {};
174
+ }
175
+ const content = fs.readFileSync(configPath, 'utf-8');
176
+ let doc;
177
+ try {
178
+ doc = yamlLoad(content);
179
+ }
180
+ catch (err) {
181
+ throw new Error(`Failed to parse config file ${configPath}: ${err.message}`);
182
+ }
183
+ return doc == null ? {} : validateConfig(doc, configPath);
184
+ }
185
+ /**
186
+ * Normalizes a providers/sources value to an array of trimmed strings. Accepts
187
+ * either a YAML array (`[redhat, osv]`) or a comma-separated string (`redhat,osv`).
188
+ * @param {string[] | string | undefined | null} value
189
+ * @returns {string[]}
190
+ */
191
+ function toArray(value) {
192
+ if (Array.isArray(value)) {
193
+ return value.filter(v => typeof v === 'string' && v.trim()).map(v => v.trim());
194
+ }
195
+ if (typeof value === 'string') {
196
+ return value.split(',').map(v => v.trim()).filter(Boolean);
197
+ }
198
+ return [];
199
+ }
200
+ /**
201
+ * Returns the first argument that is neither undefined nor null.
202
+ * @param {...unknown} values
203
+ * @returns {unknown}
204
+ */
205
+ function first(...values) {
206
+ return values.find(v => v != null);
207
+ }
208
+ /**
209
+ * Merges config sources with precedence: CLI flag > environment variable
210
+ * (`TRUSTIFY_DA_*`) > config file > hardcoded default. Providers and sources are
211
+ * normalized to arrays regardless of whether the source used a YAML array or a
212
+ * comma-separated string.
213
+ * @param {FileConfig} [fileConfig={}] - values parsed from `.trustify-da.yml`
214
+ * @param {{ providers?: string, sources?: string, groupBy?: string, backendUrl?: string }} [cliFlags={}]
215
+ * @param {{ [key: string]: string | undefined }} [envVars={}] - typically `process.env`
216
+ * @returns {ResolvedConfig} the resolved, typed config object
217
+ */
218
+ export function mergeConfig(fileConfig = {}, cliFlags = {}, envVars = {}) {
219
+ const file = validateConfig(fileConfig || {});
220
+ const cli = cliFlags || {};
221
+ const env = envVars || {};
222
+ let backendUrl = null;
223
+ let backendUrlSource = 'default';
224
+ if (cli.backendUrl != null) {
225
+ backendUrl = cli.backendUrl;
226
+ backendUrlSource = 'cli';
227
+ }
228
+ else if (env.TRUSTIFY_DA_BACKEND_URL != null) {
229
+ backendUrl = env.TRUSTIFY_DA_BACKEND_URL;
230
+ backendUrlSource = 'environment';
231
+ }
232
+ else if (file['backend-url'] != null) {
233
+ backendUrl = file['backend-url'];
234
+ backendUrlSource = 'file';
235
+ }
236
+ const groupBy = first(cli.groupBy, env.TRUSTIFY_DA_GROUP_BY, file.remediation?.['group-by']) ?? 'dependency';
237
+ if (!['dependency', 'bundle'].includes(groupBy)) {
238
+ throw new Error(`Invalid group-by value "${groupBy}". Expected dependency or bundle.`);
239
+ }
240
+ return {
241
+ backendUrl,
242
+ backendUrlSource,
243
+ providers: toArray(first(cli.providers, env.TRUSTIFY_DA_PROVIDERS, file.providers)),
244
+ sources: toArray(first(cli.sources, env.TRUSTIFY_DA_SOURCES, file.sources)),
245
+ groupBy,
246
+ remediation: file.remediation ?? {},
247
+ check: file.check ?? {},
248
+ sbom: file.sbom ?? {},
249
+ };
250
+ }
251
+ /**
252
+ * Loads and merges the project configuration for a target path.
253
+ * @param {string} [startPath] - manifest file or directory to start discovery from
254
+ * @param {{ providers?: string, sources?: string, groupBy?: string, backendUrl?: string }} [cliFlags={}] - explicit options
255
+ * @param {{ [key: string]: string | undefined }} [envVars={}] - typically `process.env`
256
+ * @returns {ResolvedConfig} the resolved, typed config object
257
+ */
258
+ export function resolveConfig(startPath, cliFlags = {}, envVars = {}) {
259
+ return mergeConfig(loadConfig(startPath), cliFlags, envVars);
260
+ }
@@ -18,9 +18,27 @@ 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;
29
+ /**
30
+ * Attach hashes to already-added components by matching their PURL. This is a
31
+ * post-processing step so ecosystem-specific hash sources (e.g. Maven reading
32
+ * the local .m2 cache) can enrich the SBOM without threading their concern
33
+ * through the shared dependency-tree parser. Components without a matching
34
+ * entry, or whose hashes are already set, are left untouched.
35
+ * @param {Map<string, Array<{alg: string, content: string}>>} hashMap - PURL→hashes map
36
+ * @return {CycloneDxSbom} the updated SBOM
37
+ */
38
+ attachHashes(hashMap: Map<string, Array<{
39
+ alg: string;
40
+ content: string;
41
+ }>>): CycloneDxSbom;
24
42
  /** @param {{}} opts - various options, settings and configuration of application.
25
43
  * @return String CycloneDx Sbom json object in a string format
26
44
  */
@@ -50,6 +68,7 @@ export default class CycloneDxSbom {
50
68
  version: any;
51
69
  scope: any;
52
70
  licenses?: any;
71
+ hashes?: any;
53
72
  };
54
73
  /**
55
74
  * This method gets an array of dependencies to be ignored, and remove all of them from CycloneDx Sbom
@@ -1,15 +1,23 @@
1
1
  import { EOL } from "os";
2
2
  import { PackageURL } from "packageurl-js";
3
+ import { getPackageVersion } from "./package_version.js";
4
+ /**
5
+ * This client's version, read once at module load, used to populate
6
+ * metadata.tools.components[].version in the generated SBOM.
7
+ * @type {string|undefined}
8
+ */
9
+ const PACKAGE_VERSION = getPackageVersion();
3
10
  /**
4
11
  *
5
12
  * @param component {PackageURL}
6
13
  * @param type type of package - application or library
7
14
  * @param scope scope of the component - runtime or compile
8
15
  * @param licenses optional license string or array of licenses for the component
9
- * @return {{"bom-ref": string, name, purl: string, type, version, scope, licenses?}}
16
+ * @param hashes optional array of hash objects for the component, e.g. [{alg: "SHA-256", content: "..."}]
17
+ * @return {{"bom-ref": string, name, purl: string, type, version, scope, licenses?, hashes?}}
10
18
  * @private
11
19
  */
12
- function getComponent(component, type, scope, licenses) {
20
+ function getComponent(component, type, scope, licenses, hashes) {
13
21
  let componentObject;
14
22
  if (component instanceof PackageURL) {
15
23
  if (component.namespace) {
@@ -40,12 +48,24 @@ function getComponent(component, type, scope, licenses) {
40
48
  // Add licenses if provided (CycloneDX format). Callers must provide valid SPDX identifiers.
41
49
  if (licenses) {
42
50
  const licenseArray = Array.isArray(licenses) ? licenses : [licenses];
43
- componentObject.licenses = licenseArray.map(lic => {
51
+ const validLicenses = licenseArray
52
+ .map(lic => {
44
53
  if (typeof lic === 'string') {
45
54
  return { license: { id: lic } };
46
55
  }
47
- return lic;
48
- });
56
+ if (typeof lic === 'object' && lic !== null && ('license' in lic || 'expression' in lic)) {
57
+ return lic;
58
+ }
59
+ return null;
60
+ })
61
+ .filter(Boolean);
62
+ if (validLicenses.length > 0) {
63
+ componentObject.licenses = validLicenses;
64
+ }
65
+ }
66
+ // Add hashes if provided (CycloneDX 1.4 format).
67
+ if (hashes && hashes.length > 0) {
68
+ componentObject.hashes = hashes;
49
69
  }
50
70
  return componentObject;
51
71
  }
@@ -86,16 +106,24 @@ export default class CycloneDxSbom {
86
106
  * Adds a dependency relationship between two components in the SBOM
87
107
  * @param {PackageURL} sourceRef - The source component (parent)
88
108
  * @param {PackageURL} targetRef - The target component (dependency)
109
+ * @param {string} [scope] - Scope of the dependency
110
+ * @param {Array<{alg: string, content: string}>} [targetHashes] - Optional hashes for the target component
89
111
  * @return {CycloneDxSbom} The updated SBOM
90
112
  */
91
- addDependency(sourceRef, targetRef, scope) {
113
+ addDependency(sourceRef, targetRef, scope, targetHashes) {
92
114
  const sourcePurl = sourceRef.toString();
93
115
  const targetPurl = targetRef.toString();
94
116
  // Ensure both components exist in the components list
95
117
  [sourceRef, targetRef].forEach((ref, index) => {
96
118
  const purl = index === 0 ? sourcePurl : targetPurl;
97
- if (this.getComponentIndex(purl) < 0) {
98
- this.components.push(getComponent(ref, "library", scope));
119
+ const existingIndex = this.getComponentIndex(purl);
120
+ if (existingIndex < 0) {
121
+ const hashes = index === 1 ? targetHashes : undefined;
122
+ this.components.push(getComponent(ref, "library", scope, undefined, hashes));
123
+ }
124
+ else if (index === 1 && targetHashes && targetHashes.length > 0 && !this.components[existingIndex].hashes) {
125
+ // Update hashes if the component was first seen without them (e.g. as a source)
126
+ this.components[existingIndex].hashes = targetHashes;
99
127
  }
100
128
  });
101
129
  // Ensure source dependency exists
@@ -114,6 +142,30 @@ export default class CycloneDxSbom {
114
142
  }
115
143
  return this;
116
144
  }
145
+ /**
146
+ * Attach hashes to already-added components by matching their PURL. This is a
147
+ * post-processing step so ecosystem-specific hash sources (e.g. Maven reading
148
+ * the local .m2 cache) can enrich the SBOM without threading their concern
149
+ * through the shared dependency-tree parser. Components without a matching
150
+ * entry, or whose hashes are already set, are left untouched.
151
+ * @param {Map<string, Array<{alg: string, content: string}>>} hashMap - PURL→hashes map
152
+ * @return {CycloneDxSbom} the updated SBOM
153
+ */
154
+ attachHashes(hashMap) {
155
+ if (!hashMap || hashMap.size === 0) {
156
+ return this;
157
+ }
158
+ for (const component of this.components) {
159
+ if (component.hashes) {
160
+ continue;
161
+ }
162
+ const hashes = hashMap.get(component.purl);
163
+ if (hashes && hashes.length > 0) {
164
+ component.hashes = hashes;
165
+ }
166
+ }
167
+ return this;
168
+ }
117
169
  /** @param {{}} opts - various options, settings and configuration of application.
118
170
  * @return String CycloneDx Sbom json object in a string format
119
171
  */
@@ -123,11 +175,18 @@ export default class CycloneDxSbom {
123
175
  const rootPurl = this.rootComponent?.purl;
124
176
  this.sbomObject = {
125
177
  "bomFormat": "CycloneDX",
126
- "specVersion": "1.4",
178
+ "specVersion": "1.6",
127
179
  "version": 1,
128
180
  "metadata": {
129
181
  "timestamp": new Date(),
130
182
  "component": this.rootComponent,
183
+ "tools": {
184
+ "components": [{
185
+ "type": "application",
186
+ "name": "trustify-da-javascript-client",
187
+ "version": PACKAGE_VERSION
188
+ }]
189
+ },
131
190
  "properties": new Array()
132
191
  },
133
192
  "components": this.components.filter(c => c.purl !== rootPurl),
@@ -1,8 +1,6 @@
1
1
  /**
2
- * This function is used to determine the Trustify DA backend URL.
3
- * The TRUSTIFY_DA_BACKEND_URL is evaluated in the following order and selected when it finds it first:
4
- * 1. Environment Variable
5
- * 2. (key,value) from opts object
2
+ * Selects the Trustify DA backend URL from opts.TRUSTIFY_DA_BACKEND_URL, then environment
3
+ * variable TRUSTIFY_DA_BACKEND_URL. Options take precedence over environment variables.
6
4
  * If TRUSTIFY_DA_BACKEND_URL is not set, the function will throw an error.
7
5
  * @param {{TRUSTIFY_DA_DEBUG?: string | undefined; TRUSTIFY_DA_BACKEND_URL?: string | undefined}} [opts={}]
8
6
  * @return {string} - The selected Trustify DA backend URL
@@ -53,7 +51,9 @@ export type Options = {
53
51
  TRUSTIFY_DA_PYTHON_PATH?: string | undefined;
54
52
  TRUSTIFY_DA_PYTHON_VIRTUAL_ENV?: string | undefined;
55
53
  TRUSTIFY_DA_PYTHON3_PATH?: string | undefined;
56
- TRUSTIFY_DA_RECOMMENDATIONS_ENABLED?: string | undefined;
54
+ TRUSTIFY_DA_PROVIDERS?: string | undefined;
55
+ TRUSTIFY_DA_RECOMMEND?: string | undefined;
56
+ TRUSTIFY_DA_SOURCES?: string | undefined;
57
57
  TRUSTIFY_DA_SKOPEO_CONFIG_PATH?: string | undefined;
58
58
  TRUSTIFY_DA_SKOPEO_PATH?: string | undefined;
59
59
  TRUSTIFY_DA_SYFT_CONFIG_PATH?: string | undefined;
@@ -61,11 +61,11 @@ export type Options = {
61
61
  TRUSTIFY_DA_YARN_PATH?: string | undefined;
62
62
  TRUSTIFY_DA_WORKSPACE_DIR?: string | undefined;
63
63
  TRUSTIFY_DA_LICENSE_CHECK?: string | undefined;
64
+ licenseCheck?: boolean | undefined;
64
65
  MATCH_MANIFEST_VERSIONS?: string | undefined;
65
66
  TRUSTIFY_DA_SOURCE?: string | undefined;
66
67
  TRUSTIFY_DA_TOKEN?: string | undefined;
67
68
  TRUSTIFY_DA_TELEMETRY_ID?: string | undefined;
68
- TRUSTIFY_DA_WORKSPACE_DIR?: string | undefined;
69
69
  batchConcurrency?: number | undefined;
70
70
  TRUSTIFY_DA_BATCH_CONCURRENCY?: string | undefined;
71
71
  workspaceDiscoveryIgnore?: string[] | undefined;
@@ -80,7 +80,7 @@ export type Options = {
80
80
  };
81
81
  export type BatchAnalysisMetadata = {
82
82
  workspaceRoot: string;
83
- ecosystem: "javascript" | "cargo" | "unknown";
83
+ ecosystem: "javascript" | "cargo" | "pyproject" | "unknown";
84
84
  total: number;
85
85
  successful: number;
86
86
  failed: number;
@@ -103,10 +103,10 @@ export type SbomResult = {
103
103
  * Get component analysis report for a manifest content.
104
104
  * @param {string} manifest - path to the manifest
105
105
  * @param {Options} [opts={}] - optional various options to pass along the application
106
- * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
106
+ * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
107
107
  * @throws {Error} if no matching provider, failed to get create content, or backend request failed
108
108
  */
109
- declare function componentAnalysis(manifest: string, opts?: Options): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport>;
109
+ declare function componentAnalysis(manifest: string, opts?: Options): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport>;
110
110
  /**
111
111
  * @overload
112
112
  * @param {string} manifest
@@ -121,38 +121,93 @@ declare function stackAnalysis(manifest: string, html: true, opts?: Options | un
121
121
  * @param {string} manifest
122
122
  * @param {false} html
123
123
  * @param {Options} [opts={}]
124
- * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
124
+ * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
125
125
  * @throws {Error}
126
126
  */
127
- declare function stackAnalysis(manifest: string, html: false, opts?: Options | undefined): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport>;
127
+ declare function stackAnalysis(manifest: string, html: false, opts?: Options | undefined): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport>;
128
128
  /**
129
129
  * Get stack analysis report for a manifest file.
130
130
  * @overload
131
131
  * @param {string} manifest - path for the manifest
132
132
  * @param {boolean} [html=false] - true will return a html string, false will return AnalysisReport object.
133
133
  * @param {Options} [opts={}] - optional various options to pass along the application
134
- * @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
134
+ * @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
135
135
  * @throws {Error} if manifest inaccessible, no matching provider, failed to get create content,
136
136
  * or backend request failed
137
137
  */
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>;
138
+ declare function stackAnalysis(manifest: string, html?: boolean | undefined, opts?: Options | undefined): Promise<string | import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport>;
139
+ /**
140
+ * @overload
141
+ * @param {string} workspaceRoot
142
+ * @param {true} html
143
+ * @param {Options & { batchMetadata: true }} opts
144
+ * @returns {Promise<{ analysis: string, metadata: BatchAnalysisMetadata }>}
145
+ * @throws {Error}
146
+ */
147
+ declare function stackAnalysisBatch(workspaceRoot: string, html: true, opts: Options & {
148
+ batchMetadata: true;
149
+ }): Promise<{
150
+ analysis: string;
151
+ metadata: BatchAnalysisMetadata;
152
+ }>;
153
+ /**
154
+ * @overload
155
+ * @param {string} workspaceRoot
156
+ * @param {true} html
157
+ * @param {Options & { batchMetadata?: false }} [opts={}]
158
+ * @returns {Promise<string>}
159
+ * @throws {Error}
160
+ */
161
+ declare function stackAnalysisBatch(workspaceRoot: string, html: true, opts?: (Options & {
162
+ batchMetadata?: false;
163
+ }) | undefined): Promise<string>;
164
+ /**
165
+ * @overload
166
+ * @param {string} workspaceRoot
167
+ * @param {false} html
168
+ * @param {Options & { batchMetadata: true }} opts
169
+ * @returns {Promise<{ analysis: Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>, metadata: BatchAnalysisMetadata }>}
170
+ * @throws {Error}
171
+ */
172
+ declare function stackAnalysisBatch(workspaceRoot: string, html: false, opts: Options & {
173
+ batchMetadata: true;
174
+ }): Promise<{
175
+ analysis: {
176
+ [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
177
+ };
178
+ metadata: BatchAnalysisMetadata;
179
+ }>;
180
+ /**
181
+ * @overload
182
+ * @param {string} workspaceRoot
183
+ * @param {false} html
184
+ * @param {Options & { batchMetadata?: false }} [opts={}]
185
+ * @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
186
+ * @throws {Error}
187
+ */
188
+ declare function stackAnalysisBatch(workspaceRoot: string, html: false, opts?: (Options & {
189
+ batchMetadata?: false;
190
+ }) | undefined): Promise<{
191
+ [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
192
+ }>;
139
193
  /**
140
194
  * Get stack analysis for all workspace packages/crates (batch).
141
195
  * Detects ecosystem from workspace root: Cargo (Cargo.toml + Cargo.lock) or JS/TS (package.json + lock file).
142
196
  * SBOMs are generated in parallel (see `batchConcurrency`) unless `continueOnError: false` (fail-fast sequential).
143
197
  * With `opts.batchMetadata` / `TRUSTIFY_DA_BATCH_METADATA`, returns `{ analysis, metadata }` including validation and SBOM errors.
144
198
  *
199
+ * @overload
145
200
  * @param {string} workspaceRoot - Path to workspace root (containing lock file and workspace config)
146
201
  * @param {boolean} [html=false] - true returns HTML, false returns JSON report
147
202
  * @param {Options} [opts={}] - `batchConcurrency`, discovery ignores, `continueOnError` (default true), `batchMetadata` (default false)
148
- * @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 }>}
203
+ * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>|{ analysis: string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>, metadata: BatchAnalysisMetadata }>}
149
204
  * @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
205
  */
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;
206
+ declare function stackAnalysisBatch(workspaceRoot: string, html?: boolean | undefined, opts?: Options | undefined): Promise<string | {
207
+ [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
153
208
  } | {
154
209
  analysis: string | {
155
- [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
210
+ [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
156
211
  };
157
212
  metadata: BatchAnalysisMetadata;
158
213
  }>;
@@ -170,11 +225,11 @@ declare function imageAnalysis(imageRefs: Array<string>, html: true, opts?: Opti
170
225
  * @param {Array<string>} imageRefs
171
226
  * @param {false} html
172
227
  * @param {Options} [opts={}]
173
- * @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
228
+ * @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
174
229
  * @throws {Error}
175
230
  */
176
231
  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;
232
+ [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
178
233
  }>;
179
234
  /**
180
235
  * Get image analysis report for a set of OCI image references.
@@ -182,12 +237,12 @@ declare function imageAnalysis(imageRefs: Array<string>, html: false, opts?: Opt
182
237
  * @param {Array<string>} imageRefs - OCI image references
183
238
  * @param {boolean} [html=false] - true will return a html string, false will return AnalysisReport
184
239
  * @param {Options} [opts={}] - optional various options to pass along the application
185
- * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
240
+ * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
186
241
  * @throws {Error} if manifest inaccessible, no matching provider, failed to get create content,
187
242
  * or backend request failed
188
243
  */
189
244
  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;
245
+ [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
191
246
  }>;
192
247
  /**
193
248
  * Validates the Exhort token.
@@ -196,6 +251,10 @@ declare function imageAnalysis(imageRefs: Array<string>, html?: boolean | undefi
196
251
  * @throws {Error} if the backend request failed.
197
252
  */
198
253
  declare function validateToken(opts?: Options): Promise<object>;
254
+ import { discoverMavenModules } from './providers/java_maven.js';
255
+ import { discoverGradleSubprojects } from './providers/java_gradle.js';
256
+ import { discoverGoWorkspaceModules } from './providers/golang_gomodules.js';
257
+ import { discoverUvWorkspaceMembers } from './providers/python_uv.js';
199
258
  import { discoverWorkspacePackages } from './workspace.js';
200
259
  import { discoverWorkspaceCrates } from './workspace.js';
201
260
  import { validatePackageJson } from './workspace.js';
@@ -203,5 +262,9 @@ import { resolveWorkspaceDiscoveryIgnore } from './workspace.js';
203
262
  import { filterManifestPathsByDiscoveryIgnore } from './workspace.js';
204
263
  import { resolveContinueOnError } from './batch_opts.js';
205
264
  import { resolveBatchMetadata } from './batch_opts.js';
206
- export { discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson, resolveWorkspaceDiscoveryIgnore, filterManifestPathsByDiscoveryIgnore, resolveContinueOnError, resolveBatchMetadata };
265
+ export { discoverMavenModules, discoverGradleSubprojects, discoverGoWorkspaceModules, discoverUvWorkspaceMembers, discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson, resolveWorkspaceDiscoveryIgnore, filterManifestPathsByDiscoveryIgnore, resolveContinueOnError, resolveBatchMetadata };
207
266
  export { getProjectLicense, findLicenseFilePath, identifyLicense, getLicenseDetails, licensesFromReport, normalizeLicensesResponse, runLicenseCheck, getCompatibility } from "./license/index.js";
267
+ export { extractRemediations, maxSeverity } from "./remediation.js";
268
+ export { generateReport, generateDeduplicationKey } from "./remediation_report.js";
269
+ export { loadConfig, mergeConfig, resolveConfig, CONFIG_FILENAMES } from "./config.js";
270
+ export { runRemediation, findManifests } from "./remediate.js";