@trustify-da/trustify-da-javascript-client 0.3.0-ea.3aa2054 → 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.
- package/README.md +199 -11
- package/dist/package.json +30 -26
- package/dist/src/analysis.d.ts +50 -22
- package/dist/src/analysis.js +70 -18
- package/dist/src/cli.js +188 -6
- package/dist/src/config.d.ts +120 -0
- package/dist/src/config.js +260 -0
- package/dist/src/cyclone_dx_sbom.d.ts +20 -1
- package/dist/src/cyclone_dx_sbom.js +68 -9
- package/dist/src/index.d.ts +85 -21
- package/dist/src/index.js +96 -40
- package/dist/src/license/index.d.ts +4 -4
- package/dist/src/license/index.js +13 -7
- package/dist/src/license/license_utils.js +4 -1
- package/dist/src/license/licenses_api.d.ts +3 -3
- package/dist/src/license/licenses_api.js +1 -1
- package/dist/src/oci_image/images.d.ts +1 -1
- package/dist/src/oci_image/images.js +15 -1
- package/dist/src/oci_image/utils.d.ts +5 -5
- package/dist/src/package_version.d.ts +8 -0
- package/dist/src/package_version.js +31 -0
- package/dist/src/provider.d.ts +4 -3
- package/dist/src/provider.js +8 -2
- package/dist/src/providers/base_java.d.ts +67 -12
- package/dist/src/providers/base_java.js +82 -61
- package/dist/src/providers/base_javascript.d.ts +74 -9
- package/dist/src/providers/base_javascript.js +116 -7
- package/dist/src/providers/base_pyproject.d.ts +19 -31
- package/dist/src/providers/base_pyproject.js +57 -73
- package/dist/src/providers/containerfile_parser.d.ts +5 -0
- package/dist/src/providers/containerfile_parser.js +20 -0
- package/dist/src/providers/golang_gomodules.d.ts +11 -1
- package/dist/src/providers/golang_gomodules.js +114 -15
- package/dist/src/providers/java_gradle.d.ts +67 -0
- package/dist/src/providers/java_gradle.js +312 -20
- package/dist/src/providers/java_gradle_groovy.d.ts +1 -1
- package/dist/src/providers/java_gradle_kotlin.d.ts +1 -1
- package/dist/src/providers/java_maven.d.ts +37 -9
- package/dist/src/providers/java_maven.js +269 -11
- package/dist/src/providers/javascript_bun.d.ts +22 -0
- package/dist/src/providers/javascript_bun.js +141 -0
- package/dist/src/providers/javascript_npm.d.ts +19 -0
- package/dist/src/providers/javascript_npm.js +61 -1
- package/dist/src/providers/javascript_pnpm.d.ts +13 -0
- package/dist/src/providers/javascript_pnpm.js +53 -3
- package/dist/src/providers/javascript_yarn.d.ts +12 -0
- package/dist/src/providers/javascript_yarn.js +75 -2
- package/dist/src/providers/manifest.js +6 -3
- package/dist/src/providers/marker_evaluator.d.ts +14 -0
- package/dist/src/providers/marker_evaluator.js +191 -0
- package/dist/src/providers/oci_dockerfile.d.ts +51 -0
- package/dist/src/providers/oci_dockerfile.js +177 -0
- package/dist/src/providers/processors/yarn_berry_processor.d.ts +6 -2
- package/dist/src/providers/processors/yarn_berry_processor.js +9 -4
- package/dist/src/providers/processors/yarn_classic_processor.d.ts +6 -2
- package/dist/src/providers/processors/yarn_classic_processor.js +8 -6
- package/dist/src/providers/python_controller.d.ts +5 -1
- package/dist/src/providers/python_controller.js +69 -8
- package/dist/src/providers/python_pip.d.ts +5 -0
- package/dist/src/providers/python_pip.js +5 -5
- package/dist/src/providers/python_pip_pyproject.d.ts +61 -0
- package/dist/src/providers/python_pip_pyproject.js +146 -0
- package/dist/src/providers/python_poetry.d.ts +64 -8
- package/dist/src/providers/python_poetry.js +139 -22
- package/dist/src/providers/python_uv.d.ts +20 -1
- package/dist/src/providers/python_uv.js +126 -3
- package/dist/src/providers/requirements_parser.js +1 -1
- package/dist/src/providers/rust_cargo.d.ts +6 -2
- package/dist/src/providers/rust_cargo.js +101 -33
- package/dist/src/providers/tree-sitter-containerfile.wasm +0 -0
- package/dist/src/remediate.d.ts +120 -0
- package/dist/src/remediate.js +253 -0
- package/dist/src/remediation.d.ts +58 -0
- package/dist/src/remediation.js +425 -0
- package/dist/src/remediation_report.d.ts +40 -0
- package/dist/src/remediation_report.js +159 -0
- package/dist/src/sbom.d.ts +18 -1
- package/dist/src/sbom.js +14 -2
- package/dist/src/tools.d.ts +37 -13
- package/dist/src/tools.js +92 -13
- package/dist/src/updaters/maven_updater.d.ts +58 -0
- package/dist/src/updaters/maven_updater.js +345 -0
- package/dist/src/updaters/toml_updater.d.ts +62 -0
- package/dist/src/updaters/toml_updater.js +276 -0
- package/dist/src/workspace.d.ts +11 -1
- package/dist/src/workspace.js +3 -2
- 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
|
|
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
|
-
* @
|
|
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
|
-
|
|
51
|
+
const validLicenses = licenseArray
|
|
52
|
+
.map(lic => {
|
|
44
53
|
if (typeof lic === 'string') {
|
|
45
54
|
return { license: { id: lic } };
|
|
46
55
|
}
|
|
47
|
-
|
|
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
|
-
|
|
98
|
-
|
|
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.
|
|
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),
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
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
|
|
@@ -24,6 +22,7 @@ export function selectTrustifyDABackend(opts?: {
|
|
|
24
22
|
export function generateSbom(manifestPath: string, opts?: Options): Promise<object>;
|
|
25
23
|
export { parseImageRef } from "./oci_image/utils.js";
|
|
26
24
|
export { ImageRef } from "./oci_image/images.js";
|
|
25
|
+
export { extractRemediations } from "./remediation.js";
|
|
27
26
|
declare namespace _default {
|
|
28
27
|
export { componentAnalysis };
|
|
29
28
|
export { stackAnalysis };
|
|
@@ -53,7 +52,9 @@ export type Options = {
|
|
|
53
52
|
TRUSTIFY_DA_PYTHON_PATH?: string | undefined;
|
|
54
53
|
TRUSTIFY_DA_PYTHON_VIRTUAL_ENV?: string | undefined;
|
|
55
54
|
TRUSTIFY_DA_PYTHON3_PATH?: string | undefined;
|
|
56
|
-
|
|
55
|
+
TRUSTIFY_DA_PROVIDERS?: string | undefined;
|
|
56
|
+
TRUSTIFY_DA_RECOMMEND?: string | undefined;
|
|
57
|
+
TRUSTIFY_DA_SOURCES?: string | undefined;
|
|
57
58
|
TRUSTIFY_DA_SKOPEO_CONFIG_PATH?: string | undefined;
|
|
58
59
|
TRUSTIFY_DA_SKOPEO_PATH?: string | undefined;
|
|
59
60
|
TRUSTIFY_DA_SYFT_CONFIG_PATH?: string | undefined;
|
|
@@ -61,6 +62,7 @@ export type Options = {
|
|
|
61
62
|
TRUSTIFY_DA_YARN_PATH?: string | undefined;
|
|
62
63
|
TRUSTIFY_DA_WORKSPACE_DIR?: string | undefined;
|
|
63
64
|
TRUSTIFY_DA_LICENSE_CHECK?: string | undefined;
|
|
65
|
+
licenseCheck?: boolean | undefined;
|
|
64
66
|
MATCH_MANIFEST_VERSIONS?: string | undefined;
|
|
65
67
|
TRUSTIFY_DA_SOURCE?: string | undefined;
|
|
66
68
|
TRUSTIFY_DA_TOKEN?: string | undefined;
|
|
@@ -80,7 +82,7 @@ export type Options = {
|
|
|
80
82
|
};
|
|
81
83
|
export type BatchAnalysisMetadata = {
|
|
82
84
|
workspaceRoot: string;
|
|
83
|
-
ecosystem: "javascript" | "cargo" | "unknown";
|
|
85
|
+
ecosystem: "javascript" | "cargo" | "pyproject" | "unknown";
|
|
84
86
|
total: number;
|
|
85
87
|
successful: number;
|
|
86
88
|
failed: number;
|
|
@@ -103,10 +105,10 @@ export type SbomResult = {
|
|
|
103
105
|
* Get component analysis report for a manifest content.
|
|
104
106
|
* @param {string} manifest - path to the manifest
|
|
105
107
|
* @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>}
|
|
108
|
+
* @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
|
|
107
109
|
* @throws {Error} if no matching provider, failed to get create content, or backend request failed
|
|
108
110
|
*/
|
|
109
|
-
declare function componentAnalysis(manifest: string, opts?: Options): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport>;
|
|
111
|
+
declare function componentAnalysis(manifest: string, opts?: Options): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport>;
|
|
110
112
|
/**
|
|
111
113
|
* @overload
|
|
112
114
|
* @param {string} manifest
|
|
@@ -121,38 +123,93 @@ declare function stackAnalysis(manifest: string, html: true, opts?: Options | un
|
|
|
121
123
|
* @param {string} manifest
|
|
122
124
|
* @param {false} html
|
|
123
125
|
* @param {Options} [opts={}]
|
|
124
|
-
* @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
|
|
126
|
+
* @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
|
|
125
127
|
* @throws {Error}
|
|
126
128
|
*/
|
|
127
|
-
declare function stackAnalysis(manifest: string, html: false, opts?: Options | undefined): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport>;
|
|
129
|
+
declare function stackAnalysis(manifest: string, html: false, opts?: Options | undefined): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport>;
|
|
128
130
|
/**
|
|
129
131
|
* Get stack analysis report for a manifest file.
|
|
130
132
|
* @overload
|
|
131
133
|
* @param {string} manifest - path for the manifest
|
|
132
134
|
* @param {boolean} [html=false] - true will return a html string, false will return AnalysisReport object.
|
|
133
135
|
* @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>}
|
|
136
|
+
* @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
|
|
135
137
|
* @throws {Error} if manifest inaccessible, no matching provider, failed to get create content,
|
|
136
138
|
* or backend request failed
|
|
137
139
|
*/
|
|
138
|
-
declare function stackAnalysis(manifest: string, html?: boolean | undefined, opts?: Options | undefined): Promise<string | import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport>;
|
|
140
|
+
declare function stackAnalysis(manifest: string, html?: boolean | undefined, opts?: Options | undefined): Promise<string | import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport>;
|
|
141
|
+
/**
|
|
142
|
+
* @overload
|
|
143
|
+
* @param {string} workspaceRoot
|
|
144
|
+
* @param {true} html
|
|
145
|
+
* @param {Options & { batchMetadata: true }} opts
|
|
146
|
+
* @returns {Promise<{ analysis: string, metadata: BatchAnalysisMetadata }>}
|
|
147
|
+
* @throws {Error}
|
|
148
|
+
*/
|
|
149
|
+
declare function stackAnalysisBatch(workspaceRoot: string, html: true, opts: Options & {
|
|
150
|
+
batchMetadata: true;
|
|
151
|
+
}): Promise<{
|
|
152
|
+
analysis: string;
|
|
153
|
+
metadata: BatchAnalysisMetadata;
|
|
154
|
+
}>;
|
|
155
|
+
/**
|
|
156
|
+
* @overload
|
|
157
|
+
* @param {string} workspaceRoot
|
|
158
|
+
* @param {true} html
|
|
159
|
+
* @param {Options & { batchMetadata?: false }} [opts={}]
|
|
160
|
+
* @returns {Promise<string>}
|
|
161
|
+
* @throws {Error}
|
|
162
|
+
*/
|
|
163
|
+
declare function stackAnalysisBatch(workspaceRoot: string, html: true, opts?: (Options & {
|
|
164
|
+
batchMetadata?: false;
|
|
165
|
+
}) | undefined): Promise<string>;
|
|
166
|
+
/**
|
|
167
|
+
* @overload
|
|
168
|
+
* @param {string} workspaceRoot
|
|
169
|
+
* @param {false} html
|
|
170
|
+
* @param {Options & { batchMetadata: true }} opts
|
|
171
|
+
* @returns {Promise<{ analysis: Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>, metadata: BatchAnalysisMetadata }>}
|
|
172
|
+
* @throws {Error}
|
|
173
|
+
*/
|
|
174
|
+
declare function stackAnalysisBatch(workspaceRoot: string, html: false, opts: Options & {
|
|
175
|
+
batchMetadata: true;
|
|
176
|
+
}): Promise<{
|
|
177
|
+
analysis: {
|
|
178
|
+
[x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
|
|
179
|
+
};
|
|
180
|
+
metadata: BatchAnalysisMetadata;
|
|
181
|
+
}>;
|
|
182
|
+
/**
|
|
183
|
+
* @overload
|
|
184
|
+
* @param {string} workspaceRoot
|
|
185
|
+
* @param {false} html
|
|
186
|
+
* @param {Options & { batchMetadata?: false }} [opts={}]
|
|
187
|
+
* @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
|
|
188
|
+
* @throws {Error}
|
|
189
|
+
*/
|
|
190
|
+
declare function stackAnalysisBatch(workspaceRoot: string, html: false, opts?: (Options & {
|
|
191
|
+
batchMetadata?: false;
|
|
192
|
+
}) | undefined): Promise<{
|
|
193
|
+
[x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
|
|
194
|
+
}>;
|
|
139
195
|
/**
|
|
140
196
|
* Get stack analysis for all workspace packages/crates (batch).
|
|
141
197
|
* Detects ecosystem from workspace root: Cargo (Cargo.toml + Cargo.lock) or JS/TS (package.json + lock file).
|
|
142
198
|
* SBOMs are generated in parallel (see `batchConcurrency`) unless `continueOnError: false` (fail-fast sequential).
|
|
143
199
|
* With `opts.batchMetadata` / `TRUSTIFY_DA_BATCH_METADATA`, returns `{ analysis, metadata }` including validation and SBOM errors.
|
|
144
200
|
*
|
|
201
|
+
* @overload
|
|
145
202
|
* @param {string} workspaceRoot - Path to workspace root (containing lock file and workspace config)
|
|
146
203
|
* @param {boolean} [html=false] - true returns HTML, false returns JSON report
|
|
147
204
|
* @param {Options} [opts={}] - `batchConcurrency`, discovery ignores, `continueOnError` (default true), `batchMetadata` (default false)
|
|
148
|
-
* @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 }>}
|
|
205
|
+
* @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
206
|
* @throws {Error} if workspace root invalid, no manifests found, no packages pass validation, no SBOMs produced, or backend request failed. When `opts.batchMetadata` is set, `error.batchMetadata` may be set on thrown errors.
|
|
150
207
|
*/
|
|
151
|
-
declare function stackAnalysisBatch(workspaceRoot: string, html?: boolean, opts?: Options): Promise<string | {
|
|
152
|
-
[x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
|
|
208
|
+
declare function stackAnalysisBatch(workspaceRoot: string, html?: boolean | undefined, opts?: Options | undefined): Promise<string | {
|
|
209
|
+
[x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
|
|
153
210
|
} | {
|
|
154
211
|
analysis: string | {
|
|
155
|
-
[x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
|
|
212
|
+
[x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
|
|
156
213
|
};
|
|
157
214
|
metadata: BatchAnalysisMetadata;
|
|
158
215
|
}>;
|
|
@@ -170,11 +227,11 @@ declare function imageAnalysis(imageRefs: Array<string>, html: true, opts?: Opti
|
|
|
170
227
|
* @param {Array<string>} imageRefs
|
|
171
228
|
* @param {false} html
|
|
172
229
|
* @param {Options} [opts={}]
|
|
173
|
-
* @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
|
|
230
|
+
* @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
|
|
174
231
|
* @throws {Error}
|
|
175
232
|
*/
|
|
176
233
|
declare function imageAnalysis(imageRefs: Array<string>, html: false, opts?: Options | undefined): Promise<{
|
|
177
|
-
[x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
|
|
234
|
+
[x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
|
|
178
235
|
}>;
|
|
179
236
|
/**
|
|
180
237
|
* Get image analysis report for a set of OCI image references.
|
|
@@ -182,12 +239,12 @@ declare function imageAnalysis(imageRefs: Array<string>, html: false, opts?: Opt
|
|
|
182
239
|
* @param {Array<string>} imageRefs - OCI image references
|
|
183
240
|
* @param {boolean} [html=false] - true will return a html string, false will return AnalysisReport
|
|
184
241
|
* @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>>}
|
|
242
|
+
* @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
|
|
186
243
|
* @throws {Error} if manifest inaccessible, no matching provider, failed to get create content,
|
|
187
244
|
* or backend request failed
|
|
188
245
|
*/
|
|
189
246
|
declare function imageAnalysis(imageRefs: Array<string>, html?: boolean | undefined, opts?: Options | undefined): Promise<string | {
|
|
190
|
-
[x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
|
|
247
|
+
[x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
|
|
191
248
|
}>;
|
|
192
249
|
/**
|
|
193
250
|
* Validates the Exhort token.
|
|
@@ -196,6 +253,10 @@ declare function imageAnalysis(imageRefs: Array<string>, html?: boolean | undefi
|
|
|
196
253
|
* @throws {Error} if the backend request failed.
|
|
197
254
|
*/
|
|
198
255
|
declare function validateToken(opts?: Options): Promise<object>;
|
|
256
|
+
import { discoverMavenModules } from './providers/java_maven.js';
|
|
257
|
+
import { discoverGradleSubprojects } from './providers/java_gradle.js';
|
|
258
|
+
import { discoverGoWorkspaceModules } from './providers/golang_gomodules.js';
|
|
259
|
+
import { discoverUvWorkspaceMembers } from './providers/python_uv.js';
|
|
199
260
|
import { discoverWorkspacePackages } from './workspace.js';
|
|
200
261
|
import { discoverWorkspaceCrates } from './workspace.js';
|
|
201
262
|
import { validatePackageJson } from './workspace.js';
|
|
@@ -203,5 +264,8 @@ import { resolveWorkspaceDiscoveryIgnore } from './workspace.js';
|
|
|
203
264
|
import { filterManifestPathsByDiscoveryIgnore } from './workspace.js';
|
|
204
265
|
import { resolveContinueOnError } from './batch_opts.js';
|
|
205
266
|
import { resolveBatchMetadata } from './batch_opts.js';
|
|
206
|
-
export { discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson, resolveWorkspaceDiscoveryIgnore, filterManifestPathsByDiscoveryIgnore, resolveContinueOnError, resolveBatchMetadata };
|
|
267
|
+
export { discoverMavenModules, discoverGradleSubprojects, discoverGoWorkspaceModules, discoverUvWorkspaceMembers, discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson, resolveWorkspaceDiscoveryIgnore, filterManifestPathsByDiscoveryIgnore, resolveContinueOnError, resolveBatchMetadata };
|
|
207
268
|
export { getProjectLicense, findLicenseFilePath, identifyLicense, getLicenseDetails, licensesFromReport, normalizeLicensesResponse, runLicenseCheck, getCompatibility } from "./license/index.js";
|
|
269
|
+
export { generateReport, generateDeduplicationKey } from "./remediation_report.js";
|
|
270
|
+
export { loadConfig, mergeConfig, resolveConfig, CONFIG_FILENAMES } from "./config.js";
|
|
271
|
+
export { runRemediation, findManifests } from "./remediate.js";
|