@trustify-da/trustify-da-javascript-client 0.3.0-ea.61444d4 → 0.3.0-ea.62b88e5
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 +22 -8
- package/dist/package.json +6 -5
- package/dist/src/analysis.d.ts +40 -19
- package/dist/src/analysis.js +41 -5
- package/dist/src/cli.js +95 -17
- package/dist/src/config.d.ts +120 -0
- package/dist/src/config.js +260 -0
- 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 +23 -23
- package/dist/src/index.js +23 -39
- package/dist/src/license/index.d.ts +2 -2
- package/dist/src/license/index.js +9 -6
- package/dist/src/license/licenses_api.d.ts +2 -2
- package/dist/src/license/licenses_api.js +1 -1
- package/dist/src/package_version.d.ts +8 -0
- package/dist/src/package_version.js +31 -0
- package/dist/src/providers/base_java.d.ts +53 -0
- package/dist/src/providers/base_java.js +64 -17
- package/dist/src/providers/base_javascript.d.ts +54 -0
- package/dist/src/providers/base_javascript.js +106 -4
- package/dist/src/providers/golang_gomodules.js +6 -5
- package/dist/src/providers/java_gradle.d.ts +48 -0
- package/dist/src/providers/java_gradle.js +204 -20
- package/dist/src/providers/java_maven.d.ts +28 -8
- 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 +13 -0
- 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/processors/yarn_berry_processor.d.ts +5 -1
- package/dist/src/providers/processors/yarn_berry_processor.js +3 -2
- package/dist/src/providers/processors/yarn_classic_processor.d.ts +5 -1
- package/dist/src/providers/processors/yarn_classic_processor.js +8 -6
- package/dist/src/providers/python_uv.js +6 -0
- package/dist/src/providers/requirements_parser.js +1 -1
- package/dist/src/providers/rust_cargo.js +34 -52
- package/dist/src/remediate.d.ts +90 -4
- package/dist/src/remediate.js +153 -58
- package/dist/src/remediation.d.ts +54 -30
- package/dist/src/remediation.js +92 -53
- package/dist/src/remediation_report.d.ts +3 -20
- package/dist/src/remediation_report.js +37 -16
- package/dist/src/sbom.d.ts +11 -0
- package/dist/src/sbom.js +10 -0
- package/dist/src/tools.d.ts +9 -11
- package/dist/src/tools.js +34 -13
- package/dist/src/updaters/maven_updater.d.ts +40 -8
- package/dist/src/updaters/maven_updater.js +26 -3
- package/dist/src/updaters/toml_updater.d.ts +42 -13
- package/dist/src/updaters/toml_updater.js +36 -4
- package/dist/src/workspace.d.ts +2 -1
- package/dist/src/workspace.js +2 -1
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<h3>Usage</h3>
|
|
8
8
|
<p>
|
|
9
9
|
|
|
10
|
-
<strong>Prerequisites:</strong>
|
|
10
|
+
<strong>Prerequisites:</strong> Supply the Trustify Dependency Analytics backend URL through <code>options.TRUSTIFY_DA_BACKEND_URL</code> or the <code>TRUSTIFY_DA_BACKEND_URL</code> environment variable (see <a href="#customization">Customization</a>). Programmatic options take precedence over environment variables.
|
|
11
11
|
|
|
12
12
|
<ul>
|
|
13
13
|
<li>
|
|
@@ -275,6 +275,18 @@ $ trustify-da-javascript-client remediate /path/to/pom.xml --dry-run --group-by
|
|
|
275
275
|
<li><a href="https://www.rust-lang.org/">Rust</a> - <a href="https://doc.rust-lang.org/cargo/">Cargo</a></li>
|
|
276
276
|
</ul>
|
|
277
277
|
|
|
278
|
+
<h3>Dockerfile Analysis Prerequisites</h3>
|
|
279
|
+
<p>
|
|
280
|
+
OCI image and <code>Dockerfile</code>/<code>Containerfile</code> analysis require the following external tools to be installed and available on your <code>PATH</code>:
|
|
281
|
+
</p>
|
|
282
|
+
<ul>
|
|
283
|
+
<li><a href="https://github.com/anchore/syft">Syft</a> — generates the image SBOM.</li>
|
|
284
|
+
<li><a href="https://github.com/containers/skopeo">skopeo</a> — resolves the image digest and manifest from the registry.</li>
|
|
285
|
+
</ul>
|
|
286
|
+
<p>
|
|
287
|
+
<strong>Dockerfile analysis is not supported on Windows</strong>, because <code>skopeo</code> provides no native Windows binary.
|
|
288
|
+
</p>
|
|
289
|
+
|
|
278
290
|
<h3>License Detection</h3>
|
|
279
291
|
<p>
|
|
280
292
|
The client automatically detects your project's license with intelligent fallback:
|
|
@@ -466,13 +478,13 @@ All of the above examples are valid for marking a package to be ignored
|
|
|
466
478
|
|
|
467
479
|
<h3>Customization</h3>
|
|
468
480
|
<p>
|
|
469
|
-
There are
|
|
481
|
+
There are two approaches for customizing <em>Trustify Dependency Analytics JavaScript Client</em>. Whether you're using this API as a
|
|
470
482
|
<em>Global Module</em>, a <em>Remote Script</em>, or an <em>ESM Module</em>, you can use <em>Environment Variables</em>
|
|
471
|
-
for various customization.
|
|
483
|
+
for various customization. <em>ESM Module</em> users can also pass programmatic options.
|
|
472
484
|
|
|
473
|
-
<strong>
|
|
485
|
+
<strong>Precedence:</strong> Programmatic options override environment variables, which override built-in defaults. A backend URL supplied through either <code>options.TRUSTIFY_DA_BACKEND_URL</code> or <code>TRUSTIFY_DA_BACKEND_URL</code> is mandatory.
|
|
474
486
|
|
|
475
|
-
|
|
487
|
+
Example programmatic customization:
|
|
476
488
|
|
|
477
489
|
```javascript
|
|
478
490
|
import client from '@trustify-da/trustify-da-javascript-client'
|
|
@@ -515,7 +527,7 @@ let imageAnalysisHtml = await client.imageAnalysis(['docker.io/library/node:18']
|
|
|
515
527
|
// Specify architecture using ^^ notation (e.g., httpd:2.4.49^^amd64)
|
|
516
528
|
let imageAnalysisWithArch = await client.imageAnalysis(['httpd:2.4.49^^amd64'], false, options)
|
|
517
529
|
```
|
|
518
|
-
**
|
|
530
|
+
**_Programmatic options take precedence over environment variables._**
|
|
519
531
|
</p>
|
|
520
532
|
|
|
521
533
|
<h4>Monorepo / Workspace Support</h4>
|
|
@@ -551,12 +563,14 @@ const options = {
|
|
|
551
563
|
}
|
|
552
564
|
```
|
|
553
565
|
|
|
566
|
+
When both are supplied, <code>options.TRUSTIFY_DA_PROXY_URL</code> takes precedence over the environment variable.
|
|
567
|
+
|
|
554
568
|
The proxy URL should be in the format: `http://host:port` or `https://host:port`. The API will automatically use the appropriate protocol (HTTP or HTTPS) based on the proxy URL provided.
|
|
555
569
|
</p>
|
|
556
570
|
|
|
557
571
|
<h4>License resolution and dependency license compliance</h4>
|
|
558
572
|
<p>
|
|
559
|
-
The client can resolve the <strong>project license</strong> from the manifest (e.g. <code>package.json</code> <code>license</code>, <code>pom.xml</code> <code><licenses></code>, <code>Cargo.toml</code> <code>license</code>) and from a <code>LICENSE</code> or <code>LICENSE.md</code> file in the project, and report when they differ. For <strong>component analysis</strong>, you can optionally run a license check: the client fetches dependency licenses from the backend (by purl) and reports dependencies whose licenses are incompatible with the project license. See <a href="docs/license-resolution-and-compliance.md">License resolution and compliance</a> for design and behavior. To disable the check on component analysis, set <code>TRUSTIFY_DA_LICENSE_CHECK=false</code> or pass <code>licenseCheck: false</code> in the options.
|
|
573
|
+
The client can resolve the <strong>project license</strong> from the manifest (e.g. <code>package.json</code> <code>license</code>, <code>pom.xml</code> <code><licenses></code>, <code>Cargo.toml</code> <code>license</code>) and from a <code>LICENSE</code> or <code>LICENSE.md</code> file in the project, and report when they differ. For <strong>component analysis</strong>, you can optionally run a license check: the client fetches dependency licenses from the backend (by purl) and reports dependencies whose licenses are incompatible with the project license. See <a href="docs/license-resolution-and-compliance.md">License resolution and compliance</a> for design and behavior. To disable the check on component analysis, set <code>TRUSTIFY_DA_LICENSE_CHECK=false</code> or pass <code>licenseCheck: false</code> in the options. An explicit <code>licenseCheck</code> option takes precedence over the environment variable.
|
|
560
574
|
</p>
|
|
561
575
|
|
|
562
576
|
<h4>Customizing Executables</h4>
|
|
@@ -664,7 +678,7 @@ can cause a confusion for the user in the client consuming the API and leads to
|
|
|
664
678
|
|
|
665
679
|
##### Usage
|
|
666
680
|
|
|
667
|
-
To eliminate confusion and improve clarity as discussed above, the following setting was introduced - `MATCH_MANIFEST_VERSIONS`, in the form of environment variable
|
|
681
|
+
To eliminate confusion and improve clarity as discussed above, the following setting was introduced - `MATCH_MANIFEST_VERSIONS`, in the form of an environment variable or key in opts. When both are supplied, the value in opts takes precedence.
|
|
668
682
|
for two ecosystems:
|
|
669
683
|
- Golang - Go Modules
|
|
670
684
|
- Python - pip
|
package/dist/package.json
CHANGED
|
@@ -25,7 +25,9 @@
|
|
|
25
25
|
"provenance": true
|
|
26
26
|
},
|
|
27
27
|
"type": "module",
|
|
28
|
-
"bin":
|
|
28
|
+
"bin": {
|
|
29
|
+
"trustify-da": "dist/src/cli.js"
|
|
30
|
+
},
|
|
29
31
|
"main": "dist/src/index.js",
|
|
30
32
|
"module": "dist/src/index.js",
|
|
31
33
|
"types": "dist/src/index.d.ts",
|
|
@@ -47,8 +49,7 @@
|
|
|
47
49
|
"postcompile": "cp node_modules/tree-sitter-requirements/tree-sitter-requirements.wasm dist/src/providers/tree-sitter-requirements.wasm && cp node_modules/tree-sitter-gomod/tree-sitter-gomod.wasm dist/src/providers/tree-sitter-gomod.wasm && cp node_modules/tree-sitter-containerfile/tree-sitter-containerfile.wasm dist/src/providers/tree-sitter-containerfile.wasm"
|
|
48
50
|
},
|
|
49
51
|
"dependencies": {
|
|
50
|
-
"@
|
|
51
|
-
"@cyclonedx/cyclonedx-library": "^10.0.0",
|
|
52
|
+
"@yarnpkg/parsers": "^3.1.0",
|
|
52
53
|
"eslint-import-resolver-typescript": "^4.4.4",
|
|
53
54
|
"fast-glob": "^3.3.3",
|
|
54
55
|
"fast-xml-parser": "^5.3.4",
|
|
@@ -68,12 +69,12 @@
|
|
|
68
69
|
"yargs": "^18.0.0"
|
|
69
70
|
},
|
|
70
71
|
"devDependencies": {
|
|
71
|
-
"@
|
|
72
|
+
"@cyclonedx/cyclonedx-library": "^10.2.0",
|
|
72
73
|
"@eslint/js": "^10.0.0",
|
|
73
74
|
"@trustify-da/trustify-da-api-model": "^2.0.12",
|
|
74
75
|
"@types/node": "^25.9.1",
|
|
75
76
|
"@types/which": "^3.0.4",
|
|
76
|
-
"
|
|
77
|
+
"ajv": "^8.20.0",
|
|
77
78
|
"c8": "^11.0.0",
|
|
78
79
|
"chai": "^6.2.2",
|
|
79
80
|
"eslint": "^10.4.1",
|
package/dist/src/analysis.d.ts
CHANGED
|
@@ -15,41 +15,62 @@ export default _default;
|
|
|
15
15
|
* @param {string} manifest - path for the manifest
|
|
16
16
|
* @param {string} url - the backend url to send the request to
|
|
17
17
|
* @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
|
|
18
|
-
* @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
|
|
18
|
+
* @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
|
|
19
19
|
*/
|
|
20
|
-
declare function requestComponent(provider: any, manifest: string, url: string, opts?: any): Promise<
|
|
20
|
+
declare function requestComponent(provider: any, manifest: string, url: string, opts?: any): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport>;
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
* @param {import('./provider').Provider} provider
|
|
24
|
-
* @param {string} manifest
|
|
25
|
-
* @param {string} url
|
|
26
|
-
* @param {
|
|
27
|
-
* @param {import("index.js").Options} [opts={}]
|
|
28
|
-
* @returns {Promise<string
|
|
22
|
+
* @overload
|
|
23
|
+
* @param {import('./provider').Provider} provider
|
|
24
|
+
* @param {string} manifest
|
|
25
|
+
* @param {string} url
|
|
26
|
+
* @param {true} [html]
|
|
27
|
+
* @param {import("index.js").Options} [opts={}]
|
|
28
|
+
* @returns {Promise<string>}
|
|
29
29
|
*/
|
|
30
|
-
declare function requestStack(provider: any, manifest: string, url: string, html?:
|
|
30
|
+
declare function requestStack(provider: any, manifest: string, url: string, html?: true | undefined, opts?: any): Promise<string>;
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
33
|
-
* @param {
|
|
34
|
-
* @param {string}
|
|
35
|
-
* @param {
|
|
32
|
+
* @overload
|
|
33
|
+
* @param {import('./provider').Provider} provider
|
|
34
|
+
* @param {string} manifest
|
|
35
|
+
* @param {string} url
|
|
36
|
+
* @param {false} [html]
|
|
37
|
+
* @param {import("index.js").Options} [opts={}]
|
|
38
|
+
* @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
|
|
39
|
+
*/
|
|
40
|
+
declare function requestStack(provider: any, manifest: string, url: string, html?: false | undefined, opts?: any): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport>;
|
|
41
|
+
/**
|
|
42
|
+
* @overload
|
|
43
|
+
* @param {Object.<string, object>} sbomByPurl
|
|
44
|
+
* @param {string} url
|
|
45
|
+
* @param {true} [html=false]
|
|
46
|
+
* @param {import("index.js").Options} [opts={}]
|
|
47
|
+
* @returns {Promise<string>}
|
|
48
|
+
*/
|
|
49
|
+
declare function requestStackBatch(sbomByPurl: {
|
|
50
|
+
[x: string]: object;
|
|
51
|
+
}, url: string, html?: true | undefined, opts?: any): Promise<string>;
|
|
52
|
+
/**
|
|
53
|
+
* @overload
|
|
54
|
+
* @param {Object.<string, object>} sbomByPurl
|
|
55
|
+
* @param {string} url
|
|
56
|
+
* @param {false} [html=false]
|
|
36
57
|
* @param {import("index.js").Options} [opts={}]
|
|
37
|
-
* @returns {Promise<
|
|
58
|
+
* @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
|
|
38
59
|
*/
|
|
39
60
|
declare function requestStackBatch(sbomByPurl: {
|
|
40
61
|
[x: string]: object;
|
|
41
|
-
}, url: string, html?:
|
|
42
|
-
[x: string]:
|
|
62
|
+
}, url: string, html?: false | undefined, opts?: any): Promise<{
|
|
63
|
+
[x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
|
|
43
64
|
}>;
|
|
44
65
|
/**
|
|
45
66
|
*
|
|
46
67
|
* @param {Array<string>} imageRefs
|
|
47
68
|
* @param {string} url
|
|
48
69
|
* @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
|
|
49
|
-
* @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
|
|
70
|
+
* @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
|
|
50
71
|
*/
|
|
51
72
|
declare function requestImages(imageRefs: Array<string>, url: string, html?: boolean, opts?: any): Promise<string | {
|
|
52
|
-
[x: string]:
|
|
73
|
+
[x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
|
|
53
74
|
}>;
|
|
54
75
|
/**
|
|
55
76
|
*
|
package/dist/src/analysis.js
CHANGED
|
@@ -7,6 +7,24 @@ import { addProxyAgent, getCustom, getTokenHeaders, TRUSTIFY_DA_OPERATION_TYPE_H
|
|
|
7
7
|
/** Media type for CycloneDX JSON batch payloads (batch-analysis API). */
|
|
8
8
|
export const CYCLONEDX_JSON_MEDIA_TYPE = 'application/vnd.cyclonedx+json';
|
|
9
9
|
export default { requestComponent, requestStack, requestStackBatch, requestImages, validateToken, appendAnalysisQueryParams };
|
|
10
|
+
/**
|
|
11
|
+
* @overload
|
|
12
|
+
* @param {import('./provider').Provider} provider
|
|
13
|
+
* @param {string} manifest
|
|
14
|
+
* @param {string} url
|
|
15
|
+
* @param {true} [html]
|
|
16
|
+
* @param {import("index.js").Options} [opts={}]
|
|
17
|
+
* @returns {Promise<string>}
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* @overload
|
|
21
|
+
* @param {import('./provider').Provider} provider
|
|
22
|
+
* @param {string} manifest
|
|
23
|
+
* @param {string} url
|
|
24
|
+
* @param {false} [html]
|
|
25
|
+
* @param {import("index.js").Options} [opts={}]
|
|
26
|
+
* @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
|
|
27
|
+
*/
|
|
10
28
|
/**
|
|
11
29
|
* Send a stack analysis request and get the report as 'text/html' or 'application/json'.
|
|
12
30
|
* @param {import('./provider').Provider} provider - the provided data for constructing the request
|
|
@@ -14,7 +32,7 @@ export default { requestComponent, requestStack, requestStackBatch, requestImage
|
|
|
14
32
|
* @param {string} url - the backend url to send the request to
|
|
15
33
|
* @param {boolean} [html=false] - true will return 'text/html', false will return 'application/json'
|
|
16
34
|
* @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
|
|
17
|
-
* @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
|
|
35
|
+
* @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
|
|
18
36
|
*/
|
|
19
37
|
async function requestStack(provider, manifest, url, html = false, opts = {}) {
|
|
20
38
|
opts["source-manifest"] = Buffer.from(fs.readFileSync(manifest).toString()).toString('base64');
|
|
@@ -75,7 +93,7 @@ async function requestStack(provider, manifest, url, html = false, opts = {}) {
|
|
|
75
93
|
* @param {string} manifest - path for the manifest
|
|
76
94
|
* @param {string} url - the backend url to send the request to
|
|
77
95
|
* @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
|
|
78
|
-
* @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
|
|
96
|
+
* @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
|
|
79
97
|
*/
|
|
80
98
|
async function requestComponent(provider, manifest, url, opts = {}) {
|
|
81
99
|
opts["source-manifest"] = Buffer.from(fs.readFileSync(manifest).toString()).toString('base64');
|
|
@@ -113,7 +131,9 @@ async function requestComponent(provider, manifest, url, opts = {}) {
|
|
|
113
131
|
console.log(JSON.stringify(result, null, 4));
|
|
114
132
|
console.log("Ending time of sending component analysis request to Trustify DA backend server= " + new Date());
|
|
115
133
|
}
|
|
116
|
-
const licenseCheckEnabled =
|
|
134
|
+
const licenseCheckEnabled = typeof opts.licenseCheck === 'boolean'
|
|
135
|
+
? opts.licenseCheck
|
|
136
|
+
: getCustom('TRUSTIFY_DA_LICENSE_CHECK', 'true', opts) !== 'false';
|
|
117
137
|
if (licenseCheckEnabled) {
|
|
118
138
|
try {
|
|
119
139
|
result.licenseSummary = await runLicenseCheck(provided.content, manifest, url, opts, result);
|
|
@@ -128,13 +148,29 @@ async function requestComponent(provider, manifest, url, opts = {}) {
|
|
|
128
148
|
}
|
|
129
149
|
return Promise.resolve(result);
|
|
130
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* @overload
|
|
153
|
+
* @param {Object.<string, object>} sbomByPurl
|
|
154
|
+
* @param {string} url
|
|
155
|
+
* @param {true} [html=false]
|
|
156
|
+
* @param {import("index.js").Options} [opts={}]
|
|
157
|
+
* @returns {Promise<string>}
|
|
158
|
+
*/
|
|
159
|
+
/**
|
|
160
|
+
* @overload
|
|
161
|
+
* @param {Object.<string, object>} sbomByPurl
|
|
162
|
+
* @param {string} url
|
|
163
|
+
* @param {false} [html=false]
|
|
164
|
+
* @param {import("index.js").Options} [opts={}]
|
|
165
|
+
* @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
|
|
166
|
+
*/
|
|
131
167
|
/**
|
|
132
168
|
* Send a batch stack analysis request for multiple manifests (SBOMs keyed by purl).
|
|
133
169
|
* @param {Object.<string, object>} sbomByPurl - Map of root purl to CycloneDX SBOM object
|
|
134
170
|
* @param {string} url - the backend url
|
|
135
171
|
* @param {boolean} [html=false] - true returns HTML, false returns JSON
|
|
136
172
|
* @param {import("index.js").Options} [opts={}]
|
|
137
|
-
* @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
|
|
173
|
+
* @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
|
|
138
174
|
*/
|
|
139
175
|
async function requestStackBatch(sbomByPurl, url, html = false, opts = {}) {
|
|
140
176
|
const finalUrl = new URL(`${url}/api/v5/batch-analysis`);
|
|
@@ -177,7 +213,7 @@ async function requestStackBatch(sbomByPurl, url, html = false, opts = {}) {
|
|
|
177
213
|
* @param {Array<string>} imageRefs
|
|
178
214
|
* @param {string} url
|
|
179
215
|
* @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
|
|
180
|
-
* @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
|
|
216
|
+
* @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
|
|
181
217
|
*/
|
|
182
218
|
async function requestImages(imageRefs, url, html = false, opts = {}) {
|
|
183
219
|
const imageSboms = {};
|
package/dist/src/cli.js
CHANGED
|
@@ -3,9 +3,45 @@ import fs from 'node:fs';
|
|
|
3
3
|
import * as path from "path";
|
|
4
4
|
import yargs from 'yargs';
|
|
5
5
|
import { hideBin } from 'yargs/helpers';
|
|
6
|
+
import { resolveConfig } from './config.js';
|
|
6
7
|
import { getProjectLicense, getLicenseDetails } from './license/index.js';
|
|
7
8
|
import { runRemediation } from './remediate.js';
|
|
9
|
+
import { generateReport } from './remediation_report.js';
|
|
8
10
|
import client, { selectTrustifyDABackend, generateSbom } from './index.js';
|
|
11
|
+
/**
|
|
12
|
+
* Builds a yargs middleware that loads `.trustify-da.yml` (discovered by walking
|
|
13
|
+
* up from the command's target path, or cwd when no path is available) and fills `--providers`, `--sources`, and
|
|
14
|
+
* `--group-by` with config file values when they were not supplied on the CLI or
|
|
15
|
+
* via environment variables. Precedence: CLI flag > env var > config file > default.
|
|
16
|
+
* @param {string} [pathKey] - positional argument holding the target path; defaults to the current working directory
|
|
17
|
+
* @param {{ groupBy?: boolean }} [options={}] - set `groupBy` when the command exposes `--group-by`
|
|
18
|
+
* @returns {(args: object) => object} yargs middleware
|
|
19
|
+
*/
|
|
20
|
+
function configMiddleware(pathKey, options = {}) {
|
|
21
|
+
return args => {
|
|
22
|
+
const startPath = pathKey === undefined ? process.cwd() : args[pathKey];
|
|
23
|
+
const envVars = options.groupBy
|
|
24
|
+
? process.env
|
|
25
|
+
: Object.fromEntries(Object.entries(process.env).filter(([key]) => key !== 'TRUSTIFY_DA_GROUP_BY'));
|
|
26
|
+
const merged = resolveConfig(startPath, { backendUrl: args.backendUrl, providers: args.providers, sources: args.sources, groupBy: args['group-by'] }, envVars);
|
|
27
|
+
if (merged.backendUrlSource === 'file' && process.env.TRUSTIFY_DA_TOKEN) {
|
|
28
|
+
throw new Error('Refusing to send TRUSTIFY_DA_TOKEN to a backend selected by project configuration. Set --backend-url or TRUSTIFY_DA_BACKEND_URL to explicitly trust it.');
|
|
29
|
+
}
|
|
30
|
+
if (args.providers !== undefined || merged.providers.length) {
|
|
31
|
+
args.providers = merged.providers.join(',');
|
|
32
|
+
}
|
|
33
|
+
if (args.sources !== undefined || merged.sources.length) {
|
|
34
|
+
args.sources = merged.sources.join(',');
|
|
35
|
+
}
|
|
36
|
+
if (merged.backendUrl != null) {
|
|
37
|
+
args.backendUrl = merged.backendUrl;
|
|
38
|
+
}
|
|
39
|
+
if (options.groupBy) {
|
|
40
|
+
args['group-by'] = merged.groupBy;
|
|
41
|
+
}
|
|
42
|
+
return args;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
9
45
|
// command for component analysis take manifest type and content
|
|
10
46
|
const component = {
|
|
11
47
|
command: 'component </path/to/manifest>',
|
|
@@ -21,6 +57,10 @@ const component = {
|
|
|
21
57
|
type: 'string',
|
|
22
58
|
normalize: true,
|
|
23
59
|
},
|
|
60
|
+
backendUrl: {
|
|
61
|
+
desc: 'Trustify DA backend URL (env: TRUSTIFY_DA_BACKEND_URL)',
|
|
62
|
+
type: 'string',
|
|
63
|
+
},
|
|
24
64
|
providers: {
|
|
25
65
|
desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
|
|
26
66
|
type: 'string',
|
|
@@ -29,14 +69,17 @@ const component = {
|
|
|
29
69
|
desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
|
|
30
70
|
type: 'string',
|
|
31
71
|
}
|
|
32
|
-
}),
|
|
72
|
+
}).middleware(configMiddleware('/path/to/manifest')),
|
|
33
73
|
handler: async (args) => {
|
|
34
74
|
let manifestName = args['/path/to/manifest'];
|
|
35
75
|
const opts = args.workspaceDir ? { TRUSTIFY_DA_WORKSPACE_DIR: args.workspaceDir } : {};
|
|
36
|
-
if (args.
|
|
76
|
+
if (args.backendUrl !== undefined) {
|
|
77
|
+
opts.TRUSTIFY_DA_BACKEND_URL = args.backendUrl;
|
|
78
|
+
}
|
|
79
|
+
if (args.providers !== undefined) {
|
|
37
80
|
opts.TRUSTIFY_DA_PROVIDERS = args.providers;
|
|
38
81
|
}
|
|
39
|
-
if (args.sources) {
|
|
82
|
+
if (args.sources !== undefined) {
|
|
40
83
|
opts.TRUSTIFY_DA_SOURCES = args.sources;
|
|
41
84
|
}
|
|
42
85
|
let res = await client.componentAnalysis(manifestName, opts);
|
|
@@ -88,6 +131,10 @@ const image = {
|
|
|
88
131
|
type: 'boolean',
|
|
89
132
|
conflicts: 'html'
|
|
90
133
|
},
|
|
134
|
+
backendUrl: {
|
|
135
|
+
desc: 'Trustify DA backend URL (env: TRUSTIFY_DA_BACKEND_URL)',
|
|
136
|
+
type: 'string',
|
|
137
|
+
},
|
|
91
138
|
providers: {
|
|
92
139
|
desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
|
|
93
140
|
type: 'string',
|
|
@@ -96,7 +143,7 @@ const image = {
|
|
|
96
143
|
desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
|
|
97
144
|
type: 'string',
|
|
98
145
|
}
|
|
99
|
-
}),
|
|
146
|
+
}).middleware(configMiddleware()),
|
|
100
147
|
handler: async (args) => {
|
|
101
148
|
let imageRefs = args['image-refs'];
|
|
102
149
|
if (!Array.isArray(imageRefs)) {
|
|
@@ -105,10 +152,13 @@ const image = {
|
|
|
105
152
|
let html = args['html'];
|
|
106
153
|
let summary = args['summary'];
|
|
107
154
|
const opts = {};
|
|
108
|
-
if (args.
|
|
155
|
+
if (args.backendUrl !== undefined) {
|
|
156
|
+
opts.TRUSTIFY_DA_BACKEND_URL = args.backendUrl;
|
|
157
|
+
}
|
|
158
|
+
if (args.providers !== undefined) {
|
|
109
159
|
opts.TRUSTIFY_DA_PROVIDERS = args.providers;
|
|
110
160
|
}
|
|
111
|
-
if (args.sources) {
|
|
161
|
+
if (args.sources !== undefined) {
|
|
112
162
|
opts.TRUSTIFY_DA_SOURCES = args.sources;
|
|
113
163
|
}
|
|
114
164
|
let res = await client.imageAnalysis(imageRefs, html, opts);
|
|
@@ -163,6 +213,10 @@ const stack = {
|
|
|
163
213
|
type: 'string',
|
|
164
214
|
normalize: true,
|
|
165
215
|
},
|
|
216
|
+
backendUrl: {
|
|
217
|
+
desc: 'Trustify DA backend URL (env: TRUSTIFY_DA_BACKEND_URL)',
|
|
218
|
+
type: 'string',
|
|
219
|
+
},
|
|
166
220
|
providers: {
|
|
167
221
|
desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
|
|
168
222
|
type: 'string',
|
|
@@ -171,16 +225,19 @@ const stack = {
|
|
|
171
225
|
desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
|
|
172
226
|
type: 'string',
|
|
173
227
|
}
|
|
174
|
-
}),
|
|
228
|
+
}).middleware(configMiddleware('/path/to/manifest')),
|
|
175
229
|
handler: async (args) => {
|
|
176
230
|
let manifest = args['/path/to/manifest'];
|
|
177
231
|
let html = args['html'];
|
|
178
232
|
let summary = args['summary'];
|
|
179
233
|
const opts = args.workspaceDir ? { TRUSTIFY_DA_WORKSPACE_DIR: args.workspaceDir } : {};
|
|
180
|
-
if (args.
|
|
234
|
+
if (args.backendUrl !== undefined) {
|
|
235
|
+
opts.TRUSTIFY_DA_BACKEND_URL = args.backendUrl;
|
|
236
|
+
}
|
|
237
|
+
if (args.providers !== undefined) {
|
|
181
238
|
opts.TRUSTIFY_DA_PROVIDERS = args.providers;
|
|
182
239
|
}
|
|
183
|
-
if (args.sources) {
|
|
240
|
+
if (args.sources !== undefined) {
|
|
184
241
|
opts.TRUSTIFY_DA_SOURCES = args.sources;
|
|
185
242
|
}
|
|
186
243
|
let theProvidersSummary = new Map();
|
|
@@ -246,6 +303,10 @@ const stackBatch = {
|
|
|
246
303
|
type: 'boolean',
|
|
247
304
|
default: false,
|
|
248
305
|
},
|
|
306
|
+
backendUrl: {
|
|
307
|
+
desc: 'Trustify DA backend URL (env: TRUSTIFY_DA_BACKEND_URL)',
|
|
308
|
+
type: 'string',
|
|
309
|
+
},
|
|
249
310
|
providers: {
|
|
250
311
|
desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
|
|
251
312
|
type: 'string',
|
|
@@ -254,12 +315,15 @@ const stackBatch = {
|
|
|
254
315
|
desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
|
|
255
316
|
type: 'string',
|
|
256
317
|
}
|
|
257
|
-
}),
|
|
318
|
+
}).middleware(configMiddleware('/path/to/workspace-root')),
|
|
258
319
|
handler: async (args) => {
|
|
259
320
|
const workspaceRoot = args['/path/to/workspace-root'];
|
|
260
321
|
const html = args['html'];
|
|
261
322
|
const summary = args['summary'];
|
|
262
323
|
const opts = {};
|
|
324
|
+
if (args.backendUrl !== undefined) {
|
|
325
|
+
opts.TRUSTIFY_DA_BACKEND_URL = args.backendUrl;
|
|
326
|
+
}
|
|
263
327
|
if (args.concurrency != null) {
|
|
264
328
|
opts.batchConcurrency = args.concurrency;
|
|
265
329
|
}
|
|
@@ -273,10 +337,10 @@ const stackBatch = {
|
|
|
273
337
|
if (args.failFast) {
|
|
274
338
|
opts.continueOnError = false;
|
|
275
339
|
}
|
|
276
|
-
if (args.providers) {
|
|
340
|
+
if (args.providers !== undefined) {
|
|
277
341
|
opts.TRUSTIFY_DA_PROVIDERS = args.providers;
|
|
278
342
|
}
|
|
279
|
-
if (args.sources) {
|
|
343
|
+
if (args.sources !== undefined) {
|
|
280
344
|
opts.TRUSTIFY_DA_SOURCES = args.sources;
|
|
281
345
|
}
|
|
282
346
|
let res = await client.stackAnalysisBatch(workspaceRoot, html, opts);
|
|
@@ -455,19 +519,33 @@ const remediate = {
|
|
|
455
519
|
'group-by': {
|
|
456
520
|
type: 'string',
|
|
457
521
|
choices: ['dependency', 'bundle'],
|
|
458
|
-
default:
|
|
459
|
-
desc: 'Report grouping strategy',
|
|
522
|
+
desc: 'Report grouping strategy (default: dependency)',
|
|
460
523
|
},
|
|
461
|
-
|
|
524
|
+
backendUrl: {
|
|
525
|
+
desc: 'Trustify DA backend URL (env: TRUSTIFY_DA_BACKEND_URL)',
|
|
526
|
+
type: 'string',
|
|
527
|
+
},
|
|
528
|
+
}).middleware(configMiddleware('path', { groupBy: true })),
|
|
462
529
|
handler: async (args) => {
|
|
463
530
|
try {
|
|
464
531
|
const result = await runRemediation(args.path, {
|
|
465
532
|
dryRun: args['dry-run'],
|
|
466
533
|
providers: args.providers,
|
|
467
534
|
sources: args.sources,
|
|
468
|
-
|
|
535
|
+
backendUrl: args.backendUrl,
|
|
469
536
|
});
|
|
470
|
-
|
|
537
|
+
if (result.remediations.length === 0 && result.manifests.length === 0) {
|
|
538
|
+
console.log('No supported manifest files found.');
|
|
539
|
+
process.exit(result.exitCode);
|
|
540
|
+
}
|
|
541
|
+
if (!args['dry-run'] && result.appliedFiles.length > 0) {
|
|
542
|
+
console.log(`Updated ${result.appliedFiles.length} file(s):`);
|
|
543
|
+
for (const file of result.appliedFiles) {
|
|
544
|
+
console.log(` ${file}`);
|
|
545
|
+
}
|
|
546
|
+
console.log('');
|
|
547
|
+
}
|
|
548
|
+
console.log(generateReport(result.remediations, { groupBy: args['group-by'], dryRun: args['dry-run'] }));
|
|
471
549
|
process.exit(result.exitCode);
|
|
472
550
|
}
|
|
473
551
|
catch (err) {
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discovers and parses `.trustify-da.yml` by walking up from `startPath`.
|
|
3
|
+
* A missing config file is not an error — an empty object is returned so callers
|
|
4
|
+
* can fall back to defaults.
|
|
5
|
+
* @param {string} [startPath] - manifest file or directory to start the search from
|
|
6
|
+
* @returns {FileConfig} the parsed config object, or `{}` when no file is found
|
|
7
|
+
* @throws {Error} when the config file exists but contains malformed YAML
|
|
8
|
+
*/
|
|
9
|
+
export function loadConfig(startPath?: string): FileConfig;
|
|
10
|
+
/**
|
|
11
|
+
* Merges config sources with precedence: CLI flag > environment variable
|
|
12
|
+
* (`TRUSTIFY_DA_*`) > config file > hardcoded default. Providers and sources are
|
|
13
|
+
* normalized to arrays regardless of whether the source used a YAML array or a
|
|
14
|
+
* comma-separated string.
|
|
15
|
+
* @param {FileConfig} [fileConfig={}] - values parsed from `.trustify-da.yml`
|
|
16
|
+
* @param {{ providers?: string, sources?: string, groupBy?: string, backendUrl?: string }} [cliFlags={}]
|
|
17
|
+
* @param {{ [key: string]: string | undefined }} [envVars={}] - typically `process.env`
|
|
18
|
+
* @returns {ResolvedConfig} the resolved, typed config object
|
|
19
|
+
*/
|
|
20
|
+
export function mergeConfig(fileConfig?: FileConfig, cliFlags?: {
|
|
21
|
+
providers?: string;
|
|
22
|
+
sources?: string;
|
|
23
|
+
groupBy?: string;
|
|
24
|
+
backendUrl?: string;
|
|
25
|
+
}, envVars?: {
|
|
26
|
+
[key: string]: string | undefined;
|
|
27
|
+
}): ResolvedConfig;
|
|
28
|
+
/**
|
|
29
|
+
* Loads and merges the project configuration for a target path.
|
|
30
|
+
* @param {string} [startPath] - manifest file or directory to start discovery from
|
|
31
|
+
* @param {{ providers?: string, sources?: string, groupBy?: string, backendUrl?: string }} [cliFlags={}] - explicit options
|
|
32
|
+
* @param {{ [key: string]: string | undefined }} [envVars={}] - typically `process.env`
|
|
33
|
+
* @returns {ResolvedConfig} the resolved, typed config object
|
|
34
|
+
*/
|
|
35
|
+
export function resolveConfig(startPath?: string, cliFlags?: {
|
|
36
|
+
providers?: string;
|
|
37
|
+
sources?: string;
|
|
38
|
+
groupBy?: string;
|
|
39
|
+
backendUrl?: string;
|
|
40
|
+
}, envVars?: {
|
|
41
|
+
[key: string]: string | undefined;
|
|
42
|
+
}): ResolvedConfig;
|
|
43
|
+
/**
|
|
44
|
+
* @typedef {{
|
|
45
|
+
* 'group-by'?: 'dependency' | 'bundle',
|
|
46
|
+
* exclude?: string[],
|
|
47
|
+
* labels?: string[],
|
|
48
|
+
* 'branch-prefix'?: string,
|
|
49
|
+
* [key: string]: unknown
|
|
50
|
+
* }} RemediationConfig
|
|
51
|
+
*/
|
|
52
|
+
/** @typedef {{ critical?: number, high?: number, 'license-conflicts'?: number, [key: string]: unknown }} FailOnConfig */
|
|
53
|
+
/** @typedef {{ 'fail-on'?: FailOnConfig, [key: string]: unknown }} CheckConfig */
|
|
54
|
+
/** @typedef {{ format?: string, targets?: string[], [key: string]: unknown }} SbomConfig */
|
|
55
|
+
/**
|
|
56
|
+
* @typedef {{
|
|
57
|
+
* 'backend-url'?: string,
|
|
58
|
+
* providers?: string[] | string,
|
|
59
|
+
* sources?: string[] | string,
|
|
60
|
+
* remediation?: RemediationConfig,
|
|
61
|
+
* check?: CheckConfig,
|
|
62
|
+
* sbom?: SbomConfig,
|
|
63
|
+
* [key: string]: unknown
|
|
64
|
+
* }} FileConfig
|
|
65
|
+
*/
|
|
66
|
+
/**
|
|
67
|
+
* @typedef {{
|
|
68
|
+
* backendUrl: string | null,
|
|
69
|
+
* backendUrlSource: 'cli' | 'environment' | 'file' | 'default',
|
|
70
|
+
* providers: string[],
|
|
71
|
+
* sources: string[],
|
|
72
|
+
* groupBy: string,
|
|
73
|
+
* remediation: object,
|
|
74
|
+
* check: object,
|
|
75
|
+
* sbom: object
|
|
76
|
+
* }} ResolvedConfig
|
|
77
|
+
*/
|
|
78
|
+
/** Config file names discovered by walking up the directory tree, in precedence order. */
|
|
79
|
+
export const CONFIG_FILENAMES: string[];
|
|
80
|
+
export type RemediationConfig = {
|
|
81
|
+
"group-by"?: "dependency" | "bundle";
|
|
82
|
+
exclude?: string[];
|
|
83
|
+
labels?: string[];
|
|
84
|
+
"branch-prefix"?: string;
|
|
85
|
+
[key: string]: unknown;
|
|
86
|
+
};
|
|
87
|
+
export type FailOnConfig = {
|
|
88
|
+
critical?: number;
|
|
89
|
+
high?: number;
|
|
90
|
+
"license-conflicts"?: number;
|
|
91
|
+
[key: string]: unknown;
|
|
92
|
+
};
|
|
93
|
+
export type CheckConfig = {
|
|
94
|
+
"fail-on"?: FailOnConfig;
|
|
95
|
+
[key: string]: unknown;
|
|
96
|
+
};
|
|
97
|
+
export type SbomConfig = {
|
|
98
|
+
format?: string;
|
|
99
|
+
targets?: string[];
|
|
100
|
+
[key: string]: unknown;
|
|
101
|
+
};
|
|
102
|
+
export type FileConfig = {
|
|
103
|
+
"backend-url"?: string;
|
|
104
|
+
providers?: string[] | string;
|
|
105
|
+
sources?: string[] | string;
|
|
106
|
+
remediation?: RemediationConfig;
|
|
107
|
+
check?: CheckConfig;
|
|
108
|
+
sbom?: SbomConfig;
|
|
109
|
+
[key: string]: unknown;
|
|
110
|
+
};
|
|
111
|
+
export type ResolvedConfig = {
|
|
112
|
+
backendUrl: string | null;
|
|
113
|
+
backendUrlSource: "cli" | "environment" | "file" | "default";
|
|
114
|
+
providers: string[];
|
|
115
|
+
sources: string[];
|
|
116
|
+
groupBy: string;
|
|
117
|
+
remediation: object;
|
|
118
|
+
check: object;
|
|
119
|
+
sbom: object;
|
|
120
|
+
};
|