@trustify-da/trustify-da-javascript-client 0.3.0-ea.6645ba5 → 0.3.0-ea.68905d3
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 +189 -3
- package/dist/package.json +29 -25
- package/dist/src/analysis.d.ts +14 -7
- package/dist/src/analysis.js +29 -13
- package/dist/src/cli.js +117 -2
- package/dist/src/cyclone_dx_sbom.d.ts +13 -0
- package/dist/src/cyclone_dx_sbom.js +39 -1
- package/dist/src/index.d.ts +15 -10
- package/dist/src/index.js +13 -25
- 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 +2 -2
- package/dist/src/provider.js +3 -1
- package/dist/src/providers/base_java.d.ts +55 -2
- package/dist/src/providers/base_java.js +64 -17
- package/dist/src/providers/base_javascript.d.ts +63 -9
- package/dist/src/providers/base_javascript.js +105 -3
- package/dist/src/providers/base_pyproject.d.ts +5 -5
- 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 +1 -1
- package/dist/src/providers/golang_gomodules.js +49 -7
- package/dist/src/providers/java_gradle.d.ts +48 -0
- package/dist/src/providers/java_gradle.js +196 -18
- 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 +29 -9
- package/dist/src/providers/java_maven.js +104 -10
- 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/manifest.js +6 -3
- 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 +4 -1
- package/dist/src/providers/python_poetry.d.ts +6 -5
- package/dist/src/providers/python_poetry.js +23 -8
- package/dist/src/providers/python_uv.d.ts +1 -1
- package/dist/src/providers/requirements_parser.js +1 -1
- package/dist/src/providers/rust_cargo.d.ts +1 -1
- package/dist/src/providers/rust_cargo.js +70 -29
- package/dist/src/providers/tree-sitter-containerfile.wasm +0 -0
- package/dist/src/remediate.d.ts +117 -0
- package/dist/src/remediate.js +249 -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 +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/package.json +30 -26
package/dist/src/cli.js
CHANGED
|
@@ -4,6 +4,8 @@ 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';
|
|
8
|
+
import { generateReport } from './remediation_report.js';
|
|
7
9
|
import client, { selectTrustifyDABackend, generateSbom } from './index.js';
|
|
8
10
|
// command for component analysis take manifest type and content
|
|
9
11
|
const component = {
|
|
@@ -19,11 +21,25 @@ const component = {
|
|
|
19
21
|
desc: 'Workspace root directory (for monorepos; lock file is expected here)',
|
|
20
22
|
type: 'string',
|
|
21
23
|
normalize: true,
|
|
24
|
+
},
|
|
25
|
+
providers: {
|
|
26
|
+
desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
|
|
27
|
+
type: 'string',
|
|
28
|
+
},
|
|
29
|
+
sources: {
|
|
30
|
+
desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
|
|
31
|
+
type: 'string',
|
|
22
32
|
}
|
|
23
33
|
}),
|
|
24
34
|
handler: async (args) => {
|
|
25
35
|
let manifestName = args['/path/to/manifest'];
|
|
26
36
|
const opts = args.workspaceDir ? { TRUSTIFY_DA_WORKSPACE_DIR: args.workspaceDir } : {};
|
|
37
|
+
if (args.providers) {
|
|
38
|
+
opts.TRUSTIFY_DA_PROVIDERS = args.providers;
|
|
39
|
+
}
|
|
40
|
+
if (args.sources) {
|
|
41
|
+
opts.TRUSTIFY_DA_SOURCES = args.sources;
|
|
42
|
+
}
|
|
27
43
|
let res = await client.componentAnalysis(manifestName, opts);
|
|
28
44
|
console.log(JSON.stringify(res, null, 2));
|
|
29
45
|
}
|
|
@@ -72,6 +88,14 @@ const image = {
|
|
|
72
88
|
desc: 'For JSON report, get only the \'summary\'',
|
|
73
89
|
type: 'boolean',
|
|
74
90
|
conflicts: 'html'
|
|
91
|
+
},
|
|
92
|
+
providers: {
|
|
93
|
+
desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
|
|
94
|
+
type: 'string',
|
|
95
|
+
},
|
|
96
|
+
sources: {
|
|
97
|
+
desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
|
|
98
|
+
type: 'string',
|
|
75
99
|
}
|
|
76
100
|
}),
|
|
77
101
|
handler: async (args) => {
|
|
@@ -81,7 +105,14 @@ const image = {
|
|
|
81
105
|
}
|
|
82
106
|
let html = args['html'];
|
|
83
107
|
let summary = args['summary'];
|
|
84
|
-
|
|
108
|
+
const opts = {};
|
|
109
|
+
if (args.providers) {
|
|
110
|
+
opts.TRUSTIFY_DA_PROVIDERS = args.providers;
|
|
111
|
+
}
|
|
112
|
+
if (args.sources) {
|
|
113
|
+
opts.TRUSTIFY_DA_SOURCES = args.sources;
|
|
114
|
+
}
|
|
115
|
+
let res = await client.imageAnalysis(imageRefs, html, opts);
|
|
85
116
|
if (summary && !html) {
|
|
86
117
|
let summaries = {};
|
|
87
118
|
for (let [imageRef, report] of Object.entries(res)) {
|
|
@@ -132,6 +163,14 @@ const stack = {
|
|
|
132
163
|
desc: 'Workspace root directory (for monorepos; lock file is expected here)',
|
|
133
164
|
type: 'string',
|
|
134
165
|
normalize: true,
|
|
166
|
+
},
|
|
167
|
+
providers: {
|
|
168
|
+
desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
|
|
169
|
+
type: 'string',
|
|
170
|
+
},
|
|
171
|
+
sources: {
|
|
172
|
+
desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
|
|
173
|
+
type: 'string',
|
|
135
174
|
}
|
|
136
175
|
}),
|
|
137
176
|
handler: async (args) => {
|
|
@@ -139,6 +178,12 @@ const stack = {
|
|
|
139
178
|
let html = args['html'];
|
|
140
179
|
let summary = args['summary'];
|
|
141
180
|
const opts = args.workspaceDir ? { TRUSTIFY_DA_WORKSPACE_DIR: args.workspaceDir } : {};
|
|
181
|
+
if (args.providers) {
|
|
182
|
+
opts.TRUSTIFY_DA_PROVIDERS = args.providers;
|
|
183
|
+
}
|
|
184
|
+
if (args.sources) {
|
|
185
|
+
opts.TRUSTIFY_DA_SOURCES = args.sources;
|
|
186
|
+
}
|
|
142
187
|
let theProvidersSummary = new Map();
|
|
143
188
|
let theProvidersObject = {};
|
|
144
189
|
let res = await client.stackAnalysis(manifest, html, opts);
|
|
@@ -201,6 +246,14 @@ const stackBatch = {
|
|
|
201
246
|
desc: 'Stop on first invalid package.json or SBOM error (env: TRUSTIFY_DA_CONTINUE_ON_ERROR=false)',
|
|
202
247
|
type: 'boolean',
|
|
203
248
|
default: false,
|
|
249
|
+
},
|
|
250
|
+
providers: {
|
|
251
|
+
desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
|
|
252
|
+
type: 'string',
|
|
253
|
+
},
|
|
254
|
+
sources: {
|
|
255
|
+
desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
|
|
256
|
+
type: 'string',
|
|
204
257
|
}
|
|
205
258
|
}),
|
|
206
259
|
handler: async (args) => {
|
|
@@ -221,6 +274,12 @@ const stackBatch = {
|
|
|
221
274
|
if (args.failFast) {
|
|
222
275
|
opts.continueOnError = false;
|
|
223
276
|
}
|
|
277
|
+
if (args.providers) {
|
|
278
|
+
opts.TRUSTIFY_DA_PROVIDERS = args.providers;
|
|
279
|
+
}
|
|
280
|
+
if (args.sources) {
|
|
281
|
+
opts.TRUSTIFY_DA_SOURCES = args.sources;
|
|
282
|
+
}
|
|
224
283
|
let res = await client.stackAnalysisBatch(workspaceRoot, html, opts);
|
|
225
284
|
const batchAnalysis = res && typeof res === 'object' && res != null && 'analysis' in res ? res.analysis : res;
|
|
226
285
|
if (summary && !html && typeof batchAnalysis === 'object') {
|
|
@@ -373,9 +432,64 @@ const sbom = {
|
|
|
373
432
|
}
|
|
374
433
|
}
|
|
375
434
|
};
|
|
435
|
+
const remediate = {
|
|
436
|
+
command: 'remediate <path>',
|
|
437
|
+
desc: 'Scan and apply vulnerability remediations to manifest files',
|
|
438
|
+
builder: yargs => yargs.positional('path', {
|
|
439
|
+
desc: 'Path to manifest file or directory',
|
|
440
|
+
type: 'string',
|
|
441
|
+
normalize: true,
|
|
442
|
+
}).options({
|
|
443
|
+
'dry-run': {
|
|
444
|
+
alias: 'd',
|
|
445
|
+
type: 'boolean',
|
|
446
|
+
desc: 'Preview changes without modifying files',
|
|
447
|
+
},
|
|
448
|
+
providers: {
|
|
449
|
+
desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
|
|
450
|
+
type: 'string',
|
|
451
|
+
},
|
|
452
|
+
sources: {
|
|
453
|
+
desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
|
|
454
|
+
type: 'string',
|
|
455
|
+
},
|
|
456
|
+
'group-by': {
|
|
457
|
+
type: 'string',
|
|
458
|
+
choices: ['dependency', 'bundle'],
|
|
459
|
+
default: 'dependency',
|
|
460
|
+
desc: 'Report grouping strategy',
|
|
461
|
+
},
|
|
462
|
+
}),
|
|
463
|
+
handler: async (args) => {
|
|
464
|
+
try {
|
|
465
|
+
const result = await runRemediation(args.path, {
|
|
466
|
+
dryRun: args['dry-run'],
|
|
467
|
+
providers: args.providers,
|
|
468
|
+
sources: args.sources,
|
|
469
|
+
});
|
|
470
|
+
if (result.remediations.length === 0 && result.manifests.length === 0) {
|
|
471
|
+
console.log('No supported manifest files found.');
|
|
472
|
+
process.exit(result.exitCode);
|
|
473
|
+
}
|
|
474
|
+
if (!args['dry-run'] && result.appliedFiles.length > 0) {
|
|
475
|
+
console.log(`Updated ${result.appliedFiles.length} file(s):`);
|
|
476
|
+
for (const file of result.appliedFiles) {
|
|
477
|
+
console.log(` ${file}`);
|
|
478
|
+
}
|
|
479
|
+
console.log('');
|
|
480
|
+
}
|
|
481
|
+
console.log(generateReport(result.remediations, { groupBy: args['group-by'], dryRun: args['dry-run'] }));
|
|
482
|
+
process.exit(result.exitCode);
|
|
483
|
+
}
|
|
484
|
+
catch (err) {
|
|
485
|
+
console.error(err.message);
|
|
486
|
+
process.exit(1);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
};
|
|
376
490
|
// parse and invoke the command
|
|
377
491
|
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}`)
|
|
492
|
+
.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
493
|
.command(stack)
|
|
380
494
|
.command(stackBatch)
|
|
381
495
|
.command(component)
|
|
@@ -383,6 +497,7 @@ yargs(hideBin(process.argv))
|
|
|
383
497
|
.command(validateToken)
|
|
384
498
|
.command(license)
|
|
385
499
|
.command(sbom)
|
|
500
|
+
.command(remediate)
|
|
386
501
|
.scriptName('')
|
|
387
502
|
.version(false)
|
|
388
503
|
.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}
|
|
@@ -135,6 +142,30 @@ export default class CycloneDxSbom {
|
|
|
135
142
|
}
|
|
136
143
|
return this;
|
|
137
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
|
+
}
|
|
138
169
|
/** @param {{}} opts - various options, settings and configuration of application.
|
|
139
170
|
* @return String CycloneDx Sbom json object in a string format
|
|
140
171
|
*/
|
|
@@ -144,11 +175,18 @@ export default class CycloneDxSbom {
|
|
|
144
175
|
const rootPurl = this.rootComponent?.purl;
|
|
145
176
|
this.sbomObject = {
|
|
146
177
|
"bomFormat": "CycloneDX",
|
|
147
|
-
"specVersion": "1.
|
|
178
|
+
"specVersion": "1.6",
|
|
148
179
|
"version": 1,
|
|
149
180
|
"metadata": {
|
|
150
181
|
"timestamp": new Date(),
|
|
151
182
|
"component": this.rootComponent,
|
|
183
|
+
"tools": {
|
|
184
|
+
"components": [{
|
|
185
|
+
"type": "application",
|
|
186
|
+
"name": "trustify-da-javascript-client",
|
|
187
|
+
"version": PACKAGE_VERSION
|
|
188
|
+
}]
|
|
189
|
+
},
|
|
152
190
|
"properties": new Array()
|
|
153
191
|
},
|
|
154
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,5 @@ 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";
|
|
271
|
+
export { runRemediation, findManifests } from "./remediate.js";
|
package/dist/src/index.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
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';
|
|
20
|
+
export { runRemediation, findManifests } from './remediate.js';
|
|
19
21
|
export default { componentAnalysis, stackAnalysis, stackAnalysisBatch, imageAnalysis, validateToken, generateSbom };
|
|
20
22
|
export { discoverMavenModules, discoverGradleSubprojects, discoverGoWorkspaceModules, discoverUvWorkspaceMembers, discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson, resolveWorkspaceDiscoveryIgnore, filterManifestPathsByDiscoveryIgnore, resolveContinueOnError, resolveBatchMetadata, };
|
|
21
23
|
/**
|
|
@@ -39,7 +41,9 @@ export { discoverMavenModules, discoverGradleSubprojects, discoverGoWorkspaceMod
|
|
|
39
41
|
* TRUSTIFY_DA_PYTHON_PATH?: string | undefined,
|
|
40
42
|
* TRUSTIFY_DA_PYTHON_VIRTUAL_ENV?: string | undefined,
|
|
41
43
|
* TRUSTIFY_DA_PYTHON3_PATH?: string | undefined,
|
|
42
|
-
*
|
|
44
|
+
* TRUSTIFY_DA_PROVIDERS?: string | undefined,
|
|
45
|
+
* TRUSTIFY_DA_RECOMMEND?: string | undefined,
|
|
46
|
+
* TRUSTIFY_DA_SOURCES?: string | undefined,
|
|
43
47
|
* TRUSTIFY_DA_SKOPEO_CONFIG_PATH?: string | undefined,
|
|
44
48
|
* TRUSTIFY_DA_SKOPEO_PATH?: string | undefined,
|
|
45
49
|
* TRUSTIFY_DA_SYFT_CONFIG_PATH?: string | undefined,
|
|
@@ -91,23 +95,7 @@ function logOptionsAndEnvironmentsVariables(alongsideText, valueToBePrinted) {
|
|
|
91
95
|
* @private
|
|
92
96
|
*/
|
|
93
97
|
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);
|
|
98
|
+
logOptionsAndEnvironmentsVariables("trustify-da-javascript-client analysis started, version: ", getPackageVersion());
|
|
111
99
|
}
|
|
112
100
|
/**
|
|
113
101
|
* This function is used to determine the Trustify DA backend URL.
|
|
@@ -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
|
@@ -34,9 +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, arg1:
|
|
40
|
+
readLicenseFromManifest: (arg0: string, arg1: Object | undefined) => string | null;
|
|
41
41
|
packageManagerName: () => string;
|
|
42
42
|
};
|
package/dist/src/provider.js
CHANGED
|
@@ -7,6 +7,7 @@ import Javascript_bun from './providers/javascript_bun.js';
|
|
|
7
7
|
import Javascript_npm from './providers/javascript_npm.js';
|
|
8
8
|
import Javascript_pnpm from './providers/javascript_pnpm.js';
|
|
9
9
|
import Javascript_yarn from './providers/javascript_yarn.js';
|
|
10
|
+
import dockerfileProvider from './providers/oci_dockerfile.js';
|
|
10
11
|
import pythonPipProvider from './providers/python_pip.js';
|
|
11
12
|
import Python_pip_pyproject from './providers/python_pip_pyproject.js';
|
|
12
13
|
import Python_poetry from './providers/python_poetry.js';
|
|
@@ -31,7 +32,8 @@ export const availableProviders = [
|
|
|
31
32
|
new Python_poetry(),
|
|
32
33
|
new Python_uv(),
|
|
33
34
|
new Python_pip_pyproject(),
|
|
34
|
-
rustCargoProvider
|
|
35
|
+
rustCargoProvider,
|
|
36
|
+
dockerfileProvider
|
|
35
37
|
];
|
|
36
38
|
/**
|
|
37
39
|
* Match a provider by manifest type only (no lock file check). Used for license reading.
|