@trustify-da/trustify-da-javascript-client 0.3.0-ea.2ea1d77 → 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 (92) hide show
  1. package/README.md +272 -19
  2. package/dist/package.json +33 -28
  3. package/dist/src/analysis.d.ts +52 -22
  4. package/dist/src/analysis.js +77 -22
  5. package/dist/src/cli.js +238 -7
  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 +27 -1
  9. package/dist/src/cyclone_dx_sbom.js +82 -9
  10. package/dist/src/index.d.ts +97 -22
  11. package/dist/src/index.js +115 -42
  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/oci_image/utils.js +11 -2
  21. package/dist/src/package_version.d.ts +8 -0
  22. package/dist/src/package_version.js +31 -0
  23. package/dist/src/provider.d.ts +4 -3
  24. package/dist/src/provider.js +12 -2
  25. package/dist/src/providers/base_java.d.ts +67 -12
  26. package/dist/src/providers/base_java.js +82 -61
  27. package/dist/src/providers/base_javascript.d.ts +90 -15
  28. package/dist/src/providers/base_javascript.js +206 -24
  29. package/dist/src/providers/base_pyproject.d.ts +158 -0
  30. package/dist/src/providers/base_pyproject.js +322 -0
  31. package/dist/src/providers/containerfile_parser.d.ts +5 -0
  32. package/dist/src/providers/containerfile_parser.js +20 -0
  33. package/dist/src/providers/golang_gomodules.d.ts +23 -13
  34. package/dist/src/providers/golang_gomodules.js +208 -120
  35. package/dist/src/providers/gomod_parser.d.ts +4 -0
  36. package/dist/src/providers/gomod_parser.js +16 -0
  37. package/dist/src/providers/java_gradle.d.ts +67 -0
  38. package/dist/src/providers/java_gradle.js +320 -22
  39. package/dist/src/providers/java_gradle_groovy.d.ts +1 -1
  40. package/dist/src/providers/java_gradle_kotlin.d.ts +1 -1
  41. package/dist/src/providers/java_maven.d.ts +37 -9
  42. package/dist/src/providers/java_maven.js +269 -11
  43. package/dist/src/providers/javascript_bun.d.ts +22 -0
  44. package/dist/src/providers/javascript_bun.js +141 -0
  45. package/dist/src/providers/javascript_npm.d.ts +19 -0
  46. package/dist/src/providers/javascript_npm.js +61 -1
  47. package/dist/src/providers/javascript_pnpm.d.ts +13 -0
  48. package/dist/src/providers/javascript_pnpm.js +53 -3
  49. package/dist/src/providers/javascript_yarn.d.ts +12 -0
  50. package/dist/src/providers/javascript_yarn.js +75 -2
  51. package/dist/src/providers/manifest.d.ts +2 -0
  52. package/dist/src/providers/manifest.js +28 -7
  53. package/dist/src/providers/marker_evaluator.d.ts +14 -0
  54. package/dist/src/providers/marker_evaluator.js +191 -0
  55. package/dist/src/providers/oci_dockerfile.d.ts +51 -0
  56. package/dist/src/providers/oci_dockerfile.js +177 -0
  57. package/dist/src/providers/processors/yarn_berry_processor.d.ts +6 -2
  58. package/dist/src/providers/processors/yarn_berry_processor.js +91 -7
  59. package/dist/src/providers/processors/yarn_classic_processor.d.ts +6 -2
  60. package/dist/src/providers/processors/yarn_classic_processor.js +8 -6
  61. package/dist/src/providers/python_controller.d.ts +5 -1
  62. package/dist/src/providers/python_controller.js +69 -8
  63. package/dist/src/providers/python_pip.d.ts +5 -0
  64. package/dist/src/providers/python_pip.js +6 -6
  65. package/dist/src/providers/python_pip_pyproject.d.ts +61 -0
  66. package/dist/src/providers/python_pip_pyproject.js +146 -0
  67. package/dist/src/providers/python_poetry.d.ts +98 -0
  68. package/dist/src/providers/python_poetry.js +286 -0
  69. package/dist/src/providers/python_uv.d.ts +46 -0
  70. package/dist/src/providers/python_uv.js +275 -0
  71. package/dist/src/providers/requirements_parser.js +1 -1
  72. package/dist/src/providers/rust_cargo.d.ts +6 -2
  73. package/dist/src/providers/rust_cargo.js +101 -33
  74. package/dist/src/providers/tree-sitter-containerfile.wasm +0 -0
  75. package/dist/src/providers/tree-sitter-gomod.wasm +0 -0
  76. package/dist/src/remediate.d.ts +107 -0
  77. package/dist/src/remediate.js +244 -0
  78. package/dist/src/remediation.d.ts +81 -0
  79. package/dist/src/remediation.js +475 -0
  80. package/dist/src/remediation_report.d.ts +23 -0
  81. package/dist/src/remediation_report.js +180 -0
  82. package/dist/src/sbom.d.ts +25 -1
  83. package/dist/src/sbom.js +23 -2
  84. package/dist/src/tools.d.ts +37 -13
  85. package/dist/src/tools.js +92 -13
  86. package/dist/src/updaters/maven_updater.d.ts +58 -0
  87. package/dist/src/updaters/maven_updater.js +345 -0
  88. package/dist/src/updaters/toml_updater.d.ts +62 -0
  89. package/dist/src/updaters/toml_updater.js +276 -0
  90. package/dist/src/workspace.d.ts +11 -1
  91. package/dist/src/workspace.js +3 -2
  92. package/package.json +34 -29
@@ -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
@@ -13,6 +11,15 @@ export function selectTrustifyDABackend(opts?: {
13
11
  TRUSTIFY_DA_DEBUG?: string | undefined;
14
12
  TRUSTIFY_DA_BACKEND_URL?: string | undefined;
15
13
  }): string;
14
+ /**
15
+ * Generate a CycloneDX SBOM from a manifest file. No backend HTTP request is made.
16
+ *
17
+ * @param {string} manifestPath - path to the manifest file (e.g. pom.xml, package.json)
18
+ * @param {Options} [opts={}] - optional options (e.g. workspace dir, tool paths)
19
+ * @returns {Promise<object>} parsed CycloneDX SBOM JSON object
20
+ * @throws {Error} if the manifest is unsupported or SBOM generation fails
21
+ */
22
+ export function generateSbom(manifestPath: string, opts?: Options): Promise<object>;
16
23
  export { parseImageRef } from "./oci_image/utils.js";
17
24
  export { ImageRef } from "./oci_image/images.js";
18
25
  declare namespace _default {
@@ -21,6 +28,7 @@ declare namespace _default {
21
28
  export { stackAnalysisBatch };
22
29
  export { imageAnalysis };
23
30
  export { validateToken };
31
+ export { generateSbom };
24
32
  }
25
33
  export default _default;
26
34
  export type Options = {
@@ -43,7 +51,9 @@ export type Options = {
43
51
  TRUSTIFY_DA_PYTHON_PATH?: string | undefined;
44
52
  TRUSTIFY_DA_PYTHON_VIRTUAL_ENV?: string | undefined;
45
53
  TRUSTIFY_DA_PYTHON3_PATH?: string | undefined;
46
- TRUSTIFY_DA_RECOMMENDATIONS_ENABLED?: string | undefined;
54
+ TRUSTIFY_DA_PROVIDERS?: string | undefined;
55
+ TRUSTIFY_DA_RECOMMEND?: string | undefined;
56
+ TRUSTIFY_DA_SOURCES?: string | undefined;
47
57
  TRUSTIFY_DA_SKOPEO_CONFIG_PATH?: string | undefined;
48
58
  TRUSTIFY_DA_SKOPEO_PATH?: string | undefined;
49
59
  TRUSTIFY_DA_SYFT_CONFIG_PATH?: string | undefined;
@@ -51,11 +61,11 @@ export type Options = {
51
61
  TRUSTIFY_DA_YARN_PATH?: string | undefined;
52
62
  TRUSTIFY_DA_WORKSPACE_DIR?: string | undefined;
53
63
  TRUSTIFY_DA_LICENSE_CHECK?: string | undefined;
64
+ licenseCheck?: boolean | undefined;
54
65
  MATCH_MANIFEST_VERSIONS?: string | undefined;
55
66
  TRUSTIFY_DA_SOURCE?: string | undefined;
56
67
  TRUSTIFY_DA_TOKEN?: string | undefined;
57
68
  TRUSTIFY_DA_TELEMETRY_ID?: string | undefined;
58
- TRUSTIFY_DA_WORKSPACE_DIR?: string | undefined;
59
69
  batchConcurrency?: number | undefined;
60
70
  TRUSTIFY_DA_BATCH_CONCURRENCY?: string | undefined;
61
71
  workspaceDiscoveryIgnore?: string[] | undefined;
@@ -64,11 +74,13 @@ export type Options = {
64
74
  TRUSTIFY_DA_CONTINUE_ON_ERROR?: string | undefined;
65
75
  batchMetadata?: boolean | undefined;
66
76
  TRUSTIFY_DA_BATCH_METADATA?: string | undefined;
77
+ TRUSTIFY_DA_UV_PATH?: string | undefined;
78
+ TRUSTIFY_DA_POETRY_PATH?: string | undefined;
67
79
  [key: string]: string | number | boolean | string[] | undefined;
68
80
  };
69
81
  export type BatchAnalysisMetadata = {
70
82
  workspaceRoot: string;
71
- ecosystem: "javascript" | "cargo" | "unknown";
83
+ ecosystem: "javascript" | "cargo" | "pyproject" | "unknown";
72
84
  total: number;
73
85
  successful: number;
74
86
  failed: number;
@@ -91,10 +103,10 @@ export type SbomResult = {
91
103
  * Get component analysis report for a manifest content.
92
104
  * @param {string} manifest - path to the manifest
93
105
  * @param {Options} [opts={}] - optional various options to pass along the application
94
- * @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>}
95
107
  * @throws {Error} if no matching provider, failed to get create content, or backend request failed
96
108
  */
97
- 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>;
98
110
  /**
99
111
  * @overload
100
112
  * @param {string} manifest
@@ -109,38 +121,93 @@ declare function stackAnalysis(manifest: string, html: true, opts?: Options | un
109
121
  * @param {string} manifest
110
122
  * @param {false} html
111
123
  * @param {Options} [opts={}]
112
- * @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>}
113
125
  * @throws {Error}
114
126
  */
115
- 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>;
116
128
  /**
117
129
  * Get stack analysis report for a manifest file.
118
130
  * @overload
119
131
  * @param {string} manifest - path for the manifest
120
132
  * @param {boolean} [html=false] - true will return a html string, false will return AnalysisReport object.
121
133
  * @param {Options} [opts={}] - optional various options to pass along the application
122
- * @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>}
123
135
  * @throws {Error} if manifest inaccessible, no matching provider, failed to get create content,
124
136
  * or backend request failed
125
137
  */
126
- 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
+ }>;
127
193
  /**
128
194
  * Get stack analysis for all workspace packages/crates (batch).
129
195
  * Detects ecosystem from workspace root: Cargo (Cargo.toml + Cargo.lock) or JS/TS (package.json + lock file).
130
196
  * SBOMs are generated in parallel (see `batchConcurrency`) unless `continueOnError: false` (fail-fast sequential).
131
197
  * With `opts.batchMetadata` / `TRUSTIFY_DA_BATCH_METADATA`, returns `{ analysis, metadata }` including validation and SBOM errors.
132
198
  *
199
+ * @overload
133
200
  * @param {string} workspaceRoot - Path to workspace root (containing lock file and workspace config)
134
201
  * @param {boolean} [html=false] - true returns HTML, false returns JSON report
135
202
  * @param {Options} [opts={}] - `batchConcurrency`, discovery ignores, `continueOnError` (default true), `batchMetadata` (default false)
136
- * @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 }>}
137
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.
138
205
  */
139
- declare function stackAnalysisBatch(workspaceRoot: string, html?: boolean, opts?: Options): Promise<string | {
140
- [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;
141
208
  } | {
142
209
  analysis: string | {
143
- [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;
144
211
  };
145
212
  metadata: BatchAnalysisMetadata;
146
213
  }>;
@@ -158,11 +225,11 @@ declare function imageAnalysis(imageRefs: Array<string>, html: true, opts?: Opti
158
225
  * @param {Array<string>} imageRefs
159
226
  * @param {false} html
160
227
  * @param {Options} [opts={}]
161
- * @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>>}
162
229
  * @throws {Error}
163
230
  */
164
231
  declare function imageAnalysis(imageRefs: Array<string>, html: false, opts?: Options | undefined): Promise<{
165
- [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;
166
233
  }>;
167
234
  /**
168
235
  * Get image analysis report for a set of OCI image references.
@@ -170,12 +237,12 @@ declare function imageAnalysis(imageRefs: Array<string>, html: false, opts?: Opt
170
237
  * @param {Array<string>} imageRefs - OCI image references
171
238
  * @param {boolean} [html=false] - true will return a html string, false will return AnalysisReport
172
239
  * @param {Options} [opts={}] - optional various options to pass along the application
173
- * @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>>}
174
241
  * @throws {Error} if manifest inaccessible, no matching provider, failed to get create content,
175
242
  * or backend request failed
176
243
  */
177
244
  declare function imageAnalysis(imageRefs: Array<string>, html?: boolean | undefined, opts?: Options | undefined): Promise<string | {
178
- [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;
179
246
  }>;
180
247
  /**
181
248
  * Validates the Exhort token.
@@ -184,6 +251,10 @@ declare function imageAnalysis(imageRefs: Array<string>, html?: boolean | undefi
184
251
  * @throws {Error} if the backend request failed.
185
252
  */
186
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';
187
258
  import { discoverWorkspacePackages } from './workspace.js';
188
259
  import { discoverWorkspaceCrates } from './workspace.js';
189
260
  import { validatePackageJson } from './workspace.js';
@@ -191,5 +262,9 @@ import { resolveWorkspaceDiscoveryIgnore } from './workspace.js';
191
262
  import { filterManifestPathsByDiscoveryIgnore } from './workspace.js';
192
263
  import { resolveContinueOnError } from './batch_opts.js';
193
264
  import { resolveBatchMetadata } from './batch_opts.js';
194
- export { discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson, resolveWorkspaceDiscoveryIgnore, filterManifestPathsByDiscoveryIgnore, resolveContinueOnError, resolveBatchMetadata };
265
+ export { discoverMavenModules, discoverGradleSubprojects, discoverGoWorkspaceModules, discoverUvWorkspaceMembers, discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson, resolveWorkspaceDiscoveryIgnore, filterManifestPathsByDiscoveryIgnore, resolveContinueOnError, resolveBatchMetadata };
195
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";
package/dist/src/index.js CHANGED
@@ -1,19 +1,26 @@
1
+ import fs from 'node:fs';
1
2
  import path from "node:path";
2
3
  import { EOL } from "os";
3
4
  import pLimit from 'p-limit';
4
- import { availableProviders, match } from './provider.js';
5
5
  import analysis from './analysis.js';
6
- import fs from 'node:fs';
7
- import { getCustom } from "./tools.js";
8
6
  import { resolveBatchMetadata, resolveContinueOnError } from './batch_opts.js';
7
+ import { getPackageVersion } from './package_version.js';
8
+ import { availableProviders, match } from './provider.js';
9
+ import { discoverGoWorkspaceModules } from './providers/golang_gomodules.js';
10
+ import { discoverGradleSubprojects } from './providers/java_gradle.js';
11
+ import { discoverMavenModules } from './providers/java_maven.js';
12
+ import { discoverUvWorkspaceMembers } from './providers/python_uv.js';
13
+ import { getCustom } from "./tools.js";
9
14
  import { discoverWorkspaceCrates, discoverWorkspacePackages, filterManifestPathsByDiscoveryIgnore, resolveWorkspaceDiscoveryIgnore, validatePackageJson, } from './workspace.js';
10
- import.meta.dirname;
11
- import * as url from 'url';
12
15
  export { parseImageRef } from "./oci_image/utils.js";
13
16
  export { ImageRef } from "./oci_image/images.js";
14
17
  export { getProjectLicense, findLicenseFilePath, identifyLicense, getLicenseDetails, licensesFromReport, normalizeLicensesResponse, runLicenseCheck, getCompatibility } from "./license/index.js";
15
- export default { componentAnalysis, stackAnalysis, stackAnalysisBatch, imageAnalysis, validateToken };
16
- export { discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson, resolveWorkspaceDiscoveryIgnore, filterManifestPathsByDiscoveryIgnore, resolveContinueOnError, resolveBatchMetadata, };
18
+ export { extractRemediations, maxSeverity } from "./remediation.js";
19
+ export { generateReport, generateDeduplicationKey } from './remediation_report.js';
20
+ export { loadConfig, mergeConfig, resolveConfig, CONFIG_FILENAMES } from './config.js';
21
+ export { runRemediation, findManifests } from './remediate.js';
22
+ export default { componentAnalysis, stackAnalysis, stackAnalysisBatch, imageAnalysis, validateToken, generateSbom };
23
+ export { discoverMavenModules, discoverGradleSubprojects, discoverGoWorkspaceModules, discoverUvWorkspaceMembers, discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson, resolveWorkspaceDiscoveryIgnore, filterManifestPathsByDiscoveryIgnore, resolveContinueOnError, resolveBatchMetadata, };
17
24
  /**
18
25
  * @typedef {{
19
26
  * TRUSTIFY_DA_CARGO_PATH?: string | undefined,
@@ -35,7 +42,9 @@ export { discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson
35
42
  * TRUSTIFY_DA_PYTHON_PATH?: string | undefined,
36
43
  * TRUSTIFY_DA_PYTHON_VIRTUAL_ENV?: string | undefined,
37
44
  * TRUSTIFY_DA_PYTHON3_PATH?: string | undefined,
38
- * TRUSTIFY_DA_RECOMMENDATIONS_ENABLED?: string | undefined,
45
+ * TRUSTIFY_DA_PROVIDERS?: string | undefined,
46
+ * TRUSTIFY_DA_RECOMMEND?: string | undefined,
47
+ * TRUSTIFY_DA_SOURCES?: string | undefined,
39
48
  * TRUSTIFY_DA_SKOPEO_CONFIG_PATH?: string | undefined,
40
49
  * TRUSTIFY_DA_SKOPEO_PATH?: string | undefined,
41
50
  * TRUSTIFY_DA_SYFT_CONFIG_PATH?: string | undefined,
@@ -43,11 +52,11 @@ export { discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson
43
52
  * TRUSTIFY_DA_YARN_PATH?: string | undefined,
44
53
  * TRUSTIFY_DA_WORKSPACE_DIR?: string | undefined,
45
54
  * TRUSTIFY_DA_LICENSE_CHECK?: string | undefined,
55
+ * licenseCheck?: boolean | undefined,
46
56
  * MATCH_MANIFEST_VERSIONS?: string | undefined,
47
57
  * TRUSTIFY_DA_SOURCE?: string | undefined,
48
58
  * TRUSTIFY_DA_TOKEN?: string | undefined,
49
59
  * TRUSTIFY_DA_TELEMETRY_ID?: string | undefined,
50
- * TRUSTIFY_DA_WORKSPACE_DIR?: string | undefined,
51
60
  * batchConcurrency?: number | undefined,
52
61
  * TRUSTIFY_DA_BATCH_CONCURRENCY?: string | undefined,
53
62
  * workspaceDiscoveryIgnore?: string[] | undefined,
@@ -56,13 +65,15 @@ export { discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson
56
65
  * TRUSTIFY_DA_CONTINUE_ON_ERROR?: string | undefined,
57
66
  * batchMetadata?: boolean | undefined,
58
67
  * TRUSTIFY_DA_BATCH_METADATA?: string | undefined,
68
+ * TRUSTIFY_DA_UV_PATH?: string | undefined,
69
+ * TRUSTIFY_DA_POETRY_PATH?: string | undefined,
59
70
  * [key: string]: string | number | boolean | string[] | undefined,
60
71
  * }} Options
61
72
  */
62
73
  /**
63
74
  * @typedef {{
64
75
  * workspaceRoot: string,
65
- * ecosystem: 'javascript' | 'cargo' | 'unknown',
76
+ * ecosystem: 'javascript' | 'cargo' | 'pyproject' | 'unknown',
66
77
  * total: number,
67
78
  * successful: number,
68
79
  * failed: number,
@@ -85,29 +96,11 @@ function logOptionsAndEnvironmentsVariables(alongsideText, valueToBePrinted) {
85
96
  * @private
86
97
  */
87
98
  function readAndPrintVersionFromPackageJson() {
88
- let dirName;
89
- // new ESM way in nodeJS ( since node version 22 ) to bring module directory.
90
- dirName = import.meta.dirname;
91
- // old ESM way in nodeJS ( before node versions 22.00 to bring module directory)
92
- if (!dirName) {
93
- dirName = url.fileURLToPath(new URL('.', import.meta.url));
94
- }
95
- try {
96
- if (__dirname) {
97
- dirName = __dirname;
98
- }
99
- }
100
- catch (e) {
101
- console.log("__dirname is not defined, continue with fileUrlPath");
102
- }
103
- let packageJson = JSON.parse(fs.readFileSync(path.join(dirName, "..", "package.json")).toString());
104
- logOptionsAndEnvironmentsVariables("trustify-da-javascript-client analysis started, version: ", packageJson.version);
99
+ logOptionsAndEnvironmentsVariables("trustify-da-javascript-client analysis started, version: ", getPackageVersion());
105
100
  }
106
101
  /**
107
- * This function is used to determine the Trustify DA backend URL.
108
- * The TRUSTIFY_DA_BACKEND_URL is evaluated in the following order and selected when it finds it first:
109
- * 1. Environment Variable
110
- * 2. (key,value) from opts object
102
+ * Selects the Trustify DA backend URL from opts.TRUSTIFY_DA_BACKEND_URL, then environment
103
+ * variable TRUSTIFY_DA_BACKEND_URL. Options take precedence over environment variables.
111
104
  * If TRUSTIFY_DA_BACKEND_URL is not set, the function will throw an error.
112
105
  * @param {{TRUSTIFY_DA_DEBUG?: string | undefined; TRUSTIFY_DA_BACKEND_URL?: string | undefined}} [opts={}]
113
106
  * @return {string} - The selected Trustify DA backend URL
@@ -138,7 +131,7 @@ export function selectTrustifyDABackend(opts = {}) {
138
131
  * @param {string} manifest
139
132
  * @param {false} html
140
133
  * @param {Options} [opts={}]
141
- * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
134
+ * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
142
135
  * @throws {Error}
143
136
  */
144
137
  /**
@@ -147,7 +140,7 @@ export function selectTrustifyDABackend(opts = {}) {
147
140
  * @param {string} manifest - path for the manifest
148
141
  * @param {boolean} [html=false] - true will return a html string, false will return AnalysisReport object.
149
142
  * @param {Options} [opts={}] - optional various options to pass along the application
150
- * @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
143
+ * @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
151
144
  * @throws {Error} if manifest inaccessible, no matching provider, failed to get create content,
152
145
  * or backend request failed
153
146
  */
@@ -161,7 +154,7 @@ async function stackAnalysis(manifest, html = false, opts = {}) {
161
154
  * Get component analysis report for a manifest content.
162
155
  * @param {string} manifest - path to the manifest
163
156
  * @param {Options} [opts={}] - optional various options to pass along the application
164
- * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
157
+ * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
165
158
  * @throws {Error} if no matching provider, failed to get create content, or backend request failed
166
159
  */
167
160
  async function componentAnalysis(manifest, opts = {}) {
@@ -169,7 +162,9 @@ async function componentAnalysis(manifest, opts = {}) {
169
162
  fs.accessSync(manifest, fs.constants.R_OK);
170
163
  opts["manifest-type"] = path.basename(manifest);
171
164
  let provider = match(manifest, availableProviders, opts); // throws error if no matching provider
172
- return await analysis.requestComponent(provider, manifest, theUrl, opts); // throws error request sending failed
165
+ const result = await analysis.requestComponent(provider, manifest, theUrl, opts); // throws error request sending failed
166
+ result.packageManager = provider.packageManagerName();
167
+ return result;
173
168
  }
174
169
  /**
175
170
  * @overload
@@ -184,7 +179,7 @@ async function componentAnalysis(manifest, opts = {}) {
184
179
  * @param {Array<string>} imageRefs
185
180
  * @param {false} html
186
181
  * @param {Options} [opts={}]
187
- * @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
182
+ * @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
188
183
  * @throws {Error}
189
184
  */
190
185
  /**
@@ -193,7 +188,7 @@ async function componentAnalysis(manifest, opts = {}) {
193
188
  * @param {Array<string>} imageRefs - OCI image references
194
189
  * @param {boolean} [html=false] - true will return a html string, false will return AnalysisReport
195
190
  * @param {Options} [opts={}] - optional various options to pass along the application
196
- * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
191
+ * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
197
192
  * @throws {Error} if manifest inaccessible, no matching provider, failed to get create content,
198
193
  * or backend request failed
199
194
  */
@@ -202,7 +197,8 @@ async function imageAnalysis(imageRefs, html = false, opts = {}) {
202
197
  return await analysis.requestImages(imageRefs, theUrl, html, opts);
203
198
  }
204
199
  /**
205
- * Max concurrent SBOM generations for batch workspace analysis. Env/opts override default 10.
200
+ * Max concurrent SBOM generations for batch workspace analysis. Options override environment;
201
+ * environment overrides the default of 10.
206
202
  * @param {Options} opts
207
203
  * @returns {number}
208
204
  * @private
@@ -235,6 +231,22 @@ function buildBatchAnalysisMetadata(root, ecosystem, totalSbomAttempts, successf
235
231
  errors: [...errors],
236
232
  };
237
233
  }
234
+ /**
235
+ * Generate a CycloneDX SBOM from a manifest file. No backend HTTP request is made.
236
+ *
237
+ * @param {string} manifestPath - path to the manifest file (e.g. pom.xml, package.json)
238
+ * @param {Options} [opts={}] - optional options (e.g. workspace dir, tool paths)
239
+ * @returns {Promise<object>} parsed CycloneDX SBOM JSON object
240
+ * @throws {Error} if the manifest is unsupported or SBOM generation fails
241
+ */
242
+ export async function generateSbom(manifestPath, opts = {}) {
243
+ fs.accessSync(manifestPath, fs.constants.R_OK);
244
+ const result = await generateOneSbom(manifestPath, opts);
245
+ if (!result.ok) {
246
+ throw new Error(`Failed to generate SBOM for ${result.manifestPath}: ${result.reason}`);
247
+ }
248
+ return result.sbom;
249
+ }
238
250
  /**
239
251
  * @typedef {{ ok: true, purl: string, sbom: object } | { ok: false, manifestPath: string, reason: string }} SbomResult
240
252
  */
@@ -261,17 +273,45 @@ async function generateOneSbom(manifestPath, workspaceOpts) {
261
273
  *
262
274
  * @param {string} root - Resolved workspace root
263
275
  * @param {Options} opts
264
- * @returns {Promise<{ ecosystem: 'javascript' | 'cargo' | 'unknown', manifestPaths: string[] }>}
276
+ * @returns {Promise<{ ecosystem: 'javascript' | 'cargo' | 'maven' | 'gradle' | 'gomodules' | 'pyproject' | 'unknown', manifestPaths: string[] }>}
265
277
  * @private
266
278
  */
267
279
  async function detectWorkspaceManifests(root, opts) {
268
280
  const cargoToml = path.join(root, 'Cargo.toml');
269
281
  const cargoLock = path.join(root, 'Cargo.lock');
270
282
  const packageJson = path.join(root, 'package.json');
283
+ const pomXml = path.join(root, 'pom.xml');
271
284
  if (fs.existsSync(cargoToml) && fs.existsSync(cargoLock)) {
272
285
  return { ecosystem: 'cargo', manifestPaths: await discoverWorkspaceCrates(root, opts) };
273
286
  }
274
- const hasJsLock = fs.existsSync(path.join(root, 'pnpm-lock.yaml'))
287
+ if (fs.existsSync(pomXml)) {
288
+ const manifestPaths = await discoverMavenModules(root, opts);
289
+ if (manifestPaths.length > 0) {
290
+ return { ecosystem: 'maven', manifestPaths };
291
+ }
292
+ }
293
+ const hasGradleSettings = fs.existsSync(path.join(root, 'settings.gradle'))
294
+ || fs.existsSync(path.join(root, 'settings.gradle.kts'));
295
+ if (hasGradleSettings) {
296
+ const manifestPaths = await discoverGradleSubprojects(root, opts);
297
+ if (manifestPaths.length > 0) {
298
+ return { ecosystem: 'gradle', manifestPaths };
299
+ }
300
+ }
301
+ if (fs.existsSync(path.join(root, 'go.work'))) {
302
+ const manifestPaths = await discoverGoWorkspaceModules(root, opts);
303
+ if (manifestPaths.length > 0) {
304
+ return { ecosystem: 'gomodules', manifestPaths };
305
+ }
306
+ }
307
+ if (fs.existsSync(path.join(root, 'pyproject.toml')) && fs.existsSync(path.join(root, 'uv.lock'))) {
308
+ const manifestPaths = await discoverUvWorkspaceMembers(root, opts);
309
+ if (manifestPaths.length > 0) {
310
+ return { ecosystem: 'pyproject', manifestPaths };
311
+ }
312
+ }
313
+ const hasJsLock = fs.existsSync(path.join(root, 'bun.lock'))
314
+ || fs.existsSync(path.join(root, 'pnpm-lock.yaml'))
275
315
  || fs.existsSync(path.join(root, 'yarn.lock'))
276
316
  || fs.existsSync(path.join(root, 'package-lock.json'));
277
317
  if (fs.existsSync(packageJson) && hasJsLock) {
@@ -380,16 +420,49 @@ function batchError(message, wantMetadata, metadata) {
380
420
  }
381
421
  return err;
382
422
  }
423
+ /**
424
+ * @overload
425
+ * @param {string} workspaceRoot
426
+ * @param {true} html
427
+ * @param {Options & { batchMetadata: true }} opts
428
+ * @returns {Promise<{ analysis: string, metadata: BatchAnalysisMetadata }>}
429
+ * @throws {Error}
430
+ */
431
+ /**
432
+ * @overload
433
+ * @param {string} workspaceRoot
434
+ * @param {true} html
435
+ * @param {Options & { batchMetadata?: false }} [opts={}]
436
+ * @returns {Promise<string>}
437
+ * @throws {Error}
438
+ */
439
+ /**
440
+ * @overload
441
+ * @param {string} workspaceRoot
442
+ * @param {false} html
443
+ * @param {Options & { batchMetadata: true }} opts
444
+ * @returns {Promise<{ analysis: Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>, metadata: BatchAnalysisMetadata }>}
445
+ * @throws {Error}
446
+ */
447
+ /**
448
+ * @overload
449
+ * @param {string} workspaceRoot
450
+ * @param {false} html
451
+ * @param {Options & { batchMetadata?: false }} [opts={}]
452
+ * @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
453
+ * @throws {Error}
454
+ */
383
455
  /**
384
456
  * Get stack analysis for all workspace packages/crates (batch).
385
457
  * Detects ecosystem from workspace root: Cargo (Cargo.toml + Cargo.lock) or JS/TS (package.json + lock file).
386
458
  * SBOMs are generated in parallel (see `batchConcurrency`) unless `continueOnError: false` (fail-fast sequential).
387
459
  * With `opts.batchMetadata` / `TRUSTIFY_DA_BATCH_METADATA`, returns `{ analysis, metadata }` including validation and SBOM errors.
388
460
  *
461
+ * @overload
389
462
  * @param {string} workspaceRoot - Path to workspace root (containing lock file and workspace config)
390
463
  * @param {boolean} [html=false] - true returns HTML, false returns JSON report
391
464
  * @param {Options} [opts={}] - `batchConcurrency`, discovery ignores, `continueOnError` (default true), `batchMetadata` (default false)
392
- * @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 }>}
465
+ * @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 }>}
393
466
  * @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.
394
467
  */
395
468
  async function stackAnalysisBatch(workspaceRoot, html = false, opts = {}) {
@@ -416,7 +489,7 @@ async function stackAnalysisBatch(workspaceRoot, html = false, opts = {}) {
416
489
  }
417
490
  }
418
491
  if (manifestPaths.length === 0) {
419
- throw new Error(`No workspace manifests found at ${root}. Ensure Cargo.toml+Cargo.lock or package.json+lock file exist.`);
492
+ throw new Error(`No workspace manifests found at ${root}. Ensure a supported workspace root exists (Cargo.toml+Cargo.lock, pom.xml, build.gradle, go.work, pyproject.toml+uv.lock, or package.json+lock file).`);
420
493
  }
421
494
  const workspaceOpts = { ...opts, TRUSTIFY_DA_WORKSPACE_DIR: root };
422
495
  const concurrency = resolveBatchConcurrency(opts);
@@ -6,13 +6,13 @@
6
6
  * @param {string} manifestPath - path to manifest
7
7
  * @param {string} url - the backend url to send the request to
8
8
  * @param {import('../index.js').Options} [opts={}]
9
- * @param {import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport} [analysisResult] - analysis result that includes licenses array from backend
9
+ * @param {import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport} [analysisResult] - analysis result that includes licenses array from backend
10
10
  * @returns {Promise<{ projectLicense: { manifest: Object|null, file: Object|null, mismatch: boolean }, incompatibleDependencies: Array<{ purl: string, licenses: string[], category?: string, reason: string }>, error?: string }>}
11
11
  */
12
- export function runLicenseCheck(sbomContent: string, manifestPath: string, url: string, opts?: import("../index.js").Options, analysisResult?: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport): Promise<{
12
+ export function runLicenseCheck(sbomContent: string, manifestPath: string, url: string, opts?: import("../index.js").Options, analysisResult?: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport): Promise<{
13
13
  projectLicense: {
14
- manifest: any | null;
15
- file: any | null;
14
+ manifest: Object | null;
15
+ file: Object | null;
16
16
  mismatch: boolean;
17
17
  };
18
18
  incompatibleDependencies: Array<{
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * License resolution and dependency license compatibility for component analysis.
3
3
  */
4
- import { getProjectLicense, findLicenseFilePath, identifyLicense } from './project_license.js';
5
- import { licensesFromReport, getLicenseDetails } from './licenses_api.js';
6
4
  import { getCompatibility } from './license_utils.js';
5
+ import { licensesFromReport, getLicenseDetails } from './licenses_api.js';
6
+ import { getProjectLicense, findLicenseFilePath, identifyLicense } from './project_license.js';
7
7
  export { getProjectLicense, findLicenseFilePath, identifyLicense } from './project_license.js';
8
8
  export { licensesFromReport, normalizeLicensesResponse, getLicenseDetails } from './licenses_api.js';
9
9
  export { getCompatibility } from './license_utils.js';
@@ -15,7 +15,7 @@ export { getCompatibility } from './license_utils.js';
15
15
  * @param {string} manifestPath - path to manifest
16
16
  * @param {string} url - the backend url to send the request to
17
17
  * @param {import('../index.js').Options} [opts={}]
18
- * @param {import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport} [analysisResult] - analysis result that includes licenses array from backend
18
+ * @param {import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport} [analysisResult] - analysis result that includes licenses array from backend
19
19
  * @returns {Promise<{ projectLicense: { manifest: Object|null, file: Object|null, mismatch: boolean }, incompatibleDependencies: Array<{ purl: string, licenses: string[], category?: string, reason: string }>, error?: string }>}
20
20
  */
21
21
  export async function runLicenseCheck(sbomContent, manifestPath, url, opts = {}, analysisResult = null) {
@@ -39,10 +39,12 @@ export async function runLicenseCheck(sbomContent, manifestPath, url, opts = {},
39
39
  // Fetch detailed license info from backend (avoid duplicate calls if same license)
40
40
  const licenseDetailsCache = new Map();
41
41
  async function getDetails(spdxId) {
42
- if (!spdxId || !url)
42
+ if (!spdxId || !url) {
43
43
  return null;
44
- if (licenseDetailsCache.has(spdxId))
44
+ }
45
+ if (licenseDetailsCache.has(spdxId)) {
45
46
  return licenseDetailsCache.get(spdxId);
47
+ }
46
48
  try {
47
49
  const details = await getLicenseDetails(spdxId, { ...opts, TRUSTIFY_DA_BACKEND_URL: url });
48
50
  licenseDetailsCache.set(spdxId, details);
@@ -81,15 +83,19 @@ export async function runLicenseCheck(sbomContent, manifestPath, url, opts = {},
81
83
  const incompatibleDependencies = [];
82
84
  for (const purl of purls) {
83
85
  const entry = licenseByPurl.get(purl);
84
- if (!entry)
86
+ if (!entry) {
85
87
  continue;
88
+ }
86
89
  const status = getCompatibility(projectCategory, entry.category);
87
90
  if (status === 'incompatible') {
91
+ const reason = entry.category?.toUpperCase() === 'UNKNOWN'
92
+ ? 'License not recognized as a standard SPDX identifier. Manual review recommended to verify compatibility.'
93
+ : 'Dependency license(s) are incompatible with the project license.';
88
94
  incompatibleDependencies.push({
89
95
  purl,
90
96
  licenses: entry.licenses,
91
97
  category: entry.category,
92
- reason: 'Dependency license(s) are incompatible with the project license.'
98
+ reason
93
99
  });
94
100
  }
95
101
  }
@@ -114,9 +114,12 @@ export function getCompatibility(projectCategory, dependencyCategory) {
114
114
  }
115
115
  const proj = projectCategory.toUpperCase();
116
116
  const dep = dependencyCategory.toUpperCase();
117
- if (proj === 'UNKNOWN' || dep === 'UNKNOWN') {
117
+ if (proj === 'UNKNOWN') {
118
118
  return 'unknown';
119
119
  }
120
+ if (dep === 'UNKNOWN') {
121
+ return 'incompatible';
122
+ }
120
123
  const restrictiveness = {
121
124
  'PERMISSIVE': 1,
122
125
  'WEAK_COPYLEFT': 2,