owasp-depscan 5.0.1__tar.gz → 5.0.3__tar.gz
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.
Potentially problematic release.
This version of owasp-depscan might be problematic. Click here for more details.
- {owasp-depscan-5.0.1/owasp_depscan.egg-info → owasp-depscan-5.0.3}/PKG-INFO +49 -10
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/README.md +46 -8
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/cli.py +63 -15
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/analysis.py +10 -4
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/audit.py +3 -1
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/csaf.py +30 -17
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/normalize.py +4 -4
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/utils.py +25 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3/owasp_depscan.egg-info}/PKG-INFO +49 -10
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/owasp_depscan.egg-info/requires.txt +2 -1
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/pyproject.toml +3 -2
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/test/test_csaf.py +319 -487
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/test/test_utils.py +45 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/LICENSE +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/MANIFEST.in +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/__init__.py +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/__init__.py +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/bom.py +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/config.py +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/explainer.py +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/github.py +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/license.py +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/logger.py +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/pkg_query.py +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/owasp_depscan.egg-info/SOURCES.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/owasp_depscan.egg-info/dependency_links.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/owasp_depscan.egg-info/entry_points.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/owasp_depscan.egg-info/top_level.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/setup.cfg +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/test/test_analysis.py +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/test/test_bom.py +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/test/test_explainer.py +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/test/test_github.py +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/test/test_license.py +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/test/test_norm.py +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/test/test_pkg_query.py +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/__init__.py +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_data/fields.yml +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_data/meta.yml +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_data/rules.yml +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/0bsd.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/afl-3.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/agpl-3.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/apache-2.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/artistic-2.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/bsd-2-clause.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/bsd-3-clause-clear.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/bsd-3-clause.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/bsd-4-clause.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/bsl-1.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/cc-by-4.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/cc-by-sa-4.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/cc0-1.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/cecill-2.1.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/cern-ohl-p-2.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/cern-ohl-s-2.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/cern-ohl-w-2.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/ecl-2.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/epl-1.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/epl-2.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/eupl-1.1.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/eupl-1.2.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/gfdl-1.3.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/gpl-2.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/gpl-3.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/isc.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/lgpl-2.1.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/lgpl-3.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/lppl-1.3c.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/mit-0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/mit.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/mpl-2.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/ms-pl.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/ms-rl.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/mulanpsl-2.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/ncsa.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/odbl-1.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/ofl-1.1.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/osl-3.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/postgresql.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/unlicense.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/upl-1.0.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/vim.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/wtfpl.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/zlib.txt +0 -0
- {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/spdx/json/licenses.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: owasp-depscan
|
|
3
|
-
Version: 5.0.
|
|
3
|
+
Version: 5.0.3
|
|
4
4
|
Summary: Fully open-source security audit for project dependencies based on known vulnerabilities and advisories.
|
|
5
5
|
Author-email: Team AppThreat <cloud@appthreat.com>
|
|
6
6
|
License: MIT
|
|
@@ -20,7 +20,7 @@ Classifier: Topic :: Utilities
|
|
|
20
20
|
Requires-Python: >=3.8
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
|
-
Requires-Dist: appthreat-vulnerability-db>=5.5.
|
|
23
|
+
Requires-Dist: appthreat-vulnerability-db>=5.5.5
|
|
24
24
|
Requires-Dist: defusedxml
|
|
25
25
|
Requires-Dist: oras
|
|
26
26
|
Requires-Dist: PyYAML
|
|
@@ -29,6 +29,7 @@ Requires-Dist: quart
|
|
|
29
29
|
Requires-Dist: PyGithub
|
|
30
30
|
Requires-Dist: toml
|
|
31
31
|
Requires-Dist: pdfkit
|
|
32
|
+
Requires-Dist: Jinja2
|
|
32
33
|
Provides-Extra: dev
|
|
33
34
|
Requires-Dist: black; extra == "dev"
|
|
34
35
|
Requires-Dist: flake8; extra == "dev"
|
|
@@ -91,7 +92,7 @@ dep-scan is ideal for use during continuous integration (CI) and as a local deve
|
|
|
91
92
|
|
|
92
93
|
### OCI Artifacts via ORAS cli
|
|
93
94
|
|
|
94
|
-
Use [ORAS cli](https://oras.land/docs/) to download the
|
|
95
|
+
Use [ORAS cli](https://oras.land/docs/) to download the vulnerability database for effortless integration. Example workflow is [here](https://github.com/owasp-dep-scan/dep-scan/blob/master/.github/workflows/gobintests.yml#L44-L53).
|
|
95
96
|
|
|
96
97
|
```bash
|
|
97
98
|
export VDB_HOME=depscan
|
|
@@ -146,22 +147,26 @@ Use the `/scan` endpoint to perform scans.
|
|
|
146
147
|
> [!NOTE]
|
|
147
148
|
> The `type` parameter is mandatory in server mode.
|
|
148
149
|
|
|
149
|
-
|
|
150
|
+
- Scanning a local directory.
|
|
151
|
+
|
|
150
152
|
```bash
|
|
151
153
|
curl --json '{"path": "/tmp/vulnerable-aws-koa-app", "type": "js"}' http://0.0.0.0:7070/scan
|
|
152
154
|
```
|
|
153
155
|
|
|
154
|
-
|
|
156
|
+
- Scanning a SBOM file (present locally).
|
|
157
|
+
|
|
155
158
|
```bash
|
|
156
159
|
curl --json '{"path": "/tmp/vulnerable-aws-koa-app/sbom_file.json", "type": "js"}' http://0.0.0.0:7070/scan
|
|
157
160
|
```
|
|
158
161
|
|
|
159
|
-
|
|
162
|
+
- Scanning a GitHub repo.
|
|
163
|
+
|
|
160
164
|
```bash
|
|
161
165
|
curl --json '{"url": "https://github.com/HooliCorp/vulnerable-aws-koa-app", "type": "js"}' http://0.0.0.0:7070/scan -o app.vdr.json
|
|
162
166
|
```
|
|
163
167
|
|
|
164
|
-
|
|
168
|
+
- Uploading a SBOM file and generating results based on it.
|
|
169
|
+
|
|
165
170
|
```bash
|
|
166
171
|
curl -X POST -H 'Content-Type: multipart/form-data' -F 'file=@/tmp/app/sbom_file.json' http://0.0.0.0:7070/scan?type=js
|
|
167
172
|
```
|
|
@@ -185,9 +190,10 @@ depscan --src $PWD --reports-dir $PWD/reports
|
|
|
185
190
|
The full list of options is below:
|
|
186
191
|
|
|
187
192
|
```bash
|
|
188
|
-
usage: cli.py [-h] [--no-banner] [--cache] [--csaf] [--sync] [--profile {appsec,research,operational,threat-modeling,license-compliance,generic}] [--no-suggest] [--risk-audit] [--private-ns PRIVATE_NS] [-t PROJECT_TYPE] [--bom BOM]
|
|
189
|
-
[
|
|
190
|
-
[--server
|
|
193
|
+
usage: cli.py [-h] [--no-banner] [--cache] [--csaf] [--sync] [--profile {appsec,research,operational,threat-modeling,license-compliance,generic}] [--no-suggest] [--risk-audit] [--private-ns PRIVATE_NS] [-t PROJECT_TYPE] [--bom BOM]
|
|
194
|
+
[-i SRC_DIR_IMAGE] [-o REPORT_FILE] [--reports-dir REPORTS_DIR] [--report-template REPORT_TEMPLATE] [--report-name REPORT_NAME] [--no-error] [--no-license-scan] [--deep] [--no-universal] [--no-vuln-table]
|
|
195
|
+
[--threatdb-server THREATDB_SERVER] [--threatdb-username THREATDB_USERNAME] [--threatdb-password THREATDB_PASSWORD] [--threatdb-token THREATDB_TOKEN] [--server] [--server-host SERVER_HOST] [--server-port SERVER_PORT]
|
|
196
|
+
[--cdxgen-server CDXGEN_SERVER] [--debug] [--explain] [--reachables-slices-file REACHABLES_SLICES_FILE] [-v]
|
|
191
197
|
|
|
192
198
|
Fully open-source security and license audit for application dependencies and container images based on known vulnerabilities and advisories.
|
|
193
199
|
|
|
@@ -212,6 +218,12 @@ options:
|
|
|
212
218
|
DEPRECATED. Use reports directory since multiple files are created. Report filename with directory
|
|
213
219
|
--reports-dir REPORTS_DIR
|
|
214
220
|
Reports directory
|
|
221
|
+
--report-template REPORT_TEMPLATE
|
|
222
|
+
Jinja template file used for rendering a custom report
|
|
223
|
+
--report-name REPORT_NAME
|
|
224
|
+
Filename of the custom report written to the --reports-dir
|
|
225
|
+
--no-error UNUSED: Continue on error to prevent build from breaking
|
|
226
|
+
--no-license-scan UNUSED: dep-scan doesn't perform license scanning by default
|
|
215
227
|
--deep Perform deep scan by passing this --deep argument to cdxgen. Useful while scanning docker images and OS packages.
|
|
216
228
|
--no-universal Depscan would attempt to perform a single universal scan instead of individual scans per language type.
|
|
217
229
|
--no-vuln-table Do not print the table with the full list of vulnerabilities. This can help reduce console output.
|
|
@@ -234,6 +246,7 @@ options:
|
|
|
234
246
|
--explain Makes depscan to explain the various analysis. Useful for creating detailed reports.
|
|
235
247
|
--reachables-slices-file REACHABLES_SLICES_FILE
|
|
236
248
|
Path for the reachables slices file created by atom.
|
|
249
|
+
--purl SEARCH_PURL Scan a single package url.
|
|
237
250
|
-v, --version Display the version
|
|
238
251
|
```
|
|
239
252
|
|
|
@@ -440,6 +453,32 @@ dep-scan could auto-detect most cloud applications and Kubernetes manifest files
|
|
|
440
453
|
|
|
441
454
|
Ensure [wkhtmltopdf](https://wkhtmltopdf.org/downloads.html) is installed or use the official container image to generate pdf reports. Use with `--explain` for more detailed reports.
|
|
442
455
|
|
|
456
|
+
## Custom reports
|
|
457
|
+
|
|
458
|
+
dep-scan can be provided with a [Jinja](https://jinja.palletsprojects.com/en/3.1.x/) template using the `--report-template` parameter.
|
|
459
|
+
Giving it will pass the vulnerability report into your template for rendering the report.
|
|
460
|
+
|
|
461
|
+
Please find a basic example here:
|
|
462
|
+
|
|
463
|
+
```jinja
|
|
464
|
+
There were {{ vulnerabilities | length }} issues identified:
|
|
465
|
+
|
|
466
|
+
{% for vuln in vulnerabilities -%}
|
|
467
|
+
* {{ vuln.id }} - {{ vuln.package }}
|
|
468
|
+
{% endfor %}
|
|
469
|
+
|
|
470
|
+
Severity counts:
|
|
471
|
+
* Low: {{ summary.LOW }}
|
|
472
|
+
* Medium: {{ summary.MEDIUM }}
|
|
473
|
+
* High: {{ summary.HIGH }}
|
|
474
|
+
* Critical: {{ summary.CRITICAL }}
|
|
475
|
+
* Unspecified: {{ summary.UNSPECIFIED }}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
The `vulnerabilities` object is the same list that can be found in the `depscan-bom.json` report file,
|
|
479
|
+
`summary` is a dictionary type with vulnerability severity quantities as shown in the example above.
|
|
480
|
+
Furthermore insights are imaginably to be made available to the template, please reach out or contribute on demand.
|
|
481
|
+
|
|
443
482
|
## Discord support
|
|
444
483
|
|
|
445
484
|
The developers could be reached via the [discord](https://discord.gg/DCNxzaeUpd) channel for enterprise support.
|
|
@@ -53,7 +53,7 @@ dep-scan is ideal for use during continuous integration (CI) and as a local deve
|
|
|
53
53
|
|
|
54
54
|
### OCI Artifacts via ORAS cli
|
|
55
55
|
|
|
56
|
-
Use [ORAS cli](https://oras.land/docs/) to download the
|
|
56
|
+
Use [ORAS cli](https://oras.land/docs/) to download the vulnerability database for effortless integration. Example workflow is [here](https://github.com/owasp-dep-scan/dep-scan/blob/master/.github/workflows/gobintests.yml#L44-L53).
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
59
|
export VDB_HOME=depscan
|
|
@@ -108,22 +108,26 @@ Use the `/scan` endpoint to perform scans.
|
|
|
108
108
|
> [!NOTE]
|
|
109
109
|
> The `type` parameter is mandatory in server mode.
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
- Scanning a local directory.
|
|
112
|
+
|
|
112
113
|
```bash
|
|
113
114
|
curl --json '{"path": "/tmp/vulnerable-aws-koa-app", "type": "js"}' http://0.0.0.0:7070/scan
|
|
114
115
|
```
|
|
115
116
|
|
|
116
|
-
|
|
117
|
+
- Scanning a SBOM file (present locally).
|
|
118
|
+
|
|
117
119
|
```bash
|
|
118
120
|
curl --json '{"path": "/tmp/vulnerable-aws-koa-app/sbom_file.json", "type": "js"}' http://0.0.0.0:7070/scan
|
|
119
121
|
```
|
|
120
122
|
|
|
121
|
-
|
|
123
|
+
- Scanning a GitHub repo.
|
|
124
|
+
|
|
122
125
|
```bash
|
|
123
126
|
curl --json '{"url": "https://github.com/HooliCorp/vulnerable-aws-koa-app", "type": "js"}' http://0.0.0.0:7070/scan -o app.vdr.json
|
|
124
127
|
```
|
|
125
128
|
|
|
126
|
-
|
|
129
|
+
- Uploading a SBOM file and generating results based on it.
|
|
130
|
+
|
|
127
131
|
```bash
|
|
128
132
|
curl -X POST -H 'Content-Type: multipart/form-data' -F 'file=@/tmp/app/sbom_file.json' http://0.0.0.0:7070/scan?type=js
|
|
129
133
|
```
|
|
@@ -147,9 +151,10 @@ depscan --src $PWD --reports-dir $PWD/reports
|
|
|
147
151
|
The full list of options is below:
|
|
148
152
|
|
|
149
153
|
```bash
|
|
150
|
-
usage: cli.py [-h] [--no-banner] [--cache] [--csaf] [--sync] [--profile {appsec,research,operational,threat-modeling,license-compliance,generic}] [--no-suggest] [--risk-audit] [--private-ns PRIVATE_NS] [-t PROJECT_TYPE] [--bom BOM]
|
|
151
|
-
[
|
|
152
|
-
[--server
|
|
154
|
+
usage: cli.py [-h] [--no-banner] [--cache] [--csaf] [--sync] [--profile {appsec,research,operational,threat-modeling,license-compliance,generic}] [--no-suggest] [--risk-audit] [--private-ns PRIVATE_NS] [-t PROJECT_TYPE] [--bom BOM]
|
|
155
|
+
[-i SRC_DIR_IMAGE] [-o REPORT_FILE] [--reports-dir REPORTS_DIR] [--report-template REPORT_TEMPLATE] [--report-name REPORT_NAME] [--no-error] [--no-license-scan] [--deep] [--no-universal] [--no-vuln-table]
|
|
156
|
+
[--threatdb-server THREATDB_SERVER] [--threatdb-username THREATDB_USERNAME] [--threatdb-password THREATDB_PASSWORD] [--threatdb-token THREATDB_TOKEN] [--server] [--server-host SERVER_HOST] [--server-port SERVER_PORT]
|
|
157
|
+
[--cdxgen-server CDXGEN_SERVER] [--debug] [--explain] [--reachables-slices-file REACHABLES_SLICES_FILE] [-v]
|
|
153
158
|
|
|
154
159
|
Fully open-source security and license audit for application dependencies and container images based on known vulnerabilities and advisories.
|
|
155
160
|
|
|
@@ -174,6 +179,12 @@ options:
|
|
|
174
179
|
DEPRECATED. Use reports directory since multiple files are created. Report filename with directory
|
|
175
180
|
--reports-dir REPORTS_DIR
|
|
176
181
|
Reports directory
|
|
182
|
+
--report-template REPORT_TEMPLATE
|
|
183
|
+
Jinja template file used for rendering a custom report
|
|
184
|
+
--report-name REPORT_NAME
|
|
185
|
+
Filename of the custom report written to the --reports-dir
|
|
186
|
+
--no-error UNUSED: Continue on error to prevent build from breaking
|
|
187
|
+
--no-license-scan UNUSED: dep-scan doesn't perform license scanning by default
|
|
177
188
|
--deep Perform deep scan by passing this --deep argument to cdxgen. Useful while scanning docker images and OS packages.
|
|
178
189
|
--no-universal Depscan would attempt to perform a single universal scan instead of individual scans per language type.
|
|
179
190
|
--no-vuln-table Do not print the table with the full list of vulnerabilities. This can help reduce console output.
|
|
@@ -196,6 +207,7 @@ options:
|
|
|
196
207
|
--explain Makes depscan to explain the various analysis. Useful for creating detailed reports.
|
|
197
208
|
--reachables-slices-file REACHABLES_SLICES_FILE
|
|
198
209
|
Path for the reachables slices file created by atom.
|
|
210
|
+
--purl SEARCH_PURL Scan a single package url.
|
|
199
211
|
-v, --version Display the version
|
|
200
212
|
```
|
|
201
213
|
|
|
@@ -402,6 +414,32 @@ dep-scan could auto-detect most cloud applications and Kubernetes manifest files
|
|
|
402
414
|
|
|
403
415
|
Ensure [wkhtmltopdf](https://wkhtmltopdf.org/downloads.html) is installed or use the official container image to generate pdf reports. Use with `--explain` for more detailed reports.
|
|
404
416
|
|
|
417
|
+
## Custom reports
|
|
418
|
+
|
|
419
|
+
dep-scan can be provided with a [Jinja](https://jinja.palletsprojects.com/en/3.1.x/) template using the `--report-template` parameter.
|
|
420
|
+
Giving it will pass the vulnerability report into your template for rendering the report.
|
|
421
|
+
|
|
422
|
+
Please find a basic example here:
|
|
423
|
+
|
|
424
|
+
```jinja
|
|
425
|
+
There were {{ vulnerabilities | length }} issues identified:
|
|
426
|
+
|
|
427
|
+
{% for vuln in vulnerabilities -%}
|
|
428
|
+
* {{ vuln.id }} - {{ vuln.package }}
|
|
429
|
+
{% endfor %}
|
|
430
|
+
|
|
431
|
+
Severity counts:
|
|
432
|
+
* Low: {{ summary.LOW }}
|
|
433
|
+
* Medium: {{ summary.MEDIUM }}
|
|
434
|
+
* High: {{ summary.HIGH }}
|
|
435
|
+
* Critical: {{ summary.CRITICAL }}
|
|
436
|
+
* Unspecified: {{ summary.UNSPECIFIED }}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
The `vulnerabilities` object is the same list that can be found in the `depscan-bom.json` report file,
|
|
440
|
+
`summary` is a dictionary type with vulnerability severity quantities as shown in the example above.
|
|
441
|
+
Furthermore insights are imaginably to be made available to the template, please reach out or contribute on demand.
|
|
442
|
+
|
|
405
443
|
## Discord support
|
|
406
444
|
|
|
407
445
|
The developers could be reached via the [discord](https://discord.gg/DCNxzaeUpd) channel for enterprise support.
|
|
@@ -181,6 +181,17 @@ def build_args():
|
|
|
181
181
|
dest="reports_dir",
|
|
182
182
|
help="Reports directory",
|
|
183
183
|
)
|
|
184
|
+
parser.add_argument(
|
|
185
|
+
"--report-template",
|
|
186
|
+
dest="report_template",
|
|
187
|
+
help="Jinja template file used for rendering a custom report",
|
|
188
|
+
)
|
|
189
|
+
parser.add_argument(
|
|
190
|
+
"--report-name",
|
|
191
|
+
default="rendered.report",
|
|
192
|
+
dest="report_name",
|
|
193
|
+
help="Filename of the custom report written to the --reports-dir",
|
|
194
|
+
)
|
|
184
195
|
parser.add_argument(
|
|
185
196
|
"--no-error",
|
|
186
197
|
action="store_true",
|
|
@@ -287,6 +298,11 @@ def build_args():
|
|
|
287
298
|
dest="reachables_slices_file",
|
|
288
299
|
help="Path for the reachables slices file created by atom.",
|
|
289
300
|
)
|
|
301
|
+
parser.add_argument(
|
|
302
|
+
"--purl",
|
|
303
|
+
dest="search_purl",
|
|
304
|
+
help="Scan a single package url.",
|
|
305
|
+
)
|
|
290
306
|
parser.add_argument(
|
|
291
307
|
"-v",
|
|
292
308
|
"--version",
|
|
@@ -437,7 +453,7 @@ def summarise(
|
|
|
437
453
|
reached_purls=reached_purls,
|
|
438
454
|
)
|
|
439
455
|
pkg_vulnerabilities, pkg_group_rows = prepare_vdr(options)
|
|
440
|
-
vdr_file = bom_file.replace(".json", ".vdr.json")
|
|
456
|
+
vdr_file = bom_file.replace(".json", ".vdr.json") if bom_file else None
|
|
441
457
|
if pkg_vulnerabilities and bom_file:
|
|
442
458
|
try:
|
|
443
459
|
with open(bom_file, encoding="utf-8") as fp:
|
|
@@ -497,9 +513,15 @@ def download_rafs_based_image():
|
|
|
497
513
|
target=vdb_rafs_database_url, outdir=rafs_data_dir.name
|
|
498
514
|
)
|
|
499
515
|
|
|
500
|
-
if
|
|
501
|
-
|
|
502
|
-
|
|
516
|
+
if (
|
|
517
|
+
paths_list
|
|
518
|
+
and os.path.exists(
|
|
519
|
+
os.path.join(rafs_data_dir.name, "data.rafs")
|
|
520
|
+
)
|
|
521
|
+
and os.path.exists(
|
|
522
|
+
os.path.join(rafs_data_dir.name, "meta.rafs")
|
|
523
|
+
)
|
|
524
|
+
):
|
|
503
525
|
nydus_download_command = [
|
|
504
526
|
f"{nydus_image_command}",
|
|
505
527
|
"unpack",
|
|
@@ -529,7 +551,8 @@ def download_rafs_based_image():
|
|
|
529
551
|
|
|
530
552
|
except Exception:
|
|
531
553
|
LOG.info(
|
|
532
|
-
"Unable to pull the vulnerability database (rafs image) from %s. Trying to pull the non-rafs-based VDB image.",
|
|
554
|
+
"Unable to pull the vulnerability database (rafs image) from %s. Trying to pull the non-rafs-based VDB image.",
|
|
555
|
+
vdb_rafs_database_url,
|
|
533
556
|
)
|
|
534
557
|
rafs_image_downloaded = False
|
|
535
558
|
|
|
@@ -808,6 +831,12 @@ def main():
|
|
|
808
831
|
# Detect the project types and perform the right type of scan
|
|
809
832
|
if args.project_type:
|
|
810
833
|
project_types_list = args.project_type.split(",")
|
|
834
|
+
elif args.search_purl:
|
|
835
|
+
purl_obj = parse_purl(args.search_purl)
|
|
836
|
+
purl_obj["purl"] = args.search_purl
|
|
837
|
+
purl_obj["vendor"] = purl_obj.get("namespace")
|
|
838
|
+
project_types_list = [purl_obj.get("type")]
|
|
839
|
+
pkg_list = [purl_obj]
|
|
811
840
|
elif args.bom and not args.project_type:
|
|
812
841
|
project_types_list = ["bom"]
|
|
813
842
|
elif not args.non_universal_scan:
|
|
@@ -846,7 +875,12 @@ def main():
|
|
|
846
875
|
risk_report_file = areport_file.replace(
|
|
847
876
|
".json", f"-risk.{project_type}.json"
|
|
848
877
|
)
|
|
849
|
-
|
|
878
|
+
# Are we scanning a single purl
|
|
879
|
+
if args.search_purl:
|
|
880
|
+
bom_file = None
|
|
881
|
+
creation_status = True
|
|
882
|
+
# Are we scanning a bom file
|
|
883
|
+
elif args.bom and os.path.exists(args.bom):
|
|
850
884
|
bom_file = args.bom
|
|
851
885
|
creation_status = True
|
|
852
886
|
else:
|
|
@@ -865,14 +899,15 @@ def main():
|
|
|
865
899
|
if not creation_status:
|
|
866
900
|
LOG.debug("Bom file %s was not created successfully", bom_file)
|
|
867
901
|
continue
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
902
|
+
if bom_file:
|
|
903
|
+
LOG.debug("Scanning using the bom file %s", bom_file)
|
|
904
|
+
if not args.bom:
|
|
905
|
+
LOG.info(
|
|
906
|
+
"To improve performance, cache the bom file and invoke "
|
|
907
|
+
"depscan with --bom %s instead of -i",
|
|
908
|
+
bom_file,
|
|
909
|
+
)
|
|
910
|
+
pkg_list = get_pkg_list(bom_file)
|
|
876
911
|
if not pkg_list:
|
|
877
912
|
LOG.debug("No packages found in the project!")
|
|
878
913
|
continue
|
|
@@ -1035,7 +1070,7 @@ def main():
|
|
|
1035
1070
|
bom_file, src_dir, args.reachables_slices_file
|
|
1036
1071
|
)
|
|
1037
1072
|
# Summarise and print results
|
|
1038
|
-
|
|
1073
|
+
summary, vdr_file, pkg_vulnerabilities, pkg_group_rows = summarise(
|
|
1039
1074
|
project_type,
|
|
1040
1075
|
results,
|
|
1041
1076
|
pkg_aliases,
|
|
@@ -1077,6 +1112,19 @@ def main():
|
|
|
1077
1112
|
else DEFAULT_TERMINAL_THEME,
|
|
1078
1113
|
)
|
|
1079
1114
|
utils.export_pdf(html_file, pdf_file)
|
|
1115
|
+
# render report into template if wished
|
|
1116
|
+
if args.report_template and os.path.isfile(args.report_template):
|
|
1117
|
+
utils.render_template_report(
|
|
1118
|
+
jsonl_report_file=report_file,
|
|
1119
|
+
summary=summary,
|
|
1120
|
+
template_file=args.report_template,
|
|
1121
|
+
result_file=os.path.join(reports_dir, args.report_name),
|
|
1122
|
+
)
|
|
1123
|
+
elif args.report_template:
|
|
1124
|
+
LOG.warning(
|
|
1125
|
+
"Template file %s doesn't exist, custom report not created.",
|
|
1126
|
+
args.report_template,
|
|
1127
|
+
)
|
|
1080
1128
|
# Submit vdr/vex files to threatdb server
|
|
1081
1129
|
if args.threatdb_server and (args.threatdb_username or args.threatdb_token):
|
|
1082
1130
|
submit_bom(
|
|
@@ -11,7 +11,7 @@ from rich.style import Style
|
|
|
11
11
|
from rich.table import Table
|
|
12
12
|
from rich.tree import Tree
|
|
13
13
|
from vdb.lib import CPE_FULL_REGEX
|
|
14
|
-
from vdb.lib.config import placeholder_fix_version
|
|
14
|
+
from vdb.lib.config import placeholder_exclude_version, placeholder_fix_version
|
|
15
15
|
from vdb.lib.utils import parse_cpe, parse_purl
|
|
16
16
|
|
|
17
17
|
from depscan.lib import config
|
|
@@ -84,7 +84,7 @@ def retrieve_bom_dependency_tree(bom_file):
|
|
|
84
84
|
:return: Dependency tree as a list
|
|
85
85
|
"""
|
|
86
86
|
if not bom_file:
|
|
87
|
-
return []
|
|
87
|
+
return [], None
|
|
88
88
|
try:
|
|
89
89
|
with open(bom_file, encoding="utf-8") as bfp:
|
|
90
90
|
bom_data = json.load(bfp)
|
|
@@ -97,6 +97,8 @@ def retrieve_bom_dependency_tree(bom_file):
|
|
|
97
97
|
|
|
98
98
|
def retrieve_oci_properties(bom_data):
|
|
99
99
|
props = {}
|
|
100
|
+
if not bom_data:
|
|
101
|
+
return props
|
|
100
102
|
for p in bom_data.get("metadata", {}).get("properties", []):
|
|
101
103
|
if p.get("name", "").startswith("oci:image:"):
|
|
102
104
|
props[p.get("name")] = p.get("value")
|
|
@@ -842,7 +844,7 @@ def summary_stats(results):
|
|
|
842
844
|
"""
|
|
843
845
|
Generate summary stats
|
|
844
846
|
|
|
845
|
-
:param results: List of scan results objects
|
|
847
|
+
:param results: List of scan results objects with severity attribute.
|
|
846
848
|
:return: A dictionary containing the summary statistics for the severity
|
|
847
849
|
levels of the vulnerabilities in the results list.
|
|
848
850
|
"""
|
|
@@ -1174,7 +1176,11 @@ def suggest_version(results, pkg_aliases=None, purl_aliases=None):
|
|
|
1174
1176
|
else:
|
|
1175
1177
|
full_pkg = pkg_aliases.get(full_pkg, full_pkg)
|
|
1176
1178
|
version_upgrades = pkg_fix_map.get(full_pkg, set())
|
|
1177
|
-
|
|
1179
|
+
if (
|
|
1180
|
+
fixed_location != placeholder_fix_version
|
|
1181
|
+
and fixed_location != placeholder_exclude_version
|
|
1182
|
+
):
|
|
1183
|
+
version_upgrades.add(fixed_location)
|
|
1178
1184
|
pkg_fix_map[full_pkg] = version_upgrades
|
|
1179
1185
|
for k, v in pkg_fix_map.items():
|
|
1180
1186
|
# Don't go near certain packages
|
|
@@ -4,14 +4,16 @@ from depscan.lib import config
|
|
|
4
4
|
from depscan.lib.pkg_query import npm_metadata, pypi_metadata
|
|
5
5
|
|
|
6
6
|
# Dict mapping project type to the audit source
|
|
7
|
-
type_audit_map = {"nodejs": NpmSource(), "js": NpmSource()}
|
|
7
|
+
type_audit_map = {"nodejs": NpmSource(), "js": NpmSource(), "npm": NpmSource()}
|
|
8
8
|
|
|
9
9
|
# Dict mapping project type to risk audit
|
|
10
10
|
risk_audit_map = {
|
|
11
|
+
"npm": npm_metadata,
|
|
11
12
|
"nodejs": npm_metadata,
|
|
12
13
|
"js": npm_metadata,
|
|
13
14
|
"python": pypi_metadata,
|
|
14
15
|
"py": pypi_metadata,
|
|
16
|
+
"pypi": pypi_metadata,
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
|
|
@@ -1173,7 +1173,11 @@ class CsafOccurence:
|
|
|
1173
1173
|
)
|
|
1174
1174
|
self.references = res["related_urls"]
|
|
1175
1175
|
self.type = (res["type"],)
|
|
1176
|
-
self.severity =
|
|
1176
|
+
self.severity = (
|
|
1177
|
+
res["severity"] if (
|
|
1178
|
+
res["severity"] in ["CRITICAL", "HIGH", "MEDIUM", "LOW", "NONE"]) else (
|
|
1179
|
+
"UNKNOWN")
|
|
1180
|
+
)
|
|
1177
1181
|
self.orig_date = res["source_orig_time"] or None
|
|
1178
1182
|
self.update_date = res["source_update_time"] or None
|
|
1179
1183
|
|
|
@@ -1186,7 +1190,8 @@ class CsafOccurence:
|
|
|
1186
1190
|
vuln["product_status"] = self.product_status
|
|
1187
1191
|
[ids, vuln["references"]] = format_references(self.references)
|
|
1188
1192
|
vuln["ids"] = ids
|
|
1189
|
-
|
|
1193
|
+
if self.cvss_v3:
|
|
1194
|
+
vuln["scores"] = [{"cvss_v3": self.cvss_v3, "products": [self.pkg]}]
|
|
1190
1195
|
self.notes.append(
|
|
1191
1196
|
{
|
|
1192
1197
|
"category": "general",
|
|
@@ -1298,28 +1303,33 @@ def parse_cvss(res):
|
|
|
1298
1303
|
If the vector string or base score are missing, or the CVSS
|
|
1299
1304
|
version is not 3.0 or 3.1, None is returned.
|
|
1300
1305
|
"""
|
|
1306
|
+
|
|
1307
|
+
# baseScore, baseSeverity, vectorString, and version are required
|
|
1301
1308
|
cvss_v3 = res.get("cvss_v3")
|
|
1302
|
-
version = re.findall(r"3.0|3.1", cvss_v3["vector_string"])
|
|
1303
|
-
# baseScore, vectorString, and version are required for a valid score
|
|
1304
1309
|
if (
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1310
|
+
not cvss_v3
|
|
1311
|
+
or not (vector_string := cvss_v3.get("vector_string"))
|
|
1312
|
+
or not (version := re.findall(r"3.0|3.1",
|
|
1313
|
+
cvss_v3.get("vector_string", "")))
|
|
1314
|
+
or not (base_score := cvss_v3.get("base_score"))
|
|
1315
|
+
or (severity := res.get("severity")) not in
|
|
1316
|
+
["CRITICAL", "HIGH", "MEDIUM", "LOW", "NONE"]
|
|
1309
1317
|
):
|
|
1310
1318
|
return None
|
|
1311
1319
|
version = version[0]
|
|
1312
|
-
|
|
1313
|
-
"baseScore":
|
|
1314
|
-
"
|
|
1315
|
-
"
|
|
1316
|
-
"
|
|
1317
|
-
"
|
|
1318
|
-
"
|
|
1320
|
+
cvss_v3_data = {
|
|
1321
|
+
"baseScore": base_score,
|
|
1322
|
+
"baseSeverity": severity,
|
|
1323
|
+
"attackVector": cvss_v3.get("attack_vector"),
|
|
1324
|
+
"privilegesRequired": cvss_v3.get("privileges_required"),
|
|
1325
|
+
"userInteraction": cvss_v3.get("user_interaction"),
|
|
1326
|
+
"scope": cvss_v3.get("scope"),
|
|
1319
1327
|
"version": version,
|
|
1320
|
-
"vectorString":
|
|
1328
|
+
"vectorString": vector_string,
|
|
1321
1329
|
}
|
|
1322
1330
|
|
|
1331
|
+
return cvss_v3_data
|
|
1332
|
+
|
|
1323
1333
|
|
|
1324
1334
|
def format_references(ref):
|
|
1325
1335
|
"""
|
|
@@ -1567,6 +1577,7 @@ def parse_toml(metadata):
|
|
|
1567
1577
|
'current_release_date'.
|
|
1568
1578
|
"""
|
|
1569
1579
|
tracking = parse_revision_history(metadata.get("tracking"))
|
|
1580
|
+
# FIXME: Could this be simplified as list comprehension without append
|
|
1570
1581
|
refs = []
|
|
1571
1582
|
[refs.append(v) for v in metadata.get("reference")]
|
|
1572
1583
|
notes = []
|
|
@@ -1873,6 +1884,8 @@ def add_vulnerabilities(data, results, direct_purls, reached_purls):
|
|
|
1873
1884
|
"HIGH": 2,
|
|
1874
1885
|
"MEDIUM": 3,
|
|
1875
1886
|
"LOW": 4,
|
|
1887
|
+
"UNKNOWN": 5,
|
|
1888
|
+
"NONE": 6,
|
|
1876
1889
|
}
|
|
1877
1890
|
affected_regex = re.compile(
|
|
1878
1891
|
r"(?P<lmod>[>=]{1,2})(?P<lower>\w+(?:.\w+)?(?:.\w+)?)-(?P<umod>[<=]{"
|
|
@@ -1884,7 +1897,7 @@ def add_vulnerabilities(data, results, direct_purls, reached_purls):
|
|
|
1884
1897
|
for r in results:
|
|
1885
1898
|
c = CsafOccurence(r)
|
|
1886
1899
|
new_vuln = c.to_dict()
|
|
1887
|
-
agg_score.add(severity_ref.get(c.severity))
|
|
1900
|
+
agg_score.add(severity_ref.get(c.severity, 5))
|
|
1888
1901
|
if c.search_string:
|
|
1889
1902
|
found = reached_dict.get(c.search_string)
|
|
1890
1903
|
if not found:
|
|
@@ -54,10 +54,10 @@ def create_pkg_variations(pkg_dict):
|
|
|
54
54
|
if purl_obj:
|
|
55
55
|
pkg_type = purl_obj.get("type")
|
|
56
56
|
qualifiers = purl_obj.get("qualifiers", {})
|
|
57
|
-
if qualifiers.get("distro_name"):
|
|
57
|
+
if qualifiers and qualifiers.get("distro_name"):
|
|
58
58
|
os_distro_name = qualifiers.get("distro_name")
|
|
59
59
|
name_aliases.add(f"""{os_distro_name}/{name}""")
|
|
60
|
-
if qualifiers.get("distro"):
|
|
60
|
+
if qualifiers and qualifiers.get("distro"):
|
|
61
61
|
os_distro = qualifiers.get("distro")
|
|
62
62
|
name_aliases.add(f"""{os_distro}/{name}""")
|
|
63
63
|
# almalinux-9.2 becomes almalinux-9
|
|
@@ -70,6 +70,7 @@ def create_pkg_variations(pkg_dict):
|
|
|
70
70
|
if vendor:
|
|
71
71
|
vendor_aliases.add(vendor)
|
|
72
72
|
vendor_aliases.add(vendor.lower())
|
|
73
|
+
vendor_aliases.add(vendor.lstrip("@"))
|
|
73
74
|
if (
|
|
74
75
|
vendor.startswith("org.")
|
|
75
76
|
or vendor.startswith("io.")
|
|
@@ -94,7 +95,6 @@ def create_pkg_variations(pkg_dict):
|
|
|
94
95
|
vendor_aliases.add("golang")
|
|
95
96
|
if pkg_type not in config.OS_PKG_TYPES:
|
|
96
97
|
name_aliases.add("package_" + name)
|
|
97
|
-
vendor_aliases.add(pkg_type)
|
|
98
98
|
if purl.startswith("pkg:composer"):
|
|
99
99
|
vendor_aliases.add("get" + name)
|
|
100
100
|
vendor_aliases.add(name + "_project")
|
|
@@ -168,7 +168,7 @@ def create_pkg_variations(pkg_dict):
|
|
|
168
168
|
name_aliases.add(name + "-bin")
|
|
169
169
|
else:
|
|
170
170
|
# Filter vendor aliases that are also name aliases
|
|
171
|
-
vendor_aliases = [x for x in vendor_aliases if x not in name_aliases]
|
|
171
|
+
vendor_aliases = [x for x in vendor_aliases if x not in name_aliases or x == vendor]
|
|
172
172
|
if len(vendor_aliases) > 0:
|
|
173
173
|
for vvar in list(vendor_aliases):
|
|
174
174
|
for nvar in list(name_aliases):
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import ast
|
|
2
2
|
import os
|
|
3
3
|
import re
|
|
4
|
+
import json
|
|
4
5
|
from collections import defaultdict
|
|
5
6
|
from datetime import datetime
|
|
6
7
|
from importlib.metadata import distribution
|
|
8
|
+
from jinja2 import Environment
|
|
7
9
|
|
|
8
10
|
from vdb.lib import db as db_lib
|
|
9
11
|
from vdb.lib.utils import version_compare
|
|
@@ -408,3 +410,26 @@ def export_pdf(
|
|
|
408
410
|
pdfkit.from_file(html_file, pdf_file, options=pdf_options)
|
|
409
411
|
except Exception:
|
|
410
412
|
pass
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def render_template_report(
|
|
416
|
+
jsonl_report_file,
|
|
417
|
+
summary,
|
|
418
|
+
template_file,
|
|
419
|
+
result_file,
|
|
420
|
+
):
|
|
421
|
+
"""
|
|
422
|
+
Render the given json_report_file and summary dict using the template_file with Jinja
|
|
423
|
+
"""
|
|
424
|
+
with open(jsonl_report_file, "r", encoding="utf-8") as jsonl_file:
|
|
425
|
+
json_report = [json.loads(jline) for jline in jsonl_file.readlines()]
|
|
426
|
+
with open(template_file, "r", encoding="utf-8") as tmpl_file:
|
|
427
|
+
template = tmpl_file.read()
|
|
428
|
+
jinja_env = Environment(autoescape=False)
|
|
429
|
+
jinja_tmpl = jinja_env.from_string(template)
|
|
430
|
+
report_result = jinja_tmpl.render(
|
|
431
|
+
vulnerabilities=json_report,
|
|
432
|
+
summary=summary,
|
|
433
|
+
)
|
|
434
|
+
with open(result_file, "w", encoding="utf-8") as outfile:
|
|
435
|
+
outfile.write(report_result)
|