@trustify-da/trustify-da-javascript-client 0.3.0-ea.e12bc82 → 0.3.0-ea.f2c4df7
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 +43 -1
- package/dist/package.json +14 -9
- package/dist/src/analysis.d.ts +5 -5
- package/dist/src/analysis.js +21 -76
- package/dist/src/cli.js +72 -6
- package/dist/src/cyclone_dx_sbom.d.ts +3 -2
- package/dist/src/cyclone_dx_sbom.js +16 -4
- package/dist/src/index.d.ts +65 -11
- package/dist/src/index.js +5 -3
- package/dist/src/license/index.d.ts +28 -0
- package/dist/src/license/index.js +100 -0
- package/dist/src/license/license_utils.d.ts +40 -0
- package/dist/src/license/license_utils.js +134 -0
- package/dist/src/license/licenses_api.d.ts +34 -0
- package/dist/src/license/licenses_api.js +91 -0
- package/dist/src/license/project_license.d.ts +28 -0
- package/dist/src/license/project_license.js +73 -0
- package/dist/src/oci_image/images.d.ts +4 -5
- package/dist/src/oci_image/utils.d.ts +4 -4
- package/dist/src/provider.d.ts +12 -3
- package/dist/src/provider.js +16 -1
- package/dist/src/providers/base_java.d.ts +3 -5
- package/dist/src/providers/base_javascript.d.ts +10 -4
- package/dist/src/providers/base_javascript.js +30 -4
- package/dist/src/providers/golang_gomodules.d.ts +11 -4
- package/dist/src/providers/golang_gomodules.js +13 -4
- package/dist/src/providers/java_gradle.d.ts +9 -3
- package/dist/src/providers/java_gradle.js +12 -2
- 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 +12 -5
- package/dist/src/providers/java_maven.js +33 -5
- package/dist/src/providers/python_controller.d.ts +5 -2
- package/dist/src/providers/python_controller.js +56 -58
- package/dist/src/providers/python_pip.d.ts +11 -4
- package/dist/src/providers/python_pip.js +46 -53
- package/dist/src/providers/requirements_parser.d.ts +6 -0
- package/dist/src/providers/requirements_parser.js +23 -0
- package/dist/src/sbom.d.ts +3 -1
- package/dist/src/sbom.js +3 -2
- package/dist/src/tools.d.ts +22 -6
- package/dist/src/tools.js +56 -1
- package/package.json +15 -10
package/README.md
CHANGED
|
@@ -43,6 +43,21 @@ let imageAnalysisWithArch = await client.imageAnalysis(['httpd:2.4.49^^amd64'])
|
|
|
43
43
|
```
|
|
44
44
|
</li>
|
|
45
45
|
</ul>
|
|
46
|
+
|
|
47
|
+
<h3>License Detection</h3>
|
|
48
|
+
<p>
|
|
49
|
+
The client automatically detects your project's license with intelligent fallback:
|
|
50
|
+
</p>
|
|
51
|
+
<ul>
|
|
52
|
+
<li><strong>Manifest-first:</strong> For ecosystems with license support (Maven, JavaScript), reads from manifest file (<code>pom.xml</code>, <code>package.json</code>)</li>
|
|
53
|
+
<li><strong>LICENSE file fallback:</strong> If no license in manifest, or for ecosystems without license support (Gradle, Go, Python), automatically reads from <code>LICENSE</code>, <code>LICENSE.md</code>, or <code>LICENSE.txt</code></li>
|
|
54
|
+
<li><strong>SBOM integration:</strong> Detected licenses are included in generated SBOMs for all ecosystems</li>
|
|
55
|
+
<li><strong>SPDX support:</strong> Automatically detects common licenses (Apache-2.0, MIT, GPL, BSD) from LICENSE file content</li>
|
|
56
|
+
</ul>
|
|
57
|
+
<p>
|
|
58
|
+
See <a href="./docs/license-resolution-and-compliance.md">License Resolution and Compliance</a> for detailed documentation.
|
|
59
|
+
</p>
|
|
60
|
+
|
|
46
61
|
<ul>
|
|
47
62
|
<li>
|
|
48
63
|
Use as ESM Module from Common-JS module
|
|
@@ -83,12 +98,13 @@ Use as CLI Script
|
|
|
83
98
|
```shell
|
|
84
99
|
$ npx @trustify-da/trustify-da-javascript-client help
|
|
85
100
|
|
|
86
|
-
Usage: trustify-da-javascript-client {component|stack|image|validate-token}
|
|
101
|
+
Usage: trustify-da-javascript-client {component|stack|image|validate-token|license}
|
|
87
102
|
|
|
88
103
|
Commands:
|
|
89
104
|
trustify-da-javascript-client stack </path/to/manifest> [--html|--summary] produce stack report for manifest path
|
|
90
105
|
trustify-da-javascript-client component <path/to/manifest> [--summary] produce component report for a manifest type and content
|
|
91
106
|
trustify-da-javascript-client image <image-refs..> [--html|--summary] produce image analysis report for OCI image references
|
|
107
|
+
trustify-da-javascript-client license </path/to/manifest> display project license information from manifest and LICENSE file in JSON format
|
|
92
108
|
|
|
93
109
|
Options:
|
|
94
110
|
--help Show help [boolean]
|
|
@@ -123,6 +139,9 @@ $ npx @trustify-da/trustify-da-javascript-client image docker.io/library/node:18
|
|
|
123
139
|
|
|
124
140
|
# specify architecture using ^^ notation (e.g., httpd:2.4.49^^amd64)
|
|
125
141
|
$ npx @trustify-da/trustify-da-javascript-client image httpd:2.4.49^^amd64
|
|
142
|
+
|
|
143
|
+
# get project license information
|
|
144
|
+
$ npx @trustify-da/trustify-da-javascript-client license /path/to/package.json
|
|
126
145
|
```
|
|
127
146
|
</li>
|
|
128
147
|
|
|
@@ -161,6 +180,9 @@ $ trustify-da-javascript-client image docker.io/library/node:18 docker.io/librar
|
|
|
161
180
|
|
|
162
181
|
# specify architecture using ^^ notation (e.g., httpd:2.4.49^^amd64)
|
|
163
182
|
$ trustify-da-javascript-client image httpd:2.4.49^^amd64
|
|
183
|
+
|
|
184
|
+
# get project license information
|
|
185
|
+
$ trustify-da-javascript-client license /path/to/package.json
|
|
164
186
|
```
|
|
165
187
|
</li>
|
|
166
188
|
</ul>
|
|
@@ -176,6 +198,21 @@ $ trustify-da-javascript-client image httpd:2.4.49^^amd64
|
|
|
176
198
|
<li><a href="https://gradle.org/">Gradle (Groovy and Kotlin DSL)</a> - <a href="https://gradle.org/install/">Gradle Installation</a></li>
|
|
177
199
|
</ul>
|
|
178
200
|
|
|
201
|
+
<h3>License Detection</h3>
|
|
202
|
+
<p>
|
|
203
|
+
The client automatically detects your project's license with intelligent fallback:
|
|
204
|
+
</p>
|
|
205
|
+
<ul>
|
|
206
|
+
<li><strong>Manifest-first:</strong> For ecosystems with license support (Maven, JavaScript), reads from manifest file (<code>pom.xml</code>, <code>package.json</code>)</li>
|
|
207
|
+
<li><strong>LICENSE file fallback:</strong> If no license in manifest, or for ecosystems without license support (Gradle, Go, Python), automatically reads from <code>LICENSE</code>, <code>LICENSE.md</code>, or <code>LICENSE.txt</code></li>
|
|
208
|
+
<li><strong>SBOM integration:</strong> Detected licenses are included in generated SBOMs for all ecosystems</li>
|
|
209
|
+
<li><strong>SPDX support:</strong> Automatically detects common licenses (Apache-2.0, MIT, GPL, BSD) from LICENSE file content</li>
|
|
210
|
+
</ul>
|
|
211
|
+
<p>
|
|
212
|
+
See <a href="./docs/license-resolution-and-compliance.md">License Resolution and Compliance</a> for detailed documentation.
|
|
213
|
+
</p>
|
|
214
|
+
|
|
215
|
+
|
|
179
216
|
<h3>Excluding Packages</h3>
|
|
180
217
|
<p>
|
|
181
218
|
Excluding a package from any analysis can be achieved by marking the package for exclusion.
|
|
@@ -372,6 +409,11 @@ const options = {
|
|
|
372
409
|
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.
|
|
373
410
|
</p>
|
|
374
411
|
|
|
412
|
+
<h4>License resolution and dependency license compliance</h4>
|
|
413
|
+
<p>
|
|
414
|
+
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>) 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.
|
|
415
|
+
</p>
|
|
416
|
+
|
|
375
417
|
<h4>Customizing Executables</h4>
|
|
376
418
|
<p>
|
|
377
419
|
This project uses each ecosystem's executable for creating dependency trees. These executables are expected to be
|
package/dist/package.json
CHANGED
|
@@ -41,33 +41,38 @@
|
|
|
41
41
|
"tests": "mocha --config .mocharc.json --grep \".*analysis module.*\" --invert",
|
|
42
42
|
"tests:rep": "mocha --reporter-option maxDiffSize=0 --reporter json > unit-tests-result.json",
|
|
43
43
|
"precompile": "rm -rf dist",
|
|
44
|
-
"compile": "tsc -p tsconfig.json"
|
|
44
|
+
"compile": "tsc -p tsconfig.json",
|
|
45
|
+
"compile:dev": "tsc -p tsconfig.dev.json"
|
|
45
46
|
},
|
|
46
47
|
"dependencies": {
|
|
47
48
|
"@babel/core": "^7.23.2",
|
|
48
|
-
"@cyclonedx/cyclonedx-library": "
|
|
49
|
+
"@cyclonedx/cyclonedx-library": "^6.13.0",
|
|
50
|
+
"eslint-import-resolver-typescript": "^4.4.4",
|
|
49
51
|
"fast-toml": "^0.5.4",
|
|
50
|
-
"fast-xml-parser": "^
|
|
52
|
+
"fast-xml-parser": "^5.3.4",
|
|
51
53
|
"help": "^3.0.2",
|
|
52
54
|
"https-proxy-agent": "^7.0.6",
|
|
53
|
-
"node-fetch": "^
|
|
54
|
-
"packageurl-js": "
|
|
55
|
-
"
|
|
55
|
+
"node-fetch": "^3.3.2",
|
|
56
|
+
"packageurl-js": "~1.0.2",
|
|
57
|
+
"tree-sitter-requirements": "github:Strum355/tree-sitter-requirements#d0261ee76b84253997fe70d7d397e78c006c3801",
|
|
58
|
+
"web-tree-sitter": "^0.26.6",
|
|
59
|
+
"yargs": "^18.0.0"
|
|
56
60
|
},
|
|
57
61
|
"devDependencies": {
|
|
58
62
|
"@babel/core": "^7.23.2",
|
|
59
|
-
"@trustify-da/trustify-da-api-model": "^2.0.
|
|
63
|
+
"@trustify-da/trustify-da-api-model": "^2.0.7",
|
|
60
64
|
"@types/node": "^20.17.30",
|
|
61
65
|
"@types/which": "^3.0.4",
|
|
62
66
|
"babel-plugin-rewire": "^1.2.0",
|
|
63
|
-
"c8": "^
|
|
67
|
+
"c8": "^11.0.0",
|
|
64
68
|
"chai": "^4.3.7",
|
|
65
69
|
"eslint": "^8.42.0",
|
|
70
|
+
"eslint-import-resolver-typescript": "^4.4.4",
|
|
66
71
|
"eslint-plugin-editorconfig": "^4.0.3",
|
|
67
72
|
"eslint-plugin-import": "^2.29.1",
|
|
68
73
|
"esmock": "^2.6.2",
|
|
69
74
|
"mocha": "^10.2.0",
|
|
70
|
-
"msw": "^
|
|
75
|
+
"msw": "^2.12.7",
|
|
71
76
|
"sinon": "^15.1.2",
|
|
72
77
|
"sinon-chai": "^3.7.0",
|
|
73
78
|
"typescript": "^5.1.3",
|
package/dist/src/analysis.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export default _default;
|
|
|
13
13
|
* @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
|
|
14
14
|
* @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
|
|
15
15
|
*/
|
|
16
|
-
declare function requestComponent(provider: import(
|
|
16
|
+
declare function requestComponent(provider: import("./provider").Provider, manifest: string, url: string, opts?: import("index.js").Options): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport>;
|
|
17
17
|
/**
|
|
18
18
|
* Send a stack analysis request and get the report as 'text/html' or 'application/json'.
|
|
19
19
|
* @param {import('./provider').Provider} provider - the provided data for constructing the request
|
|
@@ -23,7 +23,7 @@ declare function requestComponent(provider: import('./provider').Provider, manif
|
|
|
23
23
|
* @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
|
|
24
24
|
* @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
|
|
25
25
|
*/
|
|
26
|
-
declare function requestStack(provider: import(
|
|
26
|
+
declare function requestStack(provider: import("./provider").Provider, manifest: string, url: string, html?: boolean, opts?: import("index.js").Options): Promise<string | import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport>;
|
|
27
27
|
/**
|
|
28
28
|
*
|
|
29
29
|
* @param {Array<string>} imageRefs
|
|
@@ -31,8 +31,8 @@ declare function requestStack(provider: import('./provider').Provider, manifest:
|
|
|
31
31
|
* @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
|
|
32
32
|
* @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
|
|
33
33
|
*/
|
|
34
|
-
declare function requestImages(imageRefs: Array<string>, url: string, html?: boolean, opts?: import("index.js").Options
|
|
35
|
-
[x: string]: import(
|
|
34
|
+
declare function requestImages(imageRefs: Array<string>, url: string, html?: boolean, opts?: import("index.js").Options): Promise<string | {
|
|
35
|
+
[x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
|
|
36
36
|
}>;
|
|
37
37
|
/**
|
|
38
38
|
*
|
|
@@ -40,4 +40,4 @@ declare function requestImages(imageRefs: Array<string>, url: string, html?: boo
|
|
|
40
40
|
* @param {import("index.js").Options} [opts={}] - optional various options to pass headers for t he validateToken Request
|
|
41
41
|
* @return {Promise<number>} return the HTTP status Code of the response from the validate token request.
|
|
42
42
|
*/
|
|
43
|
-
declare function validateToken(url: any, opts?: import("index.js").Options
|
|
43
|
+
declare function validateToken(url: any, opts?: import("index.js").Options): Promise<number>;
|
package/dist/src/analysis.js
CHANGED
|
@@ -1,28 +1,10 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { EOL } from "os";
|
|
4
|
-
import {
|
|
4
|
+
import { runLicenseCheck } from "./license/index.js";
|
|
5
5
|
import { generateImageSBOM, parseImageRef } from "./oci_image/utils.js";
|
|
6
|
-
import {
|
|
6
|
+
import { addProxyAgent, getCustom, getTokenHeaders, TRUSTIFY_DA_OPERATION_TYPE_HEADER, TRUSTIFY_DA_PACKAGE_MANAGER_HEADER } from "./tools.js";
|
|
7
7
|
export default { requestComponent, requestStack, requestImages, validateToken };
|
|
8
|
-
const rhdaTokenHeader = "rhda-token";
|
|
9
|
-
const rhdaTelemetryId = "rhda-telemetry-id";
|
|
10
|
-
const rhdaSourceHeader = "rhda-source";
|
|
11
|
-
const rhdaOperationTypeHeader = "rhda-operation-type";
|
|
12
|
-
const rhdaPackageManagerHeader = "rhda-pkg-manager";
|
|
13
|
-
/**
|
|
14
|
-
* Adds proxy agent configuration to fetch options if a proxy URL is specified
|
|
15
|
-
* @param {RequestInit} options - The base fetch options
|
|
16
|
-
* @param {import("index.js").Options} opts - The trustify DA options that may contain proxy configuration
|
|
17
|
-
* @returns {RequestInit} The fetch options with proxy agent if applicable
|
|
18
|
-
*/
|
|
19
|
-
function addProxyAgent(options, opts) {
|
|
20
|
-
const proxyUrl = getCustom('TRUSTIFY_DA_PROXY_URL', null, opts);
|
|
21
|
-
if (proxyUrl) {
|
|
22
|
-
options.agent = new HttpsProxyAgent(proxyUrl);
|
|
23
|
-
}
|
|
24
|
-
return options;
|
|
25
|
-
}
|
|
26
8
|
/**
|
|
27
9
|
* Send a stack analysis request and get the report as 'text/html' or 'application/json'.
|
|
28
10
|
* @param {import('./provider').Provider} provider - the provided data for constructing the request
|
|
@@ -35,15 +17,15 @@ function addProxyAgent(options, opts) {
|
|
|
35
17
|
async function requestStack(provider, manifest, url, html = false, opts = {}) {
|
|
36
18
|
opts["source-manifest"] = Buffer.from(fs.readFileSync(manifest).toString()).toString('base64');
|
|
37
19
|
opts["manifest-type"] = path.parse(manifest).base;
|
|
38
|
-
let provided = provider.provideStack(manifest, opts); // throws error if content providing failed
|
|
20
|
+
let provided = await provider.provideStack(manifest, opts); // throws error if content providing failed
|
|
39
21
|
opts["source-manifest"] = "";
|
|
40
|
-
opts[
|
|
22
|
+
opts[TRUSTIFY_DA_OPERATION_TYPE_HEADER.toUpperCase().replaceAll("-", "_")] = "stack-analysis";
|
|
41
23
|
let startTime = new Date();
|
|
42
24
|
let endTime;
|
|
43
25
|
if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
|
|
44
26
|
console.log("Starting time of sending stack analysis request to the dependency analytics server= " + startTime);
|
|
45
27
|
}
|
|
46
|
-
opts[
|
|
28
|
+
opts[TRUSTIFY_DA_PACKAGE_MANAGER_HEADER.toUpperCase().replaceAll("-", "_")] = provided.ecosystem;
|
|
47
29
|
const fetchOptions = addProxyAgent({
|
|
48
30
|
method: 'POST',
|
|
49
31
|
headers: {
|
|
@@ -53,7 +35,7 @@ async function requestStack(provider, manifest, url, html = false, opts = {}) {
|
|
|
53
35
|
},
|
|
54
36
|
body: provided.content
|
|
55
37
|
}, opts);
|
|
56
|
-
const finalUrl = new URL(`${url}/api/
|
|
38
|
+
const finalUrl = new URL(`${url}/api/v5/analysis`);
|
|
57
39
|
if (opts['TRUSTIFY_DA_RECOMMENDATIONS_ENABLED'] === 'false') {
|
|
58
40
|
finalUrl.searchParams.append('recommend', 'false');
|
|
59
41
|
}
|
|
@@ -94,13 +76,13 @@ async function requestStack(provider, manifest, url, html = false, opts = {}) {
|
|
|
94
76
|
*/
|
|
95
77
|
async function requestComponent(provider, manifest, url, opts = {}) {
|
|
96
78
|
opts["source-manifest"] = Buffer.from(fs.readFileSync(manifest).toString()).toString('base64');
|
|
97
|
-
let provided = provider.provideComponent(manifest, opts); // throws error if content providing failed
|
|
79
|
+
let provided = await provider.provideComponent(manifest, opts); // throws error if content providing failed
|
|
98
80
|
opts["source-manifest"] = "";
|
|
99
|
-
opts[
|
|
81
|
+
opts[TRUSTIFY_DA_OPERATION_TYPE_HEADER.toUpperCase().replaceAll("-", "_")] = "component-analysis";
|
|
100
82
|
if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
|
|
101
83
|
console.log("Starting time of sending component analysis request to Trustify DA backend server= " + new Date());
|
|
102
84
|
}
|
|
103
|
-
opts[
|
|
85
|
+
opts[TRUSTIFY_DA_PACKAGE_MANAGER_HEADER.toUpperCase().replaceAll("-", "_")] = provided.ecosystem;
|
|
104
86
|
const fetchOptions = addProxyAgent({
|
|
105
87
|
method: 'POST',
|
|
106
88
|
headers: {
|
|
@@ -110,7 +92,7 @@ async function requestComponent(provider, manifest, url, opts = {}) {
|
|
|
110
92
|
},
|
|
111
93
|
body: provided.content
|
|
112
94
|
}, opts);
|
|
113
|
-
const finalUrl = new URL(`${url}/api/
|
|
95
|
+
const finalUrl = new URL(`${url}/api/v5/analysis`);
|
|
114
96
|
if (opts['TRUSTIFY_DA_RECOMMENDATIONS_ENABLED'] === 'false') {
|
|
115
97
|
finalUrl.searchParams.append('recommend', 'false');
|
|
116
98
|
}
|
|
@@ -127,6 +109,15 @@ async function requestComponent(provider, manifest, url, opts = {}) {
|
|
|
127
109
|
console.log(JSON.stringify(result, null, 4));
|
|
128
110
|
console.log("Ending time of sending component analysis request to Trustify DA backend server= " + new Date());
|
|
129
111
|
}
|
|
112
|
+
const licenseCheckEnabled = getCustom('TRUSTIFY_DA_LICENSE_CHECK', 'true', opts) !== 'false' && opts.licenseCheck !== false;
|
|
113
|
+
if (licenseCheckEnabled) {
|
|
114
|
+
try {
|
|
115
|
+
result.licenseSummary = await runLicenseCheck(provided.content, manifest, url, opts, result);
|
|
116
|
+
}
|
|
117
|
+
catch (licenseErr) {
|
|
118
|
+
result.licenseSummary = { error: licenseErr.message };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
130
121
|
}
|
|
131
122
|
else {
|
|
132
123
|
throw new Error(`Got error response from Trustify DA backend - http return code : ${resp.status}, ex-request-id: ${resp.headers.get("ex-request-id")} error message => ${await resp.text()}`);
|
|
@@ -146,7 +137,7 @@ async function requestImages(imageRefs, url, html = false, opts = {}) {
|
|
|
146
137
|
const parsedImageRef = parseImageRef(image, opts);
|
|
147
138
|
imageSboms[parsedImageRef.getPackageURL().toString()] = generateImageSBOM(parsedImageRef, opts);
|
|
148
139
|
}
|
|
149
|
-
const finalUrl = new URL(`${url}/api/
|
|
140
|
+
const finalUrl = new URL(`${url}/api/v5/batch-analysis`);
|
|
150
141
|
if (opts['TRUSTIFY_DA_RECOMMENDATIONS_ENABLED'] === 'false') {
|
|
151
142
|
finalUrl.searchParams.append('recommend', 'false');
|
|
152
143
|
}
|
|
@@ -195,7 +186,7 @@ async function validateToken(url, opts = {}) {
|
|
|
195
186
|
...getTokenHeaders(opts),
|
|
196
187
|
}
|
|
197
188
|
}, opts);
|
|
198
|
-
let resp = await fetch(`${url}/api/
|
|
189
|
+
let resp = await fetch(`${url}/api/v5/token`, fetchOptions);
|
|
199
190
|
if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
|
|
200
191
|
let exRequestId = resp.headers.get("ex-request-id");
|
|
201
192
|
if (exRequestId) {
|
|
@@ -204,49 +195,3 @@ async function validateToken(url, opts = {}) {
|
|
|
204
195
|
}
|
|
205
196
|
return resp.status;
|
|
206
197
|
}
|
|
207
|
-
/**
|
|
208
|
-
*
|
|
209
|
-
* @param {string} headerName - the header name to populate in request
|
|
210
|
-
* @param headers
|
|
211
|
-
* @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
|
|
212
|
-
* @private
|
|
213
|
-
*/
|
|
214
|
-
function setRhdaHeader(headerName, headers, opts) {
|
|
215
|
-
let rhdaHeaderValue = getCustom(headerName.toUpperCase().replaceAll("-", "_"), null, opts);
|
|
216
|
-
if (rhdaHeaderValue) {
|
|
217
|
-
headers[headerName] = rhdaHeaderValue;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Utility function for fetching vendor tokens
|
|
222
|
-
* @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
|
|
223
|
-
* @returns {{}}
|
|
224
|
-
*/
|
|
225
|
-
function getTokenHeaders(opts = {}) {
|
|
226
|
-
let supportedTokens = ['snyk', 'oss-index'];
|
|
227
|
-
let headers = {};
|
|
228
|
-
supportedTokens.forEach(vendor => {
|
|
229
|
-
let token = getCustom(`TRUSTIFY_DA_${vendor.replace("-", "_").toUpperCase()}_TOKEN`, null, opts);
|
|
230
|
-
if (token) {
|
|
231
|
-
headers[`ex-${vendor}-token`] = token;
|
|
232
|
-
}
|
|
233
|
-
let user = getCustom(`TRUSTIFY_DA_${vendor.replace("-", "_").toUpperCase()}_USER`, null, opts);
|
|
234
|
-
if (user) {
|
|
235
|
-
headers[`ex-${vendor}-user`] = user;
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
setRhdaHeader(rhdaTokenHeader, headers, opts);
|
|
239
|
-
setRhdaHeader(rhdaSourceHeader, headers, opts);
|
|
240
|
-
setRhdaHeader(rhdaOperationTypeHeader, headers, opts);
|
|
241
|
-
setRhdaHeader(rhdaPackageManagerHeader, headers, opts);
|
|
242
|
-
setRhdaHeader(rhdaTelemetryId, headers, opts);
|
|
243
|
-
if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
|
|
244
|
-
console.log("Headers Values to be sent to Trustify DA backend:" + EOL);
|
|
245
|
-
for (const headerKey in headers) {
|
|
246
|
-
if (!headerKey.match(RegexNotToBeLogged)) {
|
|
247
|
-
console.log(`${headerKey}: ${headers[headerKey]}`);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
return headers;
|
|
252
|
-
}
|
package/dist/src/cli.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import yargs from 'yargs';
|
|
4
4
|
import { hideBin } from 'yargs/helpers';
|
|
5
|
-
import
|
|
5
|
+
import { getProjectLicense, getLicenseDetails } from './license/index.js';
|
|
6
|
+
import client, { selectTrustifyDABackend } from './index.js';
|
|
6
7
|
// command for component analysis take manifest type and content
|
|
7
8
|
const component = {
|
|
8
9
|
command: 'component </path/to/manifest>',
|
|
@@ -22,9 +23,8 @@ const validateToken = {
|
|
|
22
23
|
command: 'validate-token <token-provider> [--token-value thevalue]',
|
|
23
24
|
desc: 'Validates input token if authentic and authorized',
|
|
24
25
|
builder: yargs => yargs.positional('token-provider', {
|
|
25
|
-
desc: 'the token provider',
|
|
26
|
-
type: 'string'
|
|
27
|
-
choices: ['snyk', 'oss-index'],
|
|
26
|
+
desc: 'the token provider name',
|
|
27
|
+
type: 'string'
|
|
28
28
|
}).options({
|
|
29
29
|
tokenValue: {
|
|
30
30
|
alias: 'value',
|
|
@@ -37,7 +37,7 @@ const validateToken = {
|
|
|
37
37
|
let opts = {};
|
|
38
38
|
if (args['tokenValue'] !== undefined && args['tokenValue'].trim() !== "") {
|
|
39
39
|
let tokenValue = args['tokenValue'].trim();
|
|
40
|
-
opts[`
|
|
40
|
+
opts[`TRUSTIFY_DA_PROVIDER_${tokenProvider}_TOKEN`] = tokenValue;
|
|
41
41
|
}
|
|
42
42
|
let res = await client.validateToken(opts);
|
|
43
43
|
console.log(res);
|
|
@@ -143,13 +143,79 @@ const stack = {
|
|
|
143
143
|
console.log(html ? res : JSON.stringify(!html && summary ? theProvidersObject : res, null, 2));
|
|
144
144
|
}
|
|
145
145
|
};
|
|
146
|
+
// command for license checking
|
|
147
|
+
const license = {
|
|
148
|
+
command: 'license </path/to/manifest>',
|
|
149
|
+
desc: 'Display project license information from manifest and LICENSE file in JSON format',
|
|
150
|
+
builder: yargs => yargs.positional('/path/to/manifest', {
|
|
151
|
+
desc: 'manifest path for license analysis',
|
|
152
|
+
type: 'string',
|
|
153
|
+
normalize: true,
|
|
154
|
+
}),
|
|
155
|
+
handler: async (args) => {
|
|
156
|
+
let manifestPath = args['/path/to/manifest'];
|
|
157
|
+
const opts = {}; // CLI options can be extended in the future
|
|
158
|
+
try {
|
|
159
|
+
selectTrustifyDABackend(opts);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
console.error(JSON.stringify({ error: err.message }, null, 2));
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
let localResult;
|
|
166
|
+
try {
|
|
167
|
+
localResult = getProjectLicense(manifestPath);
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
console.error(JSON.stringify({ error: `Failed to read manifest: ${err.message}` }, null, 2));
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
const errors = [];
|
|
174
|
+
// Build LicenseInfo objects
|
|
175
|
+
const buildLicenseInfo = async (spdxId) => {
|
|
176
|
+
if (!spdxId) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
const licenseInfo = { spdxId };
|
|
180
|
+
try {
|
|
181
|
+
const details = await getLicenseDetails(spdxId, opts);
|
|
182
|
+
if (details) {
|
|
183
|
+
// Check if backend recognized the license as valid
|
|
184
|
+
if (details.category === 'UNKNOWN') {
|
|
185
|
+
errors.push(`"${spdxId}" is not a valid SPDX license identifier. Please use a valid SPDX expression (e.g., "Apache-2.0", "MIT"). See https://spdx.org/licenses/`);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
Object.assign(licenseInfo, details);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
errors.push(`No license details found for ${spdxId}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
errors.push(`Failed to fetch details for ${spdxId}: ${err.message}`);
|
|
197
|
+
}
|
|
198
|
+
return licenseInfo;
|
|
199
|
+
};
|
|
200
|
+
const output = {
|
|
201
|
+
manifestLicense: await buildLicenseInfo(localResult.fromManifest),
|
|
202
|
+
fileLicense: await buildLicenseInfo(localResult.fromFile),
|
|
203
|
+
mismatch: localResult.mismatch
|
|
204
|
+
};
|
|
205
|
+
if (errors.length > 0) {
|
|
206
|
+
output.errors = errors;
|
|
207
|
+
}
|
|
208
|
+
console.log(JSON.stringify(output, null, 2));
|
|
209
|
+
}
|
|
210
|
+
};
|
|
146
211
|
// parse and invoke the command
|
|
147
212
|
yargs(hideBin(process.argv))
|
|
148
|
-
.usage(`Usage: ${process.argv[0].includes("node") ? path.parse(process.argv[1]).base : path.parse(process.argv[0]).base} {component|stack|image|validate-token}`)
|
|
213
|
+
.usage(`Usage: ${process.argv[0].includes("node") ? path.parse(process.argv[1]).base : path.parse(process.argv[0]).base} {component|stack|image|validate-token|license}`)
|
|
149
214
|
.command(stack)
|
|
150
215
|
.command(component)
|
|
151
216
|
.command(image)
|
|
152
217
|
.command(validateToken)
|
|
218
|
+
.command(license)
|
|
153
219
|
.scriptName('')
|
|
154
220
|
.version(false)
|
|
155
221
|
.demandCommand(1)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/// <reference types="packageurl-js/src/package-url" />
|
|
2
1
|
export default class CycloneDxSbom {
|
|
3
2
|
sbomObject: any;
|
|
4
3
|
rootComponent: any;
|
|
@@ -7,9 +6,10 @@ export default class CycloneDxSbom {
|
|
|
7
6
|
sourceManifestForAuditTrail: any;
|
|
8
7
|
/**
|
|
9
8
|
* @param {PackageURL} root - add main/root component for sbom
|
|
9
|
+
* @param {string|Array} [licenses] - optional license(s) for the root component
|
|
10
10
|
* @return {CycloneDxSbom} the CycloneDxSbom Sbom Object
|
|
11
11
|
*/
|
|
12
|
-
addRoot(root: PackageURL): CycloneDxSbom;
|
|
12
|
+
addRoot(root: PackageURL, licenses?: string | any[]): CycloneDxSbom;
|
|
13
13
|
/**
|
|
14
14
|
* @return {{{"bom-ref": string, name, purl: string, type, version}}} root component of sbom.
|
|
15
15
|
*/
|
|
@@ -49,6 +49,7 @@ export default class CycloneDxSbom {
|
|
|
49
49
|
type: any;
|
|
50
50
|
version: any;
|
|
51
51
|
scope: any;
|
|
52
|
+
licenses?: any;
|
|
52
53
|
};
|
|
53
54
|
/**
|
|
54
55
|
* This method gets an array of dependencies to be ignored, and remove all of them from CycloneDx Sbom
|
|
@@ -5,10 +5,11 @@ import { PackageURL } from "packageurl-js";
|
|
|
5
5
|
* @param component {PackageURL}
|
|
6
6
|
* @param type type of package - application or library
|
|
7
7
|
* @param scope scope of the component - runtime or compile
|
|
8
|
-
* @
|
|
8
|
+
* @param licenses optional license string or array of licenses for the component
|
|
9
|
+
* @return {{"bom-ref": string, name, purl: string, type, version, scope, licenses?}}
|
|
9
10
|
* @private
|
|
10
11
|
*/
|
|
11
|
-
function getComponent(component, type, scope) {
|
|
12
|
+
function getComponent(component, type, scope, licenses) {
|
|
12
13
|
let componentObject;
|
|
13
14
|
if (component instanceof PackageURL) {
|
|
14
15
|
if (component.namespace) {
|
|
@@ -36,6 +37,16 @@ function getComponent(component, type, scope) {
|
|
|
36
37
|
else {
|
|
37
38
|
componentObject = component;
|
|
38
39
|
}
|
|
40
|
+
// Add licenses if provided (CycloneDX format). Callers must provide valid SPDX identifiers.
|
|
41
|
+
if (licenses) {
|
|
42
|
+
const licenseArray = Array.isArray(licenses) ? licenses : [licenses];
|
|
43
|
+
componentObject.licenses = licenseArray.map(lic => {
|
|
44
|
+
if (typeof lic === 'string') {
|
|
45
|
+
return { license: { id: lic } };
|
|
46
|
+
}
|
|
47
|
+
return lic;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
39
50
|
return componentObject;
|
|
40
51
|
}
|
|
41
52
|
function createDependency(dependency) {
|
|
@@ -56,11 +67,12 @@ export default class CycloneDxSbom {
|
|
|
56
67
|
}
|
|
57
68
|
/**
|
|
58
69
|
* @param {PackageURL} root - add main/root component for sbom
|
|
70
|
+
* @param {string|Array} [licenses] - optional license(s) for the root component
|
|
59
71
|
* @return {CycloneDxSbom} the CycloneDxSbom Sbom Object
|
|
60
72
|
*/
|
|
61
|
-
addRoot(root) {
|
|
73
|
+
addRoot(root, licenses) {
|
|
62
74
|
this.rootComponent =
|
|
63
|
-
getComponent(root, "application");
|
|
75
|
+
getComponent(root, "application", undefined, licenses);
|
|
64
76
|
this.components.push(this.rootComponent);
|
|
65
77
|
return this;
|
|
66
78
|
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
export function selectTrustifyDABackend(opts?: {
|
|
13
13
|
TRUSTIFY_DA_DEBUG?: string | undefined;
|
|
14
14
|
TRUSTIFY_DA_BACKEND_URL?: string | undefined;
|
|
15
|
-
}
|
|
15
|
+
}): string;
|
|
16
16
|
export { parseImageRef } from "./oci_image/utils.js";
|
|
17
17
|
export { ImageRef } from "./oci_image/images.js";
|
|
18
18
|
declare namespace _default {
|
|
@@ -23,7 +23,6 @@ declare namespace _default {
|
|
|
23
23
|
}
|
|
24
24
|
export default _default;
|
|
25
25
|
export type Options = {
|
|
26
|
-
[key: string]: string | undefined;
|
|
27
26
|
TRUSTIFY_DA_DOCKER_PATH?: string | undefined;
|
|
28
27
|
TRUSTIFY_DA_GO_MVS_LOGIC_ENABLED?: string | undefined;
|
|
29
28
|
TRUSTIFY_DA_GO_PATH?: string | undefined;
|
|
@@ -48,10 +47,12 @@ export type Options = {
|
|
|
48
47
|
TRUSTIFY_DA_SYFT_CONFIG_PATH?: string | undefined;
|
|
49
48
|
TRUSTIFY_DA_SYFT_PATH?: string | undefined;
|
|
50
49
|
TRUSTIFY_DA_YARN_PATH?: string | undefined;
|
|
50
|
+
TRUSTIFY_DA_LICENSE_CHECK?: string | undefined;
|
|
51
51
|
MATCH_MANIFEST_VERSIONS?: string | undefined;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
TRUSTIFY_DA_SOURCE?: string | undefined;
|
|
53
|
+
TRUSTIFY_DA_TOKEN?: string | undefined;
|
|
54
|
+
TRUSTIFY_DA_TELEMETRY_ID?: string | undefined;
|
|
55
|
+
[key: string]: string | undefined;
|
|
55
56
|
};
|
|
56
57
|
/**
|
|
57
58
|
* Get component analysis report for a manifest content.
|
|
@@ -60,16 +61,68 @@ export type Options = {
|
|
|
60
61
|
* @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
|
|
61
62
|
* @throws {Error} if no matching provider, failed to get create content, or backend request failed
|
|
62
63
|
*/
|
|
63
|
-
declare function componentAnalysis(manifest: string, opts?: Options
|
|
64
|
+
declare function componentAnalysis(manifest: string, opts?: Options): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport>;
|
|
65
|
+
/**
|
|
66
|
+
* @overload
|
|
67
|
+
* @param {string} manifest
|
|
68
|
+
* @param {true} html
|
|
69
|
+
* @param {Options} [opts={}]
|
|
70
|
+
* @returns {Promise<string>}
|
|
71
|
+
* @throws {Error}
|
|
72
|
+
*/
|
|
64
73
|
declare function stackAnalysis(manifest: string, html: true, opts?: Options | undefined): Promise<string>;
|
|
65
|
-
|
|
66
|
-
|
|
74
|
+
/**
|
|
75
|
+
* @overload
|
|
76
|
+
* @param {string} manifest
|
|
77
|
+
* @param {false} html
|
|
78
|
+
* @param {Options} [opts={}]
|
|
79
|
+
* @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
|
|
80
|
+
* @throws {Error}
|
|
81
|
+
*/
|
|
82
|
+
declare function stackAnalysis(manifest: string, html: false, opts?: Options | undefined): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport>;
|
|
83
|
+
/**
|
|
84
|
+
* Get stack analysis report for a manifest file.
|
|
85
|
+
* @overload
|
|
86
|
+
* @param {string} manifest - path for the manifest
|
|
87
|
+
* @param {boolean} [html=false] - true will return a html string, false will return AnalysisReport object.
|
|
88
|
+
* @param {Options} [opts={}] - optional various options to pass along the application
|
|
89
|
+
* @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
|
|
90
|
+
* @throws {Error} if manifest inaccessible, no matching provider, failed to get create content,
|
|
91
|
+
* or backend request failed
|
|
92
|
+
*/
|
|
93
|
+
declare function stackAnalysis(manifest: string, html?: boolean | undefined, opts?: Options | undefined): Promise<string | import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport>;
|
|
94
|
+
/**
|
|
95
|
+
* @overload
|
|
96
|
+
* @param {Array<string>} imageRefs
|
|
97
|
+
* @param {true} html
|
|
98
|
+
* @param {Options} [opts={}]
|
|
99
|
+
* @returns {Promise<string>}
|
|
100
|
+
* @throws {Error}
|
|
101
|
+
*/
|
|
67
102
|
declare function imageAnalysis(imageRefs: Array<string>, html: true, opts?: Options | undefined): Promise<string>;
|
|
103
|
+
/**
|
|
104
|
+
* @overload
|
|
105
|
+
* @param {Array<string>} imageRefs
|
|
106
|
+
* @param {false} html
|
|
107
|
+
* @param {Options} [opts={}]
|
|
108
|
+
* @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
|
|
109
|
+
* @throws {Error}
|
|
110
|
+
*/
|
|
68
111
|
declare function imageAnalysis(imageRefs: Array<string>, html: false, opts?: Options | undefined): Promise<{
|
|
69
|
-
[x: string]: import(
|
|
112
|
+
[x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
|
|
70
113
|
}>;
|
|
114
|
+
/**
|
|
115
|
+
* Get image analysis report for a set of OCI image references.
|
|
116
|
+
* @overload
|
|
117
|
+
* @param {Array<string>} imageRefs - OCI image references
|
|
118
|
+
* @param {boolean} [html=false] - true will return a html string, false will return AnalysisReport
|
|
119
|
+
* @param {Options} [opts={}] - optional various options to pass along the application
|
|
120
|
+
* @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
|
|
121
|
+
* @throws {Error} if manifest inaccessible, no matching provider, failed to get create content,
|
|
122
|
+
* or backend request failed
|
|
123
|
+
*/
|
|
71
124
|
declare function imageAnalysis(imageRefs: Array<string>, html?: boolean | undefined, opts?: Options | undefined): Promise<string | {
|
|
72
|
-
[x: string]: import(
|
|
125
|
+
[x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
|
|
73
126
|
}>;
|
|
74
127
|
/**
|
|
75
128
|
* Validates the Exhort token.
|
|
@@ -77,4 +130,5 @@ declare function imageAnalysis(imageRefs: Array<string>, html?: boolean | undefi
|
|
|
77
130
|
* @returns {Promise<object>} A promise that resolves with the validation result from the backend.
|
|
78
131
|
* @throws {Error} if the backend request failed.
|
|
79
132
|
*/
|
|
80
|
-
declare function validateToken(opts?: Options
|
|
133
|
+
declare function validateToken(opts?: Options): Promise<object>;
|
|
134
|
+
export { getProjectLicense, findLicenseFilePath, identifyLicense, getLicenseDetails, licensesFromReport, normalizeLicensesResponse, runLicenseCheck, getCompatibility } from "./license/index.js";
|