@trustify-da/trustify-da-javascript-client 0.3.0-ea.bbe2094 → 0.3.0-ea.bdaadb2
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 +177 -3
- package/dist/package.json +29 -22
- package/dist/src/analysis.d.ts +14 -7
- package/dist/src/analysis.js +29 -13
- package/dist/src/cli.js +106 -2
- package/dist/src/cyclone_dx_sbom.d.ts +13 -0
- package/dist/src/cyclone_dx_sbom.js +50 -4
- package/dist/src/index.d.ts +14 -10
- package/dist/src/index.js +15 -26
- package/dist/src/license/index.d.ts +3 -3
- package/dist/src/license/index.js +4 -1
- package/dist/src/license/license_utils.js +4 -1
- package/dist/src/license/licenses_api.d.ts +2 -2
- 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 +4 -2
- package/dist/src/providers/base_java.d.ts +67 -3
- package/dist/src/providers/base_java.js +80 -23
- package/dist/src/providers/base_javascript.d.ts +68 -9
- package/dist/src/providers/base_javascript.js +112 -3
- package/dist/src/providers/base_pyproject.d.ts +10 -5
- package/dist/src/providers/base_pyproject.js +7 -0
- 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 +2 -1
- package/dist/src/providers/golang_gomodules.js +44 -3
- package/dist/src/providers/java_gradle.d.ts +48 -0
- package/dist/src/providers/java_gradle.js +194 -15
- 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 +30 -5
- package/dist/src/providers/java_maven.js +168 -7
- package/dist/src/providers/javascript_bun.d.ts +12 -0
- package/dist/src/providers/javascript_bun.js +42 -1
- package/dist/src/providers/javascript_npm.d.ts +19 -1
- package/dist/src/providers/javascript_npm.js +40 -1
- package/dist/src/providers/javascript_pnpm.d.ts +13 -0
- package/dist/src/providers/javascript_pnpm.js +47 -1
- package/dist/src/providers/javascript_yarn.d.ts +12 -0
- package/dist/src/providers/javascript_yarn.js +75 -2
- 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 +3 -2
- 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.js +61 -4
- package/dist/src/providers/python_pip.d.ts +1 -0
- package/dist/src/providers/python_pip.js +1 -1
- package/dist/src/providers/python_poetry.d.ts +31 -8
- package/dist/src/providers/python_poetry.js +62 -14
- package/dist/src/providers/python_uv.d.ts +5 -14
- package/dist/src/providers/python_uv.js +44 -2
- 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 +21 -0
- package/dist/src/remediate.js +149 -0
- package/dist/src/remediation.d.ts +57 -0
- package/dist/src/remediation.js +436 -0
- package/dist/src/remediation_report.d.ts +40 -0
- package/dist/src/remediation_report.js +159 -0
- package/dist/src/sbom.d.ts +11 -0
- package/dist/src/sbom.js +10 -0
- package/dist/src/tools.d.ts +2 -2
- package/dist/src/tools.js +24 -1
- package/dist/src/updaters/maven_updater.d.ts +26 -0
- package/dist/src/updaters/maven_updater.js +322 -0
- package/dist/src/updaters/toml_updater.d.ts +33 -0
- package/dist/src/updaters/toml_updater.js +244 -0
- package/package.json +30 -23
package/dist/src/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import * as path from "path";
|
|
|
4
4
|
import yargs from 'yargs';
|
|
5
5
|
import { hideBin } from 'yargs/helpers';
|
|
6
6
|
import { getProjectLicense, getLicenseDetails } from './license/index.js';
|
|
7
|
+
import { runRemediation } from './remediate.js';
|
|
7
8
|
import client, { selectTrustifyDABackend, generateSbom } from './index.js';
|
|
8
9
|
// command for component analysis take manifest type and content
|
|
9
10
|
const component = {
|
|
@@ -19,11 +20,25 @@ const component = {
|
|
|
19
20
|
desc: 'Workspace root directory (for monorepos; lock file is expected here)',
|
|
20
21
|
type: 'string',
|
|
21
22
|
normalize: true,
|
|
23
|
+
},
|
|
24
|
+
providers: {
|
|
25
|
+
desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
|
|
26
|
+
type: 'string',
|
|
27
|
+
},
|
|
28
|
+
sources: {
|
|
29
|
+
desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
|
|
30
|
+
type: 'string',
|
|
22
31
|
}
|
|
23
32
|
}),
|
|
24
33
|
handler: async (args) => {
|
|
25
34
|
let manifestName = args['/path/to/manifest'];
|
|
26
35
|
const opts = args.workspaceDir ? { TRUSTIFY_DA_WORKSPACE_DIR: args.workspaceDir } : {};
|
|
36
|
+
if (args.providers) {
|
|
37
|
+
opts.TRUSTIFY_DA_PROVIDERS = args.providers;
|
|
38
|
+
}
|
|
39
|
+
if (args.sources) {
|
|
40
|
+
opts.TRUSTIFY_DA_SOURCES = args.sources;
|
|
41
|
+
}
|
|
27
42
|
let res = await client.componentAnalysis(manifestName, opts);
|
|
28
43
|
console.log(JSON.stringify(res, null, 2));
|
|
29
44
|
}
|
|
@@ -72,6 +87,14 @@ const image = {
|
|
|
72
87
|
desc: 'For JSON report, get only the \'summary\'',
|
|
73
88
|
type: 'boolean',
|
|
74
89
|
conflicts: 'html'
|
|
90
|
+
},
|
|
91
|
+
providers: {
|
|
92
|
+
desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
|
|
93
|
+
type: 'string',
|
|
94
|
+
},
|
|
95
|
+
sources: {
|
|
96
|
+
desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
|
|
97
|
+
type: 'string',
|
|
75
98
|
}
|
|
76
99
|
}),
|
|
77
100
|
handler: async (args) => {
|
|
@@ -81,7 +104,14 @@ const image = {
|
|
|
81
104
|
}
|
|
82
105
|
let html = args['html'];
|
|
83
106
|
let summary = args['summary'];
|
|
84
|
-
|
|
107
|
+
const opts = {};
|
|
108
|
+
if (args.providers) {
|
|
109
|
+
opts.TRUSTIFY_DA_PROVIDERS = args.providers;
|
|
110
|
+
}
|
|
111
|
+
if (args.sources) {
|
|
112
|
+
opts.TRUSTIFY_DA_SOURCES = args.sources;
|
|
113
|
+
}
|
|
114
|
+
let res = await client.imageAnalysis(imageRefs, html, opts);
|
|
85
115
|
if (summary && !html) {
|
|
86
116
|
let summaries = {};
|
|
87
117
|
for (let [imageRef, report] of Object.entries(res)) {
|
|
@@ -132,6 +162,14 @@ const stack = {
|
|
|
132
162
|
desc: 'Workspace root directory (for monorepos; lock file is expected here)',
|
|
133
163
|
type: 'string',
|
|
134
164
|
normalize: true,
|
|
165
|
+
},
|
|
166
|
+
providers: {
|
|
167
|
+
desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
|
|
168
|
+
type: 'string',
|
|
169
|
+
},
|
|
170
|
+
sources: {
|
|
171
|
+
desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
|
|
172
|
+
type: 'string',
|
|
135
173
|
}
|
|
136
174
|
}),
|
|
137
175
|
handler: async (args) => {
|
|
@@ -139,6 +177,12 @@ const stack = {
|
|
|
139
177
|
let html = args['html'];
|
|
140
178
|
let summary = args['summary'];
|
|
141
179
|
const opts = args.workspaceDir ? { TRUSTIFY_DA_WORKSPACE_DIR: args.workspaceDir } : {};
|
|
180
|
+
if (args.providers) {
|
|
181
|
+
opts.TRUSTIFY_DA_PROVIDERS = args.providers;
|
|
182
|
+
}
|
|
183
|
+
if (args.sources) {
|
|
184
|
+
opts.TRUSTIFY_DA_SOURCES = args.sources;
|
|
185
|
+
}
|
|
142
186
|
let theProvidersSummary = new Map();
|
|
143
187
|
let theProvidersObject = {};
|
|
144
188
|
let res = await client.stackAnalysis(manifest, html, opts);
|
|
@@ -201,6 +245,14 @@ const stackBatch = {
|
|
|
201
245
|
desc: 'Stop on first invalid package.json or SBOM error (env: TRUSTIFY_DA_CONTINUE_ON_ERROR=false)',
|
|
202
246
|
type: 'boolean',
|
|
203
247
|
default: false,
|
|
248
|
+
},
|
|
249
|
+
providers: {
|
|
250
|
+
desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
|
|
251
|
+
type: 'string',
|
|
252
|
+
},
|
|
253
|
+
sources: {
|
|
254
|
+
desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
|
|
255
|
+
type: 'string',
|
|
204
256
|
}
|
|
205
257
|
}),
|
|
206
258
|
handler: async (args) => {
|
|
@@ -221,6 +273,12 @@ const stackBatch = {
|
|
|
221
273
|
if (args.failFast) {
|
|
222
274
|
opts.continueOnError = false;
|
|
223
275
|
}
|
|
276
|
+
if (args.providers) {
|
|
277
|
+
opts.TRUSTIFY_DA_PROVIDERS = args.providers;
|
|
278
|
+
}
|
|
279
|
+
if (args.sources) {
|
|
280
|
+
opts.TRUSTIFY_DA_SOURCES = args.sources;
|
|
281
|
+
}
|
|
224
282
|
let res = await client.stackAnalysisBatch(workspaceRoot, html, opts);
|
|
225
283
|
const batchAnalysis = res && typeof res === 'object' && res != null && 'analysis' in res ? res.analysis : res;
|
|
226
284
|
if (summary && !html && typeof batchAnalysis === 'object') {
|
|
@@ -373,9 +431,54 @@ const sbom = {
|
|
|
373
431
|
}
|
|
374
432
|
}
|
|
375
433
|
};
|
|
434
|
+
const remediate = {
|
|
435
|
+
command: 'remediate <path>',
|
|
436
|
+
desc: 'Scan and apply vulnerability remediations to manifest files',
|
|
437
|
+
builder: yargs => yargs.positional('path', {
|
|
438
|
+
desc: 'Path to manifest file or directory',
|
|
439
|
+
type: 'string',
|
|
440
|
+
normalize: true,
|
|
441
|
+
}).options({
|
|
442
|
+
'dry-run': {
|
|
443
|
+
alias: 'd',
|
|
444
|
+
type: 'boolean',
|
|
445
|
+
desc: 'Preview changes without modifying files',
|
|
446
|
+
},
|
|
447
|
+
providers: {
|
|
448
|
+
desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
|
|
449
|
+
type: 'string',
|
|
450
|
+
},
|
|
451
|
+
sources: {
|
|
452
|
+
desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
|
|
453
|
+
type: 'string',
|
|
454
|
+
},
|
|
455
|
+
'group-by': {
|
|
456
|
+
type: 'string',
|
|
457
|
+
choices: ['dependency', 'bundle'],
|
|
458
|
+
default: 'dependency',
|
|
459
|
+
desc: 'Report grouping strategy',
|
|
460
|
+
},
|
|
461
|
+
}),
|
|
462
|
+
handler: async (args) => {
|
|
463
|
+
try {
|
|
464
|
+
const result = await runRemediation(args.path, {
|
|
465
|
+
dryRun: args['dry-run'],
|
|
466
|
+
providers: args.providers,
|
|
467
|
+
sources: args.sources,
|
|
468
|
+
groupBy: args['group-by'],
|
|
469
|
+
});
|
|
470
|
+
console.log(result.output);
|
|
471
|
+
process.exit(result.exitCode);
|
|
472
|
+
}
|
|
473
|
+
catch (err) {
|
|
474
|
+
console.error(err.message);
|
|
475
|
+
process.exit(1);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
};
|
|
376
479
|
// parse and invoke the command
|
|
377
480
|
yargs(hideBin(process.argv))
|
|
378
|
-
.usage(`Usage: ${process.argv[0].includes("node") ? path.parse(process.argv[1]).base : path.parse(process.argv[0]).base} {component|stack|stack-batch|image|validate-token|license|sbom}`)
|
|
481
|
+
.usage(`Usage: ${process.argv[0].includes("node") ? path.parse(process.argv[1]).base : path.parse(process.argv[0]).base} {component|stack|stack-batch|image|validate-token|license|sbom|remediate}`)
|
|
379
482
|
.command(stack)
|
|
380
483
|
.command(stackBatch)
|
|
381
484
|
.command(component)
|
|
@@ -383,6 +486,7 @@ yargs(hideBin(process.argv))
|
|
|
383
486
|
.command(validateToken)
|
|
384
487
|
.command(license)
|
|
385
488
|
.command(sbom)
|
|
489
|
+
.command(remediate)
|
|
386
490
|
.scriptName('')
|
|
387
491
|
.version(false)
|
|
388
492
|
.demandCommand(1)
|
|
@@ -26,6 +26,19 @@ export default class CycloneDxSbom {
|
|
|
26
26
|
alg: string;
|
|
27
27
|
content: string;
|
|
28
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;
|
|
29
42
|
/** @param {{}} opts - various options, settings and configuration of application.
|
|
30
43
|
* @return String CycloneDx Sbom json object in a string format
|
|
31
44
|
*/
|
|
@@ -1,5 +1,12 @@
|
|
|
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}
|
|
@@ -41,12 +48,20 @@ function getComponent(component, type, scope, licenses, hashes) {
|
|
|
41
48
|
// Add licenses if provided (CycloneDX format). Callers must provide valid SPDX identifiers.
|
|
42
49
|
if (licenses) {
|
|
43
50
|
const licenseArray = Array.isArray(licenses) ? licenses : [licenses];
|
|
44
|
-
|
|
51
|
+
const validLicenses = licenseArray
|
|
52
|
+
.map(lic => {
|
|
45
53
|
if (typeof lic === 'string') {
|
|
46
54
|
return { license: { id: lic } };
|
|
47
55
|
}
|
|
48
|
-
|
|
49
|
-
|
|
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
|
+
}
|
|
50
65
|
}
|
|
51
66
|
// Add hashes if provided (CycloneDX 1.4 format).
|
|
52
67
|
if (hashes && hashes.length > 0) {
|
|
@@ -127,6 +142,30 @@ export default class CycloneDxSbom {
|
|
|
127
142
|
}
|
|
128
143
|
return this;
|
|
129
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
|
+
}
|
|
130
169
|
/** @param {{}} opts - various options, settings and configuration of application.
|
|
131
170
|
* @return String CycloneDx Sbom json object in a string format
|
|
132
171
|
*/
|
|
@@ -136,11 +175,18 @@ export default class CycloneDxSbom {
|
|
|
136
175
|
const rootPurl = this.rootComponent?.purl;
|
|
137
176
|
this.sbomObject = {
|
|
138
177
|
"bomFormat": "CycloneDX",
|
|
139
|
-
"specVersion": "1.
|
|
178
|
+
"specVersion": "1.6",
|
|
140
179
|
"version": 1,
|
|
141
180
|
"metadata": {
|
|
142
181
|
"timestamp": new Date(),
|
|
143
182
|
"component": this.rootComponent,
|
|
183
|
+
"tools": {
|
|
184
|
+
"components": [{
|
|
185
|
+
"type": "application",
|
|
186
|
+
"name": "trustify-da-javascript-client",
|
|
187
|
+
"version": PACKAGE_VERSION
|
|
188
|
+
}]
|
|
189
|
+
},
|
|
144
190
|
"properties": new Array()
|
|
145
191
|
},
|
|
146
192
|
"components": this.components.filter(c => c.purl !== rootPurl),
|
package/dist/src/index.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export function selectTrustifyDABackend(opts?: {
|
|
|
24
24
|
export function generateSbom(manifestPath: string, opts?: Options): Promise<object>;
|
|
25
25
|
export { parseImageRef } from "./oci_image/utils.js";
|
|
26
26
|
export { ImageRef } from "./oci_image/images.js";
|
|
27
|
+
export { extractRemediations } from "./remediation.js";
|
|
27
28
|
declare namespace _default {
|
|
28
29
|
export { componentAnalysis };
|
|
29
30
|
export { stackAnalysis };
|
|
@@ -53,7 +54,9 @@ export type Options = {
|
|
|
53
54
|
TRUSTIFY_DA_PYTHON_PATH?: string | undefined;
|
|
54
55
|
TRUSTIFY_DA_PYTHON_VIRTUAL_ENV?: string | undefined;
|
|
55
56
|
TRUSTIFY_DA_PYTHON3_PATH?: string | undefined;
|
|
56
|
-
|
|
57
|
+
TRUSTIFY_DA_PROVIDERS?: string | undefined;
|
|
58
|
+
TRUSTIFY_DA_RECOMMEND?: string | undefined;
|
|
59
|
+
TRUSTIFY_DA_SOURCES?: string | undefined;
|
|
57
60
|
TRUSTIFY_DA_SKOPEO_CONFIG_PATH?: string | undefined;
|
|
58
61
|
TRUSTIFY_DA_SKOPEO_PATH?: string | undefined;
|
|
59
62
|
TRUSTIFY_DA_SYFT_CONFIG_PATH?: string | undefined;
|
|
@@ -106,7 +109,7 @@ export type SbomResult = {
|
|
|
106
109
|
* @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
|
|
107
110
|
* @throws {Error} if no matching provider, failed to get create content, or backend request failed
|
|
108
111
|
*/
|
|
109
|
-
declare function componentAnalysis(manifest: string, opts?: Options): Promise<
|
|
112
|
+
declare function componentAnalysis(manifest: string, opts?: Options): Promise<any>;
|
|
110
113
|
/**
|
|
111
114
|
* @overload
|
|
112
115
|
* @param {string} manifest
|
|
@@ -124,7 +127,7 @@ declare function stackAnalysis(manifest: string, html: true, opts?: Options | un
|
|
|
124
127
|
* @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
|
|
125
128
|
* @throws {Error}
|
|
126
129
|
*/
|
|
127
|
-
declare function stackAnalysis(manifest: string, html: false, opts?: Options | undefined): Promise<
|
|
130
|
+
declare function stackAnalysis(manifest: string, html: false, opts?: Options | undefined): Promise<any>;
|
|
128
131
|
/**
|
|
129
132
|
* Get stack analysis report for a manifest file.
|
|
130
133
|
* @overload
|
|
@@ -135,7 +138,7 @@ declare function stackAnalysis(manifest: string, html: false, opts?: Options | u
|
|
|
135
138
|
* @throws {Error} if manifest inaccessible, no matching provider, failed to get create content,
|
|
136
139
|
* or backend request failed
|
|
137
140
|
*/
|
|
138
|
-
declare function stackAnalysis(manifest: string, html?: boolean | undefined, opts?: Options | undefined): Promise<string |
|
|
141
|
+
declare function stackAnalysis(manifest: string, html?: boolean | undefined, opts?: Options | undefined): Promise<string | any>;
|
|
139
142
|
/**
|
|
140
143
|
* @overload
|
|
141
144
|
* @param {string} workspaceRoot
|
|
@@ -173,7 +176,7 @@ declare function stackAnalysisBatch(workspaceRoot: string, html: false, opts: Op
|
|
|
173
176
|
batchMetadata: true;
|
|
174
177
|
}): Promise<{
|
|
175
178
|
analysis: {
|
|
176
|
-
[x: string]:
|
|
179
|
+
[x: string]: any;
|
|
177
180
|
};
|
|
178
181
|
metadata: BatchAnalysisMetadata;
|
|
179
182
|
}>;
|
|
@@ -188,7 +191,7 @@ declare function stackAnalysisBatch(workspaceRoot: string, html: false, opts: Op
|
|
|
188
191
|
declare function stackAnalysisBatch(workspaceRoot: string, html: false, opts?: (Options & {
|
|
189
192
|
batchMetadata?: false;
|
|
190
193
|
}) | undefined): Promise<{
|
|
191
|
-
[x: string]:
|
|
194
|
+
[x: string]: any;
|
|
192
195
|
}>;
|
|
193
196
|
/**
|
|
194
197
|
* Get stack analysis for all workspace packages/crates (batch).
|
|
@@ -204,10 +207,10 @@ declare function stackAnalysisBatch(workspaceRoot: string, html: false, opts?: (
|
|
|
204
207
|
* @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.
|
|
205
208
|
*/
|
|
206
209
|
declare function stackAnalysisBatch(workspaceRoot: string, html?: boolean | undefined, opts?: Options | undefined): Promise<string | {
|
|
207
|
-
[x: string]:
|
|
210
|
+
[x: string]: any;
|
|
208
211
|
} | {
|
|
209
212
|
analysis: string | {
|
|
210
|
-
[x: string]:
|
|
213
|
+
[x: string]: any;
|
|
211
214
|
};
|
|
212
215
|
metadata: BatchAnalysisMetadata;
|
|
213
216
|
}>;
|
|
@@ -229,7 +232,7 @@ declare function imageAnalysis(imageRefs: Array<string>, html: true, opts?: Opti
|
|
|
229
232
|
* @throws {Error}
|
|
230
233
|
*/
|
|
231
234
|
declare function imageAnalysis(imageRefs: Array<string>, html: false, opts?: Options | undefined): Promise<{
|
|
232
|
-
[x: string]:
|
|
235
|
+
[x: string]: any;
|
|
233
236
|
}>;
|
|
234
237
|
/**
|
|
235
238
|
* Get image analysis report for a set of OCI image references.
|
|
@@ -242,7 +245,7 @@ declare function imageAnalysis(imageRefs: Array<string>, html: false, opts?: Opt
|
|
|
242
245
|
* or backend request failed
|
|
243
246
|
*/
|
|
244
247
|
declare function imageAnalysis(imageRefs: Array<string>, html?: boolean | undefined, opts?: Options | undefined): Promise<string | {
|
|
245
|
-
[x: string]:
|
|
248
|
+
[x: string]: any;
|
|
246
249
|
}>;
|
|
247
250
|
/**
|
|
248
251
|
* Validates the Exhort token.
|
|
@@ -264,3 +267,4 @@ import { resolveContinueOnError } from './batch_opts.js';
|
|
|
264
267
|
import { resolveBatchMetadata } from './batch_opts.js';
|
|
265
268
|
export { discoverMavenModules, discoverGradleSubprojects, discoverGoWorkspaceModules, discoverUvWorkspaceMembers, discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson, resolveWorkspaceDiscoveryIgnore, filterManifestPathsByDiscoveryIgnore, resolveContinueOnError, resolveBatchMetadata };
|
|
266
269
|
export { getProjectLicense, findLicenseFilePath, identifyLicense, getLicenseDetails, licensesFromReport, normalizeLicensesResponse, runLicenseCheck, getCompatibility } from "./license/index.js";
|
|
270
|
+
export { generateReport, generateDeduplicationKey } from "./remediation_report.js";
|
package/dist/src/index.js
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
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';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
7
|
+
import { getPackageVersion } from './package_version.js';
|
|
8
|
+
import { availableProviders, match } from './provider.js';
|
|
11
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
12
|
import { discoverUvWorkspaceMembers } from './providers/python_uv.js';
|
|
13
|
+
import { getCustom } from "./tools.js";
|
|
13
14
|
import { discoverWorkspaceCrates, discoverWorkspacePackages, filterManifestPathsByDiscoveryIgnore, resolveWorkspaceDiscoveryIgnore, validatePackageJson, } from './workspace.js';
|
|
14
|
-
import.meta.dirname;
|
|
15
|
-
import * as url from 'url';
|
|
16
15
|
export { parseImageRef } from "./oci_image/utils.js";
|
|
17
16
|
export { ImageRef } from "./oci_image/images.js";
|
|
18
17
|
export { getProjectLicense, findLicenseFilePath, identifyLicense, getLicenseDetails, licensesFromReport, normalizeLicensesResponse, runLicenseCheck, getCompatibility } from "./license/index.js";
|
|
18
|
+
export { extractRemediations } from "./remediation.js";
|
|
19
|
+
export { generateReport, generateDeduplicationKey } from './remediation_report.js';
|
|
19
20
|
export default { componentAnalysis, stackAnalysis, stackAnalysisBatch, imageAnalysis, validateToken, generateSbom };
|
|
20
21
|
export { discoverMavenModules, discoverGradleSubprojects, discoverGoWorkspaceModules, discoverUvWorkspaceMembers, discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson, resolveWorkspaceDiscoveryIgnore, filterManifestPathsByDiscoveryIgnore, resolveContinueOnError, resolveBatchMetadata, };
|
|
21
22
|
/**
|
|
@@ -39,7 +40,9 @@ export { discoverMavenModules, discoverGradleSubprojects, discoverGoWorkspaceMod
|
|
|
39
40
|
* TRUSTIFY_DA_PYTHON_PATH?: string | undefined,
|
|
40
41
|
* TRUSTIFY_DA_PYTHON_VIRTUAL_ENV?: string | undefined,
|
|
41
42
|
* TRUSTIFY_DA_PYTHON3_PATH?: string | undefined,
|
|
42
|
-
*
|
|
43
|
+
* TRUSTIFY_DA_PROVIDERS?: string | undefined,
|
|
44
|
+
* TRUSTIFY_DA_RECOMMEND?: string | undefined,
|
|
45
|
+
* TRUSTIFY_DA_SOURCES?: string | undefined,
|
|
43
46
|
* TRUSTIFY_DA_SKOPEO_CONFIG_PATH?: string | undefined,
|
|
44
47
|
* TRUSTIFY_DA_SKOPEO_PATH?: string | undefined,
|
|
45
48
|
* TRUSTIFY_DA_SYFT_CONFIG_PATH?: string | undefined,
|
|
@@ -91,23 +94,7 @@ function logOptionsAndEnvironmentsVariables(alongsideText, valueToBePrinted) {
|
|
|
91
94
|
* @private
|
|
92
95
|
*/
|
|
93
96
|
function readAndPrintVersionFromPackageJson() {
|
|
94
|
-
|
|
95
|
-
// new ESM way in nodeJS ( since node version 22 ) to bring module directory.
|
|
96
|
-
dirName = import.meta.dirname;
|
|
97
|
-
// old ESM way in nodeJS ( before node versions 22.00 to bring module directory)
|
|
98
|
-
if (!dirName) {
|
|
99
|
-
dirName = url.fileURLToPath(new URL('.', import.meta.url));
|
|
100
|
-
}
|
|
101
|
-
try {
|
|
102
|
-
if (__dirname) {
|
|
103
|
-
dirName = __dirname;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
catch (e) {
|
|
107
|
-
console.log("__dirname is not defined, continue with fileUrlPath");
|
|
108
|
-
}
|
|
109
|
-
let packageJson = JSON.parse(fs.readFileSync(path.join(dirName, "..", "package.json")).toString());
|
|
110
|
-
logOptionsAndEnvironmentsVariables("trustify-da-javascript-client analysis started, version: ", packageJson.version);
|
|
97
|
+
logOptionsAndEnvironmentsVariables("trustify-da-javascript-client analysis started, version: ", getPackageVersion());
|
|
111
98
|
}
|
|
112
99
|
/**
|
|
113
100
|
* This function is used to determine the Trustify DA backend URL.
|
|
@@ -175,7 +162,9 @@ async function componentAnalysis(manifest, opts = {}) {
|
|
|
175
162
|
fs.accessSync(manifest, fs.constants.R_OK);
|
|
176
163
|
opts["manifest-type"] = path.basename(manifest);
|
|
177
164
|
let provider = match(manifest, availableProviders, opts); // throws error if no matching provider
|
|
178
|
-
|
|
165
|
+
const result = await analysis.requestComponent(provider, manifest, theUrl, opts); // throws error request sending failed
|
|
166
|
+
result.packageManager = provider.packageManagerName();
|
|
167
|
+
return result;
|
|
179
168
|
}
|
|
180
169
|
/**
|
|
181
170
|
* @overload
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
* @param {import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').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?:
|
|
12
|
+
export function runLicenseCheck(sbomContent: string, manifestPath: string, url: string, opts?: import("../index.js").Options, analysisResult?: any): Promise<{
|
|
13
13
|
projectLicense: {
|
|
14
|
-
manifest:
|
|
15
|
-
file:
|
|
14
|
+
manifest: Object | null;
|
|
15
|
+
file: Object | null;
|
|
16
16
|
mismatch: boolean;
|
|
17
17
|
};
|
|
18
18
|
incompatibleDependencies: Array<{
|
|
@@ -85,11 +85,14 @@ export async function runLicenseCheck(sbomContent, manifestPath, url, opts = {},
|
|
|
85
85
|
continue;
|
|
86
86
|
const status = getCompatibility(projectCategory, entry.category);
|
|
87
87
|
if (status === 'incompatible') {
|
|
88
|
+
const reason = entry.category?.toUpperCase() === 'UNKNOWN'
|
|
89
|
+
? 'License not recognized as a standard SPDX identifier. Manual review recommended to verify compatibility.'
|
|
90
|
+
: 'Dependency license(s) are incompatible with the project license.';
|
|
88
91
|
incompatibleDependencies.push({
|
|
89
92
|
purl,
|
|
90
93
|
licenses: entry.licenses,
|
|
91
94
|
category: entry.category,
|
|
92
|
-
reason
|
|
95
|
+
reason
|
|
93
96
|
});
|
|
94
97
|
}
|
|
95
98
|
}
|
|
@@ -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'
|
|
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,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @param {import('../index.js').Options} [opts={}] - options (proxy, token, TRUSTIFY_DA_BACKEND_URL, etc.)
|
|
7
7
|
* @returns {Promise<Object|null>} License details or null if not found
|
|
8
8
|
*/
|
|
9
|
-
export function getLicenseDetails(spdxId: string, opts?: import("../index.js").Options): Promise<
|
|
9
|
+
export function getLicenseDetails(spdxId: string, opts?: import("../index.js").Options): Promise<Object | null>;
|
|
10
10
|
/**
|
|
11
11
|
* Normalize the LicensesResponse shape (array of LicenseProviderResult) into a map of purl -> license info.
|
|
12
12
|
* Each provider result has { status, summary, packages } where packages is { [purl]: { concluded, evidence } }.
|
|
@@ -28,7 +28,7 @@ export function normalizeLicensesResponse(data: unknown, purls?: string[]): Map<
|
|
|
28
28
|
* @param {string[]} [purls] - optional list of purls to restrict to
|
|
29
29
|
* @returns {Map<string, { licenses: string[], category?: string }>}
|
|
30
30
|
*/
|
|
31
|
-
export function licensesFromReport(analysisReport:
|
|
31
|
+
export function licensesFromReport(analysisReport: any, purls?: string[]): Map<string, {
|
|
32
32
|
licenses: string[];
|
|
33
33
|
category?: string;
|
|
34
34
|
}>;
|
|
@@ -78,7 +78,7 @@ export class ImageRef {
|
|
|
78
78
|
* @param {string} [platform]
|
|
79
79
|
* @param {import("index.js").Options} [opts={}]
|
|
80
80
|
*/
|
|
81
|
-
constructor(image: string, platform?: string, opts?:
|
|
81
|
+
constructor(image: string, platform?: string, opts?: any);
|
|
82
82
|
/** @type {Image} */
|
|
83
83
|
image: Image;
|
|
84
84
|
/** @type {Platform} */
|
|
@@ -242,8 +242,22 @@ export class ImageRef {
|
|
|
242
242
|
getPackageURL() {
|
|
243
243
|
/** @type {Object.<string, string>} */
|
|
244
244
|
const qualifiers = {};
|
|
245
|
-
|
|
245
|
+
let repositoryUrl = this.image.getNameWithoutTag();
|
|
246
246
|
const simpleName = this.image.getSimpleName();
|
|
247
|
+
// Normalize Docker Hub image references so all forms produce the same PURL:
|
|
248
|
+
// node → docker.io/node
|
|
249
|
+
// docker.io/library/node → docker.io/node
|
|
250
|
+
if (repositoryUrl != null) {
|
|
251
|
+
const lower = repositoryUrl.toLowerCase();
|
|
252
|
+
if (lower === simpleName.toLowerCase()) {
|
|
253
|
+
// Bare name (e.g. "node") — default to docker.io
|
|
254
|
+
repositoryUrl = `docker.io/${simpleName}`;
|
|
255
|
+
}
|
|
256
|
+
else if (lower.startsWith('docker.io/library/')) {
|
|
257
|
+
// Official image with library/ prefix — strip it
|
|
258
|
+
repositoryUrl = `docker.io/${lower.substring('docker.io/library/'.length)}`;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
247
261
|
if (repositoryUrl != null && repositoryUrl.toLowerCase() !== simpleName.toLowerCase()) {
|
|
248
262
|
qualifiers[ImageRef.REPOSITORY_QUALIFIER] = repositoryUrl.toLowerCase();
|
|
249
263
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @param {import("../index.js").Options} [opts={}] - optional various options to pass along the application
|
|
5
5
|
* @returns {{}}
|
|
6
6
|
*/
|
|
7
|
-
export function generateImageSBOM(imageRef:
|
|
7
|
+
export function generateImageSBOM(imageRef: any, opts?: import("../index.js").Options): {};
|
|
8
8
|
/**
|
|
9
9
|
*
|
|
10
10
|
* @param {string} image
|
|
@@ -25,7 +25,7 @@ export function getImagePlatform(opts?: import("../index.js").Options): Platform
|
|
|
25
25
|
* @returns {Object.<string, string>} - The image digests
|
|
26
26
|
* @throws {Error} If the image info is invalid
|
|
27
27
|
*/
|
|
28
|
-
export function getImageDigests(imageRef:
|
|
28
|
+
export function getImageDigests(imageRef: any, opts?: import("../index.js").Options): {
|
|
29
29
|
[x: string]: string;
|
|
30
30
|
};
|
|
31
31
|
export type SyftImageSource = {
|
|
@@ -34,9 +34,9 @@ export type SyftImageSource = {
|
|
|
34
34
|
getVariant: () => string;
|
|
35
35
|
};
|
|
36
36
|
export type SyftImageSourceType = {
|
|
37
|
-
getOs: (arg0:
|
|
38
|
-
getArch: (arg0:
|
|
39
|
-
getVariant: (arg0:
|
|
37
|
+
getOs: (arg0: Object) => string;
|
|
38
|
+
getArch: (arg0: Object) => string;
|
|
39
|
+
getVariant: (arg0: Object) => string;
|
|
40
40
|
};
|
|
41
41
|
import { ImageRef } from './images.js';
|
|
42
42
|
import { Platform } from './platform.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads this package's version from package.json, resolving the path relative
|
|
3
|
+
* to this module. Works under ESM (node >= 22 import.meta.dirname, older via
|
|
4
|
+
* import.meta.url) and CommonJS (__dirname). Returns undefined if the file
|
|
5
|
+
* cannot be read, so callers never fail on a missing version.
|
|
6
|
+
* @return {string|undefined} the package version, or undefined
|
|
7
|
+
*/
|
|
8
|
+
export function getPackageVersion(): string | undefined;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import * as url from "url";
|
|
4
|
+
/**
|
|
5
|
+
* Reads this package's version from package.json, resolving the path relative
|
|
6
|
+
* to this module. Works under ESM (node >= 22 import.meta.dirname, older via
|
|
7
|
+
* import.meta.url) and CommonJS (__dirname). Returns undefined if the file
|
|
8
|
+
* cannot be read, so callers never fail on a missing version.
|
|
9
|
+
* @return {string|undefined} the package version, or undefined
|
|
10
|
+
*/
|
|
11
|
+
export function getPackageVersion() {
|
|
12
|
+
try {
|
|
13
|
+
let dirName = import.meta.dirname;
|
|
14
|
+
if (!dirName) {
|
|
15
|
+
dirName = url.fileURLToPath(new URL('.', import.meta.url));
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
if (__dirname) {
|
|
19
|
+
dirName = __dirname;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// __dirname not defined under ESM, continue with fileURLToPath
|
|
24
|
+
}
|
|
25
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(dirName, "..", "package.json")).toString());
|
|
26
|
+
return packageJson.version;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
package/dist/src/provider.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export function match(manifest: string, providers: [Provider], opts?: {
|
|
|
21
21
|
TRUSTIFY_DA_WORKSPACE_DIR?: string;
|
|
22
22
|
}): Provider;
|
|
23
23
|
/** @typedef {{ecosystem: string, contentType: string, content: string}} Provided */
|
|
24
|
-
/** @typedef {{isSupported: function(string): boolean, validateLockFile: function(string, Object): void, provideComponent: function(string, {}): Provided | Promise<Provided>, provideStack: function(string, {}): Provided | Promise<Provided>, readLicenseFromManifest: function(string): string | null}} Provider */
|
|
24
|
+
/** @typedef {{isSupported: function(string): boolean, validateLockFile: function(string, Object): void, provideComponent: function(string, {}): Provided | Promise<Provided>, provideStack: function(string, {}): Provided | Promise<Provided>, readLicenseFromManifest: function(string, Object=): string | null, packageManagerName: function(): string}} Provider */
|
|
25
25
|
/**
|
|
26
26
|
* MUST include all providers here.
|
|
27
27
|
* @type {[Provider]}
|
|
@@ -34,8 +34,9 @@ export type Provided = {
|
|
|
34
34
|
};
|
|
35
35
|
export type Provider = {
|
|
36
36
|
isSupported: (arg0: string) => boolean;
|
|
37
|
-
validateLockFile: (arg0: string, arg1:
|
|
37
|
+
validateLockFile: (arg0: string, arg1: Object) => void;
|
|
38
38
|
provideComponent: (arg0: string, arg1: {}) => Provided | Promise<Provided>;
|
|
39
39
|
provideStack: (arg0: string, arg1: {}) => Provided | Promise<Provided>;
|
|
40
|
-
readLicenseFromManifest: (arg0: string) => string | null;
|
|
40
|
+
readLicenseFromManifest: (arg0: string, arg1: Object | undefined) => string | null;
|
|
41
|
+
packageManagerName: () => string;
|
|
41
42
|
};
|