@trustify-da/trustify-da-javascript-client 0.3.0-ea.2ea1d77 → 0.3.0-ea.329b3cf
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 +75 -10
- package/dist/package.json +28 -24
- package/dist/src/analysis.d.ts +16 -7
- package/dist/src/analysis.js +36 -17
- package/dist/src/cli.js +109 -3
- package/dist/src/cyclone_dx_sbom.d.ts +14 -1
- package/dist/src/cyclone_dx_sbom.js +43 -8
- package/dist/src/index.d.ts +86 -11
- package/dist/src/index.js +97 -8
- 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/oci_image/utils.js +11 -2
- package/dist/src/provider.d.ts +4 -3
- package/dist/src/provider.js +12 -2
- package/dist/src/providers/base_java.d.ts +14 -12
- package/dist/src/providers/base_java.js +18 -44
- package/dist/src/providers/base_javascript.d.ts +36 -15
- package/dist/src/providers/base_javascript.js +100 -20
- package/dist/src/providers/base_pyproject.d.ts +158 -0
- package/dist/src/providers/base_pyproject.js +322 -0
- package/dist/src/providers/containerfile_parser.d.ts +5 -0
- package/dist/src/providers/containerfile_parser.js +20 -0
- package/dist/src/providers/golang_gomodules.d.ts +23 -13
- package/dist/src/providers/golang_gomodules.js +165 -119
- package/dist/src/providers/gomod_parser.d.ts +4 -0
- package/dist/src/providers/gomod_parser.js +16 -0
- package/dist/src/providers/java_gradle.d.ts +19 -0
- package/dist/src/providers/java_gradle.js +116 -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 +10 -2
- package/dist/src/providers/java_maven.js +165 -1
- package/dist/src/providers/javascript_bun.d.ts +10 -0
- package/dist/src/providers/javascript_bun.js +100 -0
- package/dist/src/providers/javascript_npm.d.ts +6 -0
- package/dist/src/providers/javascript_npm.js +21 -0
- package/dist/src/providers/javascript_pnpm.js +6 -2
- package/dist/src/providers/manifest.d.ts +2 -0
- package/dist/src/providers/manifest.js +22 -4
- package/dist/src/providers/marker_evaluator.d.ts +14 -0
- package/dist/src/providers/marker_evaluator.js +191 -0
- package/dist/src/providers/oci_dockerfile.d.ts +51 -0
- package/dist/src/providers/oci_dockerfile.js +177 -0
- package/dist/src/providers/processors/yarn_berry_processor.d.ts +2 -2
- package/dist/src/providers/processors/yarn_berry_processor.js +88 -5
- package/dist/src/providers/processors/yarn_classic_processor.d.ts +2 -2
- package/dist/src/providers/python_controller.d.ts +5 -1
- package/dist/src/providers/python_controller.js +69 -8
- package/dist/src/providers/python_pip.d.ts +5 -0
- package/dist/src/providers/python_pip.js +6 -6
- package/dist/src/providers/python_pip_pyproject.d.ts +61 -0
- package/dist/src/providers/python_pip_pyproject.js +146 -0
- package/dist/src/providers/python_poetry.d.ts +98 -0
- package/dist/src/providers/python_poetry.js +286 -0
- package/dist/src/providers/python_uv.d.ts +46 -0
- package/dist/src/providers/python_uv.js +269 -0
- package/dist/src/providers/rust_cargo.d.ts +6 -2
- package/dist/src/providers/rust_cargo.js +31 -4
- package/dist/src/providers/tree-sitter-containerfile.wasm +0 -0
- package/dist/src/providers/tree-sitter-gomod.wasm +0 -0
- package/dist/src/remediation.d.ts +34 -0
- package/dist/src/remediation.js +321 -0
- package/dist/src/remediation_report.d.ts +40 -0
- package/dist/src/remediation_report.js +159 -0
- package/dist/src/sbom.d.ts +14 -1
- package/dist/src/sbom.js +13 -2
- package/dist/src/tools.d.ts +28 -2
- package/dist/src/tools.js +58 -0
- package/dist/src/updaters/maven_updater.d.ts +26 -0
- package/dist/src/updaters/maven_updater.js +322 -0
- package/dist/src/updaters/toml_updater.d.ts +33 -0
- package/dist/src/updaters/toml_updater.js +244 -0
- package/dist/src/workspace.d.ts +9 -0
- package/dist/src/workspace.js +1 -1
- package/package.json +29 -25
package/README.md
CHANGED
|
@@ -224,7 +224,8 @@ $ trustify-da-javascript-client license /path/to/package.json
|
|
|
224
224
|
<li><a href="https://www.javascript.com/">JavaScript</a> - <a href="https://pnpm.io/">pnpm</a></li>
|
|
225
225
|
<li><a href="https://www.javascript.com/">JavaScript</a> - <a href="https://classic.yarnpkg.com/">Yarn Classic</a> / <a href="https://yarnpkg.com/">Yarn Berry</a></li>
|
|
226
226
|
<li><a href="https://go.dev/">Golang</a> - <a href="https://go.dev/blog/using-go-modules/">Go Modules</a></li>
|
|
227
|
-
<li><a href="https://www.python.org/">Python</a> - <a href="https://pypi.org/project/pip/">pip Installer</a
|
|
227
|
+
<li><a href="https://www.python.org/">Python</a> - <a href="https://pypi.org/project/pip/">pip Installer</a> (<code>requirements.txt</code>)</li>
|
|
228
|
+
<li><a href="https://www.python.org/">Python</a> - <a href="https://python-poetry.org/">Poetry</a> / <a href="https://docs.astral.sh/uv/">uv</a> (<code>pyproject.toml</code>)</li>
|
|
228
229
|
<li><a href="https://gradle.org/">Gradle (Groovy and Kotlin DSL)</a> - <a href="https://gradle.org/install/">Gradle Installation</a></li>
|
|
229
230
|
<li><a href="https://www.rust-lang.org/">Rust</a> - <a href="https://doc.rust-lang.org/cargo/">Cargo</a></li>
|
|
230
231
|
</ul>
|
|
@@ -287,8 +288,11 @@ Excluding a package from any analysis can be achieved by marking the package for
|
|
|
287
288
|
]
|
|
288
289
|
}
|
|
289
290
|
```
|
|
291
|
+
</li>
|
|
292
|
+
|
|
293
|
+
<li>
|
|
294
|
+
<em>Golang</em> users can add in go.mod a comment with <code>// exhortignore</code> next to the package to be ignored, or to "piggyback" on existing comment ( e.g - <code>// indirect</code>), for example:
|
|
290
295
|
|
|
291
|
-
<em>Golang</em> users can add in go.mod a comment with //exhortignore next to the package to be ignored, or to "piggyback" on existing comment ( e.g - //indirect) , for example:
|
|
292
296
|
```go
|
|
293
297
|
module github.com/trustify-da/SaaSi/deployer
|
|
294
298
|
|
|
@@ -297,7 +301,7 @@ go 1.19
|
|
|
297
301
|
require (
|
|
298
302
|
github.com/gin-gonic/gin v1.9.1
|
|
299
303
|
github.com/google/uuid v1.1.2
|
|
300
|
-
github.com/jessevdk/go-flags v1.5.0 //exhortignore
|
|
304
|
+
github.com/jessevdk/go-flags v1.5.0 // exhortignore
|
|
301
305
|
github.com/kr/pretty v0.3.1
|
|
302
306
|
gopkg.in/yaml.v2 v2.4.0
|
|
303
307
|
k8s.io/apimachinery v0.26.1
|
|
@@ -305,14 +309,20 @@ require (
|
|
|
305
309
|
)
|
|
306
310
|
|
|
307
311
|
require (
|
|
308
|
-
github.com/davecgh/go-spew v1.1.1 // indirect exhortignore
|
|
312
|
+
github.com/davecgh/go-spew v1.1.1 // indirect; exhortignore
|
|
309
313
|
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
|
|
310
|
-
github.com/go-logr/logr v1.2.3 // indirect
|
|
314
|
+
github.com/go-logr/logr v1.2.3 // indirect; exhortignore
|
|
311
315
|
|
|
312
316
|
)
|
|
313
317
|
```
|
|
314
318
|
|
|
319
|
+
<b>NOTE</b>: It is important to format <code>exhortignore</code> markers on indirect dependencies as shown above, otherwise the Go tooling (as well as this library) may incorrectly parse dependencies marked as indirect as being direct dependencies instead.
|
|
320
|
+
</li>
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
<li>
|
|
315
324
|
<em>Python pip</em> users can add in requirements.txt a comment with #exhortignore(or # exhortignore) to the right of the same artifact to be ignored, for example:
|
|
325
|
+
|
|
316
326
|
```properties
|
|
317
327
|
anyio==3.6.2
|
|
318
328
|
asgiref==3.4.1
|
|
@@ -343,11 +353,14 @@ Werkzeug==2.0.3
|
|
|
343
353
|
zipp==3.6.0
|
|
344
354
|
|
|
345
355
|
```
|
|
356
|
+
</li>
|
|
346
357
|
|
|
358
|
+
<li>
|
|
347
359
|
<em>Gradle</em> users can add in build.gradle a comment with //exhortignore next to the package to be ignored:
|
|
360
|
+
|
|
348
361
|
```build.gradle
|
|
349
362
|
plugins {
|
|
350
|
-
id 'java'
|
|
363
|
+
id 'java'
|
|
351
364
|
}
|
|
352
365
|
|
|
353
366
|
group = 'groupName'
|
|
@@ -379,9 +392,31 @@ version = "1.10"
|
|
|
379
392
|
log = "0.4" # trustify-da-ignore
|
|
380
393
|
```
|
|
381
394
|
|
|
382
|
-
All of the 6 above examples are valid for marking a package to be ignored
|
|
383
|
-
</li>
|
|
384
395
|
|
|
396
|
+
<em>Python pyproject.toml</em> users can add a comment with <code>#exhortignore</code> (or <code># trustify-da-ignore</code>) next to the dependency in <code>pyproject.toml</code>.
|
|
397
|
+
|
|
398
|
+
PEP 621 style (<code>[project]</code> dependencies):
|
|
399
|
+
```toml
|
|
400
|
+
[project]
|
|
401
|
+
dependencies = [
|
|
402
|
+
"flask>=2.0.3",
|
|
403
|
+
"requests>=2.25.1",
|
|
404
|
+
"uvicorn>=0.17.0", #exhortignore
|
|
405
|
+
"click>=8.0.4", # trustify-da-ignore
|
|
406
|
+
]
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Poetry style (<code>[tool.poetry.dependencies]</code>):
|
|
410
|
+
```toml
|
|
411
|
+
[tool.poetry.dependencies]
|
|
412
|
+
flask = "^2.0.3"
|
|
413
|
+
requests = "^2.25.1"
|
|
414
|
+
uvicorn = "^0.17.0" #exhortignore
|
|
415
|
+
click = "^8.0.4" # trustify-da-ignore
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
All of the above examples are valid for marking a package to be ignored
|
|
419
|
+
</li>
|
|
385
420
|
</ul>
|
|
386
421
|
|
|
387
422
|
<h3>Customization</h3>
|
|
@@ -410,6 +445,8 @@ let options = {
|
|
|
410
445
|
'TRUSTIFY_DA_PIP3_PATH' : '/path/to/pip3',
|
|
411
446
|
'TRUSTIFY_DA_PYTHON_PATH' : '/path/to/python',
|
|
412
447
|
'TRUSTIFY_DA_PIP_PATH' : '/path/to/pip',
|
|
448
|
+
'TRUSTIFY_DA_UV_PATH' : '/path/to/uv',
|
|
449
|
+
'TRUSTIFY_DA_POETRY_PATH' : '/path/to/poetry',
|
|
413
450
|
'TRUSTIFY_DA_GRADLE_PATH' : '/path/to/gradle',
|
|
414
451
|
'TRUSTIFY_DA_CARGO_PATH' : '/path/to/cargo',
|
|
415
452
|
// Workspace root for monorepos (Cargo, npm/pnpm/yarn); lock file expected here
|
|
@@ -556,6 +593,16 @@ following keys for setting custom paths for the said executables.
|
|
|
556
593
|
<td>TRUSTIFY_DA_CARGO_PATH</td>
|
|
557
594
|
</tr>
|
|
558
595
|
<tr>
|
|
596
|
+
<td><a href="https://docs.astral.sh/uv/">uv</a></td>
|
|
597
|
+
<td><em>uv</em></td>
|
|
598
|
+
<td>TRUSTIFY_DA_UV_PATH</td>
|
|
599
|
+
</tr>
|
|
600
|
+
<tr>
|
|
601
|
+
<td><a href="https://python-poetry.org/">Poetry</a></td>
|
|
602
|
+
<td><em>poetry</em></td>
|
|
603
|
+
<td>TRUSTIFY_DA_POETRY_PATH</td>
|
|
604
|
+
</tr>
|
|
605
|
+
<tr>
|
|
559
606
|
<td>Workspace root (monorepos)</td>
|
|
560
607
|
<td>—</td>
|
|
561
608
|
<td>workspaceDir / TRUSTIFY_DA_WORKSPACE_DIR</td>
|
|
@@ -608,6 +655,24 @@ TRUSTIFY_DA_GO_MVS_LOGIC_ENABLED=false
|
|
|
608
655
|
|
|
609
656
|
#### Python Support
|
|
610
657
|
|
|
658
|
+
The client supports two Python manifest formats:
|
|
659
|
+
|
|
660
|
+
- **`requirements.txt`** — uses pip/pip3 to resolve dependencies
|
|
661
|
+
- **`pyproject.toml`** — uses [uv](https://docs.astral.sh/uv/) or [Poetry](https://python-poetry.org/) to resolve dependencies
|
|
662
|
+
|
|
663
|
+
##### pyproject.toml
|
|
664
|
+
|
|
665
|
+
For `pyproject.toml` projects, the client detects which tool manages the project by checking for lock files:
|
|
666
|
+
- If `poetry.lock` is present and `[tool.poetry]` is defined, **Poetry** is used (`poetry show --tree` and `poetry show --all`)
|
|
667
|
+
- If `uv.lock` is present, **uv** is used (`uv export --format requirements.txt --frozen --no-hashes`)
|
|
668
|
+
- If neither lock file is found, an error is thrown
|
|
669
|
+
|
|
670
|
+
Both PEP 621 (`[project]` dependencies) and Poetry-style (`[tool.poetry.dependencies]`) are supported.
|
|
671
|
+
|
|
672
|
+
Custom executable paths can be set via `TRUSTIFY_DA_UV_PATH` and `TRUSTIFY_DA_POETRY_PATH`.
|
|
673
|
+
|
|
674
|
+
##### requirements.txt
|
|
675
|
+
|
|
611
676
|
By default, For python support, the api assumes that the package is installed using the pip/pip3 binary on the system PATH, or using the customized
|
|
612
677
|
Binaries passed to environment variables. In any case, If the package is not installed , then an error will be thrown.
|
|
613
678
|
|
|
@@ -623,14 +688,14 @@ This increasing the chances and the probability that the automatic installation
|
|
|
623
688
|
###### Usage
|
|
624
689
|
A New setting is introduced - `TRUSTIFY_DA_PYTHON_INSTALL_BEST_EFFORTS` (as both env variable/key in `options` object)
|
|
625
690
|
1. `TRUSTIFY_DA_PYTHON_INSTALL_BEST_EFFORTS`="false" - install requirements.txt while respecting declared versions for all packages.
|
|
626
|
-
2. `TRUSTIFY_DA_PYTHON_INSTALL_BEST_EFFORTS`="true" - install all packages from requirements.txt, not respecting the declared version, but trying to install a version tailored for the used python version. When using this setting, you must set
|
|
691
|
+
2. `TRUSTIFY_DA_PYTHON_INSTALL_BEST_EFFORTS`="true" - install all packages from requirements.txt, not respecting the declared version, but trying to install a version tailored for the used python version. When using this setting, you must set `TRUSTIFY_DA_PYTHON_VIRTUAL_ENV` to 'true' and `MATCH_MANIFEST_VERSIONS` to 'false'.
|
|
627
692
|
|
|
628
693
|
##### Using `pipdeptree`
|
|
629
694
|
By default, The API algorithm will use native commands of PIP installer as data source to build the dependency tree.
|
|
630
695
|
It's also possible to use the lightweight Python PIP utility [pipdeptree](https://pypi.org/project/pipdeptree/) as data source instead. In order to activate this, you need to set the environment variable/option `TRUSTIFY_DA_PIP_USE_DEP_TREE` to 'true'.
|
|
631
696
|
|
|
632
697
|
#### Toggle Red Hat Trusted Content recommendations
|
|
633
|
-
Both the HTML-based report and JSON response will by default contain recommendations for migrating to Red Hat-based Trusted Content repositories. This feature can be disabled by setting `
|
|
698
|
+
Both the HTML-based report and JSON response will by default contain recommendations for migrating to Red Hat-based Trusted Content repositories. This feature can be disabled by setting `TRUSTIFY_DA_RECOMMEND` to 'false' via environment variables or options.
|
|
634
699
|
|
|
635
700
|
#### Additional CLI arguments
|
|
636
701
|
For some ecosystems we support passing additional CLI arguments to the underlying tools. The following table outlines the supported ecosystems and the environment variable/option that configures this. Note that the arguments are expected to be in the format of a JSON array.
|
package/dist/package.json
CHANGED
|
@@ -35,55 +35,58 @@
|
|
|
35
35
|
"config/**/*"
|
|
36
36
|
],
|
|
37
37
|
"scripts": {
|
|
38
|
-
"lint": "eslint src test
|
|
39
|
-
"lint:fix": "eslint src test --
|
|
38
|
+
"lint": "eslint src test",
|
|
39
|
+
"lint:fix": "eslint src test --fix",
|
|
40
40
|
"test": "c8 npm run tests",
|
|
41
|
-
"tests": "mocha --config .mocharc.json
|
|
41
|
+
"tests": "mocha --config .mocharc.json",
|
|
42
42
|
"tests:rep": "mocha --reporter-option maxDiffSize=0 --reporter json > unit-tests-result.json",
|
|
43
|
-
"pretest": "cp node_modules/tree-sitter-requirements/tree-sitter-requirements.wasm src/providers/tree-sitter-requirements.wasm",
|
|
43
|
+
"pretest": "cp node_modules/tree-sitter-requirements/tree-sitter-requirements.wasm src/providers/tree-sitter-requirements.wasm && cp node_modules/tree-sitter-gomod/tree-sitter-gomod.wasm src/providers/tree-sitter-gomod.wasm && cp node_modules/tree-sitter-containerfile/tree-sitter-containerfile.wasm src/providers/tree-sitter-containerfile.wasm",
|
|
44
44
|
"precompile": "rm -rf dist",
|
|
45
45
|
"compile": "tsc -p tsconfig.json",
|
|
46
46
|
"compile:dev": "tsc -p tsconfig.dev.json",
|
|
47
|
-
"postcompile": "cp node_modules/tree-sitter-requirements/tree-sitter-requirements.wasm dist/src/providers/tree-sitter-requirements.wasm"
|
|
47
|
+
"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
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@babel/core": "^7.23.2",
|
|
51
|
-
"@cyclonedx/cyclonedx-library": "^
|
|
51
|
+
"@cyclonedx/cyclonedx-library": "^10.0.0",
|
|
52
52
|
"eslint-import-resolver-typescript": "^4.4.4",
|
|
53
53
|
"fast-glob": "^3.3.3",
|
|
54
|
-
"fast-toml": "^0.5.4",
|
|
55
54
|
"fast-xml-parser": "^5.3.4",
|
|
56
55
|
"help": "^3.0.2",
|
|
57
|
-
"https-proxy-agent": "^
|
|
56
|
+
"https-proxy-agent": "^9.0.0",
|
|
58
57
|
"js-yaml": "^4.1.1",
|
|
58
|
+
"jsonc-parser": "^3.3.1",
|
|
59
59
|
"micromatch": "^4.0.8",
|
|
60
60
|
"node-fetch": "^3.3.2",
|
|
61
|
-
"p-limit": "^
|
|
61
|
+
"p-limit": "^7.3.0",
|
|
62
62
|
"packageurl-js": "~1.0.2",
|
|
63
63
|
"smol-toml": "^1.6.0",
|
|
64
|
+
"tree-sitter-containerfile": "^0.8.1",
|
|
65
|
+
"tree-sitter-gomod": "github:strum355/tree-sitter-go-mod#56326f2ad478892ace58ff247a97d492a3cbcdda",
|
|
64
66
|
"tree-sitter-requirements": "github:Strum355/tree-sitter-requirements#d0261ee76b84253997fe70d7d397e78c006c3801",
|
|
65
|
-
"web-tree-sitter": "^0.26.
|
|
67
|
+
"web-tree-sitter": "^0.26.7",
|
|
66
68
|
"yargs": "^18.0.0"
|
|
67
69
|
},
|
|
68
70
|
"devDependencies": {
|
|
69
71
|
"@babel/core": "^7.23.2",
|
|
70
|
-
"@
|
|
71
|
-
"@
|
|
72
|
+
"@eslint/js": "^10.0.0",
|
|
73
|
+
"@trustify-da/trustify-da-api-model": "^2.0.12",
|
|
74
|
+
"@types/node": "^25.9.1",
|
|
72
75
|
"@types/which": "^3.0.4",
|
|
73
76
|
"babel-plugin-rewire": "^1.2.0",
|
|
74
77
|
"c8": "^11.0.0",
|
|
75
|
-
"chai": "^
|
|
76
|
-
"eslint": "^
|
|
78
|
+
"chai": "^6.2.2",
|
|
79
|
+
"eslint": "^10.4.1",
|
|
77
80
|
"eslint-import-resolver-typescript": "^4.4.4",
|
|
78
|
-
"eslint-plugin-
|
|
79
|
-
"eslint-plugin-import": "^2.29.1",
|
|
81
|
+
"eslint-plugin-import-x": "^4.16.2",
|
|
80
82
|
"esmock": "^2.6.2",
|
|
81
|
-
"
|
|
83
|
+
"globals": "^17.6.0",
|
|
84
|
+
"mocha": "^11.7.6",
|
|
82
85
|
"msw": "^2.12.7",
|
|
83
|
-
"sinon": "^
|
|
84
|
-
"sinon-chai": "^
|
|
85
|
-
"typescript": "^
|
|
86
|
-
"which": "^
|
|
86
|
+
"sinon": "^22.0.0",
|
|
87
|
+
"sinon-chai": "^4.0.1",
|
|
88
|
+
"typescript": "^6.0.3",
|
|
89
|
+
"which": "^7.0.0"
|
|
87
90
|
},
|
|
88
91
|
"mocha": {
|
|
89
92
|
"check-leaks": false,
|
|
@@ -111,12 +114,13 @@
|
|
|
111
114
|
"reporter": [
|
|
112
115
|
"html",
|
|
113
116
|
"json",
|
|
117
|
+
"lcov",
|
|
114
118
|
"text"
|
|
115
119
|
]
|
|
116
120
|
},
|
|
117
|
-
"
|
|
118
|
-
"
|
|
119
|
-
|
|
121
|
+
"overrides": {
|
|
122
|
+
"tree-sitter": "0.22.4"
|
|
123
|
+
},
|
|
120
124
|
"resolutions": {
|
|
121
125
|
"@hapi/joi": "17.1.1"
|
|
122
126
|
}
|
package/dist/src/analysis.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
/** Media type for CycloneDX JSON batch payloads (batch-analysis API). */
|
|
2
|
+
export const CYCLONEDX_JSON_MEDIA_TYPE: "application/vnd.cyclonedx+json";
|
|
1
3
|
declare namespace _default {
|
|
2
4
|
export { requestComponent };
|
|
3
5
|
export { requestStack };
|
|
4
6
|
export { requestStackBatch };
|
|
5
7
|
export { requestImages };
|
|
6
8
|
export { validateToken };
|
|
9
|
+
export { appendAnalysisQueryParams };
|
|
7
10
|
}
|
|
8
11
|
export default _default;
|
|
9
12
|
/**
|
|
@@ -14,7 +17,7 @@ export default _default;
|
|
|
14
17
|
* @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
|
|
15
18
|
* @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
|
|
16
19
|
*/
|
|
17
|
-
declare function requestComponent(provider:
|
|
20
|
+
declare function requestComponent(provider: any, manifest: string, url: string, opts?: any): Promise<any>;
|
|
18
21
|
/**
|
|
19
22
|
* Send a stack analysis request and get the report as 'text/html' or 'application/json'.
|
|
20
23
|
* @param {import('./provider').Provider} provider - the provided data for constructing the request
|
|
@@ -24,7 +27,7 @@ declare function requestComponent(provider: import("./provider").Provider, manif
|
|
|
24
27
|
* @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
|
|
25
28
|
* @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
|
|
26
29
|
*/
|
|
27
|
-
declare function requestStack(provider:
|
|
30
|
+
declare function requestStack(provider: any, manifest: string, url: string, html?: boolean, opts?: any): Promise<string | any>;
|
|
28
31
|
/**
|
|
29
32
|
* Send a batch stack analysis request for multiple manifests (SBOMs keyed by purl).
|
|
30
33
|
* @param {Object.<string, object>} sbomByPurl - Map of root purl to CycloneDX SBOM object
|
|
@@ -34,9 +37,9 @@ declare function requestStack(provider: import("./provider").Provider, manifest:
|
|
|
34
37
|
* @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
|
|
35
38
|
*/
|
|
36
39
|
declare function requestStackBatch(sbomByPurl: {
|
|
40
|
+
[x: string]: object;
|
|
41
|
+
}, url: string, html?: boolean, opts?: any): Promise<string | {
|
|
37
42
|
[x: string]: any;
|
|
38
|
-
}, url: string, html?: boolean, opts?: import("index.js").Options): Promise<string | {
|
|
39
|
-
[x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport;
|
|
40
43
|
}>;
|
|
41
44
|
/**
|
|
42
45
|
*
|
|
@@ -45,8 +48,8 @@ declare function requestStackBatch(sbomByPurl: {
|
|
|
45
48
|
* @param {import("index.js").Options} [opts={}] - optional various options to pass along the application
|
|
46
49
|
* @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
|
|
47
50
|
*/
|
|
48
|
-
declare function requestImages(imageRefs: Array<string>, url: string, html?: boolean, opts?:
|
|
49
|
-
[x: string]:
|
|
51
|
+
declare function requestImages(imageRefs: Array<string>, url: string, html?: boolean, opts?: any): Promise<string | {
|
|
52
|
+
[x: string]: any;
|
|
50
53
|
}>;
|
|
51
54
|
/**
|
|
52
55
|
*
|
|
@@ -54,4 +57,10 @@ declare function requestImages(imageRefs: Array<string>, url: string, html?: boo
|
|
|
54
57
|
* @param {import("index.js").Options} [opts={}] - optional various options to pass headers for t he validateToken Request
|
|
55
58
|
* @return {Promise<number>} return the HTTP status Code of the response from the validate token request.
|
|
56
59
|
*/
|
|
57
|
-
declare function validateToken(url: any, opts?:
|
|
60
|
+
declare function validateToken(url: any, opts?: any): Promise<number>;
|
|
61
|
+
/**
|
|
62
|
+
* Append common analysis query parameters (recommend, providers, sources) to a URL.
|
|
63
|
+
* @param {URL} url - the URL object to append query parameters to
|
|
64
|
+
* @param {import("index.js").Options} [opts={}] - options containing parameter overrides
|
|
65
|
+
*/
|
|
66
|
+
declare function appendAnalysisQueryParams(url: URL, opts?: any): void;
|
package/dist/src/analysis.js
CHANGED
|
@@ -4,7 +4,9 @@ import { EOL } from "os";
|
|
|
4
4
|
import { runLicenseCheck } from "./license/index.js";
|
|
5
5
|
import { generateImageSBOM, parseImageRef } from "./oci_image/utils.js";
|
|
6
6
|
import { addProxyAgent, getCustom, getTokenHeaders, TRUSTIFY_DA_OPERATION_TYPE_HEADER, TRUSTIFY_DA_PACKAGE_MANAGER_HEADER } from "./tools.js";
|
|
7
|
-
|
|
7
|
+
/** Media type for CycloneDX JSON batch payloads (batch-analysis API). */
|
|
8
|
+
export const CYCLONEDX_JSON_MEDIA_TYPE = 'application/vnd.cyclonedx+json';
|
|
9
|
+
export default { requestComponent, requestStack, requestStackBatch, requestImages, validateToken, appendAnalysisQueryParams };
|
|
8
10
|
/**
|
|
9
11
|
* Send a stack analysis request and get the report as 'text/html' or 'application/json'.
|
|
10
12
|
* @param {import('./provider').Provider} provider - the provided data for constructing the request
|
|
@@ -18,6 +20,9 @@ async function requestStack(provider, manifest, url, html = false, opts = {}) {
|
|
|
18
20
|
opts["source-manifest"] = Buffer.from(fs.readFileSync(manifest).toString()).toString('base64');
|
|
19
21
|
opts["manifest-type"] = path.parse(manifest).base;
|
|
20
22
|
let provided = await provider.provideStack(manifest, opts); // throws error if content providing failed
|
|
23
|
+
if (provided.batch) {
|
|
24
|
+
return requestStackBatch(JSON.parse(provided.content), url, html, opts);
|
|
25
|
+
}
|
|
21
26
|
opts["source-manifest"] = "";
|
|
22
27
|
opts[TRUSTIFY_DA_OPERATION_TYPE_HEADER.toUpperCase().replaceAll("-", "_")] = "stack-analysis";
|
|
23
28
|
let startTime = new Date();
|
|
@@ -36,9 +41,7 @@ async function requestStack(provider, manifest, url, html = false, opts = {}) {
|
|
|
36
41
|
body: provided.content
|
|
37
42
|
}, opts);
|
|
38
43
|
const finalUrl = new URL(`${url}/api/v5/analysis`);
|
|
39
|
-
|
|
40
|
-
finalUrl.searchParams.append('recommend', 'false');
|
|
41
|
-
}
|
|
44
|
+
appendAnalysisQueryParams(finalUrl, opts);
|
|
42
45
|
let resp = await fetch(finalUrl, fetchOptions);
|
|
43
46
|
let result;
|
|
44
47
|
if (resp.status === 200) {
|
|
@@ -77,6 +80,9 @@ async function requestStack(provider, manifest, url, html = false, opts = {}) {
|
|
|
77
80
|
async function requestComponent(provider, manifest, url, opts = {}) {
|
|
78
81
|
opts["source-manifest"] = Buffer.from(fs.readFileSync(manifest).toString()).toString('base64');
|
|
79
82
|
let provided = await provider.provideComponent(manifest, opts); // throws error if content providing failed
|
|
83
|
+
if (provided.batch) {
|
|
84
|
+
return requestStackBatch(JSON.parse(provided.content), url, false, opts);
|
|
85
|
+
}
|
|
80
86
|
opts["source-manifest"] = "";
|
|
81
87
|
opts[TRUSTIFY_DA_OPERATION_TYPE_HEADER.toUpperCase().replaceAll("-", "_")] = "component-analysis";
|
|
82
88
|
if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
|
|
@@ -93,9 +99,7 @@ async function requestComponent(provider, manifest, url, opts = {}) {
|
|
|
93
99
|
body: provided.content
|
|
94
100
|
}, opts);
|
|
95
101
|
const finalUrl = new URL(`${url}/api/v5/analysis`);
|
|
96
|
-
|
|
97
|
-
finalUrl.searchParams.append('recommend', 'false');
|
|
98
|
-
}
|
|
102
|
+
appendAnalysisQueryParams(finalUrl, opts);
|
|
99
103
|
let resp = await fetch(finalUrl, fetchOptions);
|
|
100
104
|
let result;
|
|
101
105
|
if (resp.status === 200) {
|
|
@@ -134,14 +138,12 @@ async function requestComponent(provider, manifest, url, opts = {}) {
|
|
|
134
138
|
*/
|
|
135
139
|
async function requestStackBatch(sbomByPurl, url, html = false, opts = {}) {
|
|
136
140
|
const finalUrl = new URL(`${url}/api/v5/batch-analysis`);
|
|
137
|
-
|
|
138
|
-
finalUrl.searchParams.append('recommend', 'false');
|
|
139
|
-
}
|
|
141
|
+
appendAnalysisQueryParams(finalUrl, opts);
|
|
140
142
|
const fetchOptions = addProxyAgent({
|
|
141
143
|
method: 'POST',
|
|
142
144
|
headers: {
|
|
143
145
|
'Accept': html ? 'text/html' : 'application/json',
|
|
144
|
-
'Content-Type':
|
|
146
|
+
'Content-Type': CYCLONEDX_JSON_MEDIA_TYPE,
|
|
145
147
|
...getTokenHeaders(opts)
|
|
146
148
|
},
|
|
147
149
|
body: JSON.stringify(sbomByPurl)
|
|
@@ -184,18 +186,17 @@ async function requestImages(imageRefs, url, html = false, opts = {}) {
|
|
|
184
186
|
imageSboms[parsedImageRef.getPackageURL().toString()] = generateImageSBOM(parsedImageRef, opts);
|
|
185
187
|
}
|
|
186
188
|
const finalUrl = new URL(`${url}/api/v5/batch-analysis`);
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
const resp = await fetch(finalUrl, {
|
|
189
|
+
appendAnalysisQueryParams(finalUrl, opts);
|
|
190
|
+
const fetchOptions = addProxyAgent({
|
|
191
191
|
method: 'POST',
|
|
192
192
|
headers: {
|
|
193
193
|
'Accept': html ? 'text/html' : 'application/json',
|
|
194
|
-
'Content-Type':
|
|
194
|
+
'Content-Type': CYCLONEDX_JSON_MEDIA_TYPE,
|
|
195
195
|
...getTokenHeaders(opts)
|
|
196
196
|
},
|
|
197
197
|
body: JSON.stringify(imageSboms),
|
|
198
|
-
});
|
|
198
|
+
}, opts);
|
|
199
|
+
const resp = await fetch(finalUrl, fetchOptions);
|
|
199
200
|
if (resp.status === 200) {
|
|
200
201
|
let result;
|
|
201
202
|
if (!html) {
|
|
@@ -219,6 +220,24 @@ async function requestImages(imageRefs, url, html = false, opts = {}) {
|
|
|
219
220
|
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()}`);
|
|
220
221
|
}
|
|
221
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Append common analysis query parameters (recommend, providers, sources) to a URL.
|
|
225
|
+
* @param {URL} url - the URL object to append query parameters to
|
|
226
|
+
* @param {import("index.js").Options} [opts={}] - options containing parameter overrides
|
|
227
|
+
*/
|
|
228
|
+
function appendAnalysisQueryParams(url, opts = {}) {
|
|
229
|
+
if (getCustom('TRUSTIFY_DA_RECOMMEND', 'true', opts) === 'false') {
|
|
230
|
+
url.searchParams.append('recommend', 'false');
|
|
231
|
+
}
|
|
232
|
+
const providers = getCustom('TRUSTIFY_DA_PROVIDERS', null, opts);
|
|
233
|
+
if (providers) {
|
|
234
|
+
url.searchParams.append('providers', providers);
|
|
235
|
+
}
|
|
236
|
+
const sources = getCustom('TRUSTIFY_DA_SOURCES', null, opts);
|
|
237
|
+
if (sources) {
|
|
238
|
+
url.searchParams.append('sources', sources);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
222
241
|
/**
|
|
223
242
|
*
|
|
224
243
|
* @param url the backend url to send the request to
|
package/dist/src/cli.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
2
3
|
import * as path from "path";
|
|
3
4
|
import yargs from 'yargs';
|
|
4
5
|
import { hideBin } from 'yargs/helpers';
|
|
5
6
|
import { getProjectLicense, getLicenseDetails } from './license/index.js';
|
|
6
|
-
import client, { selectTrustifyDABackend } from './index.js';
|
|
7
|
+
import client, { selectTrustifyDABackend, generateSbom } from './index.js';
|
|
7
8
|
// command for component analysis take manifest type and content
|
|
8
9
|
const component = {
|
|
9
10
|
command: 'component </path/to/manifest>',
|
|
@@ -18,11 +19,25 @@ const component = {
|
|
|
18
19
|
desc: 'Workspace root directory (for monorepos; lock file is expected here)',
|
|
19
20
|
type: 'string',
|
|
20
21
|
normalize: true,
|
|
22
|
+
},
|
|
23
|
+
providers: {
|
|
24
|
+
desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
|
|
25
|
+
type: 'string',
|
|
26
|
+
},
|
|
27
|
+
sources: {
|
|
28
|
+
desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
|
|
29
|
+
type: 'string',
|
|
21
30
|
}
|
|
22
31
|
}),
|
|
23
32
|
handler: async (args) => {
|
|
24
33
|
let manifestName = args['/path/to/manifest'];
|
|
25
34
|
const opts = args.workspaceDir ? { TRUSTIFY_DA_WORKSPACE_DIR: args.workspaceDir } : {};
|
|
35
|
+
if (args.providers) {
|
|
36
|
+
opts.TRUSTIFY_DA_PROVIDERS = args.providers;
|
|
37
|
+
}
|
|
38
|
+
if (args.sources) {
|
|
39
|
+
opts.TRUSTIFY_DA_SOURCES = args.sources;
|
|
40
|
+
}
|
|
26
41
|
let res = await client.componentAnalysis(manifestName, opts);
|
|
27
42
|
console.log(JSON.stringify(res, null, 2));
|
|
28
43
|
}
|
|
@@ -71,6 +86,14 @@ const image = {
|
|
|
71
86
|
desc: 'For JSON report, get only the \'summary\'',
|
|
72
87
|
type: 'boolean',
|
|
73
88
|
conflicts: 'html'
|
|
89
|
+
},
|
|
90
|
+
providers: {
|
|
91
|
+
desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
|
|
92
|
+
type: 'string',
|
|
93
|
+
},
|
|
94
|
+
sources: {
|
|
95
|
+
desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
|
|
96
|
+
type: 'string',
|
|
74
97
|
}
|
|
75
98
|
}),
|
|
76
99
|
handler: async (args) => {
|
|
@@ -80,7 +103,14 @@ const image = {
|
|
|
80
103
|
}
|
|
81
104
|
let html = args['html'];
|
|
82
105
|
let summary = args['summary'];
|
|
83
|
-
|
|
106
|
+
const opts = {};
|
|
107
|
+
if (args.providers) {
|
|
108
|
+
opts.TRUSTIFY_DA_PROVIDERS = args.providers;
|
|
109
|
+
}
|
|
110
|
+
if (args.sources) {
|
|
111
|
+
opts.TRUSTIFY_DA_SOURCES = args.sources;
|
|
112
|
+
}
|
|
113
|
+
let res = await client.imageAnalysis(imageRefs, html, opts);
|
|
84
114
|
if (summary && !html) {
|
|
85
115
|
let summaries = {};
|
|
86
116
|
for (let [imageRef, report] of Object.entries(res)) {
|
|
@@ -131,6 +161,14 @@ const stack = {
|
|
|
131
161
|
desc: 'Workspace root directory (for monorepos; lock file is expected here)',
|
|
132
162
|
type: 'string',
|
|
133
163
|
normalize: true,
|
|
164
|
+
},
|
|
165
|
+
providers: {
|
|
166
|
+
desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
|
|
167
|
+
type: 'string',
|
|
168
|
+
},
|
|
169
|
+
sources: {
|
|
170
|
+
desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
|
|
171
|
+
type: 'string',
|
|
134
172
|
}
|
|
135
173
|
}),
|
|
136
174
|
handler: async (args) => {
|
|
@@ -138,6 +176,12 @@ const stack = {
|
|
|
138
176
|
let html = args['html'];
|
|
139
177
|
let summary = args['summary'];
|
|
140
178
|
const opts = args.workspaceDir ? { TRUSTIFY_DA_WORKSPACE_DIR: args.workspaceDir } : {};
|
|
179
|
+
if (args.providers) {
|
|
180
|
+
opts.TRUSTIFY_DA_PROVIDERS = args.providers;
|
|
181
|
+
}
|
|
182
|
+
if (args.sources) {
|
|
183
|
+
opts.TRUSTIFY_DA_SOURCES = args.sources;
|
|
184
|
+
}
|
|
141
185
|
let theProvidersSummary = new Map();
|
|
142
186
|
let theProvidersObject = {};
|
|
143
187
|
let res = await client.stackAnalysis(manifest, html, opts);
|
|
@@ -200,6 +244,14 @@ const stackBatch = {
|
|
|
200
244
|
desc: 'Stop on first invalid package.json or SBOM error (env: TRUSTIFY_DA_CONTINUE_ON_ERROR=false)',
|
|
201
245
|
type: 'boolean',
|
|
202
246
|
default: false,
|
|
247
|
+
},
|
|
248
|
+
providers: {
|
|
249
|
+
desc: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)',
|
|
250
|
+
type: 'string',
|
|
251
|
+
},
|
|
252
|
+
sources: {
|
|
253
|
+
desc: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)',
|
|
254
|
+
type: 'string',
|
|
203
255
|
}
|
|
204
256
|
}),
|
|
205
257
|
handler: async (args) => {
|
|
@@ -220,6 +272,12 @@ const stackBatch = {
|
|
|
220
272
|
if (args.failFast) {
|
|
221
273
|
opts.continueOnError = false;
|
|
222
274
|
}
|
|
275
|
+
if (args.providers) {
|
|
276
|
+
opts.TRUSTIFY_DA_PROVIDERS = args.providers;
|
|
277
|
+
}
|
|
278
|
+
if (args.sources) {
|
|
279
|
+
opts.TRUSTIFY_DA_SOURCES = args.sources;
|
|
280
|
+
}
|
|
223
281
|
let res = await client.stackAnalysisBatch(workspaceRoot, html, opts);
|
|
224
282
|
const batchAnalysis = res && typeof res === 'object' && res != null && 'analysis' in res ? res.analysis : res;
|
|
225
283
|
if (summary && !html && typeof batchAnalysis === 'object') {
|
|
@@ -325,15 +383,63 @@ const license = {
|
|
|
325
383
|
console.log(JSON.stringify(output, null, 2));
|
|
326
384
|
}
|
|
327
385
|
};
|
|
386
|
+
const sbom = {
|
|
387
|
+
command: 'sbom </path/to/manifest> [--output]',
|
|
388
|
+
desc: 'generate a CycloneDX SBOM from a manifest file',
|
|
389
|
+
builder: yargs => yargs.positional('/path/to/manifest', {
|
|
390
|
+
desc: 'manifest path for SBOM generation',
|
|
391
|
+
type: 'string',
|
|
392
|
+
normalize: true,
|
|
393
|
+
}).options({
|
|
394
|
+
output: {
|
|
395
|
+
alias: 'o',
|
|
396
|
+
desc: 'Write SBOM JSON to a file instead of stdout',
|
|
397
|
+
type: 'string',
|
|
398
|
+
normalize: true,
|
|
399
|
+
},
|
|
400
|
+
workspaceDir: {
|
|
401
|
+
alias: 'w',
|
|
402
|
+
desc: 'Workspace root directory (for monorepos; lock file is expected here)',
|
|
403
|
+
type: 'string',
|
|
404
|
+
normalize: true,
|
|
405
|
+
}
|
|
406
|
+
}),
|
|
407
|
+
handler: async (args) => {
|
|
408
|
+
let manifest = args['/path/to/manifest'];
|
|
409
|
+
const opts = args.workspaceDir ? { TRUSTIFY_DA_WORKSPACE_DIR: args.workspaceDir } : {};
|
|
410
|
+
let result;
|
|
411
|
+
try {
|
|
412
|
+
result = await generateSbom(manifest, opts);
|
|
413
|
+
}
|
|
414
|
+
catch (err) {
|
|
415
|
+
console.error(JSON.stringify({ error: `Failed to generate SBOM: ${err.message}` }, null, 2));
|
|
416
|
+
process.exit(1);
|
|
417
|
+
}
|
|
418
|
+
const json = JSON.stringify(result, null, 2);
|
|
419
|
+
if (args.output) {
|
|
420
|
+
try {
|
|
421
|
+
fs.writeFileSync(args.output, json);
|
|
422
|
+
}
|
|
423
|
+
catch (err) {
|
|
424
|
+
console.error(JSON.stringify({ error: `Failed to write output file: ${err.message}` }, null, 2));
|
|
425
|
+
process.exit(1);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
console.log(json);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
};
|
|
328
433
|
// parse and invoke the command
|
|
329
434
|
yargs(hideBin(process.argv))
|
|
330
|
-
.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}`)
|
|
435
|
+
.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}`)
|
|
331
436
|
.command(stack)
|
|
332
437
|
.command(stackBatch)
|
|
333
438
|
.command(component)
|
|
334
439
|
.command(image)
|
|
335
440
|
.command(validateToken)
|
|
336
441
|
.command(license)
|
|
442
|
+
.command(sbom)
|
|
337
443
|
.scriptName('')
|
|
338
444
|
.version(false)
|
|
339
445
|
.demandCommand(1)
|