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.

Files changed (86) hide show
  1. {owasp-depscan-5.0.1/owasp_depscan.egg-info → owasp-depscan-5.0.3}/PKG-INFO +49 -10
  2. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/README.md +46 -8
  3. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/cli.py +63 -15
  4. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/analysis.py +10 -4
  5. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/audit.py +3 -1
  6. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/csaf.py +30 -17
  7. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/normalize.py +4 -4
  8. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/utils.py +25 -0
  9. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3/owasp_depscan.egg-info}/PKG-INFO +49 -10
  10. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/owasp_depscan.egg-info/requires.txt +2 -1
  11. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/pyproject.toml +3 -2
  12. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/test/test_csaf.py +319 -487
  13. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/test/test_utils.py +45 -0
  14. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/LICENSE +0 -0
  15. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/MANIFEST.in +0 -0
  16. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/__init__.py +0 -0
  17. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/__init__.py +0 -0
  18. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/bom.py +0 -0
  19. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/config.py +0 -0
  20. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/explainer.py +0 -0
  21. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/github.py +0 -0
  22. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/license.py +0 -0
  23. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/logger.py +0 -0
  24. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/depscan/lib/pkg_query.py +0 -0
  25. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/owasp_depscan.egg-info/SOURCES.txt +0 -0
  26. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/owasp_depscan.egg-info/dependency_links.txt +0 -0
  27. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/owasp_depscan.egg-info/entry_points.txt +0 -0
  28. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/owasp_depscan.egg-info/top_level.txt +0 -0
  29. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/setup.cfg +0 -0
  30. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/test/test_analysis.py +0 -0
  31. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/test/test_bom.py +0 -0
  32. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/test/test_explainer.py +0 -0
  33. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/test/test_github.py +0 -0
  34. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/test/test_license.py +0 -0
  35. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/test/test_norm.py +0 -0
  36. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/test/test_pkg_query.py +0 -0
  37. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/__init__.py +0 -0
  38. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_data/fields.yml +0 -0
  39. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_data/meta.yml +0 -0
  40. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_data/rules.yml +0 -0
  41. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/0bsd.txt +0 -0
  42. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/afl-3.0.txt +0 -0
  43. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/agpl-3.0.txt +0 -0
  44. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/apache-2.0.txt +0 -0
  45. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/artistic-2.0.txt +0 -0
  46. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/bsd-2-clause.txt +0 -0
  47. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/bsd-3-clause-clear.txt +0 -0
  48. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/bsd-3-clause.txt +0 -0
  49. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/bsd-4-clause.txt +0 -0
  50. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/bsl-1.0.txt +0 -0
  51. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/cc-by-4.0.txt +0 -0
  52. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/cc-by-sa-4.0.txt +0 -0
  53. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/cc0-1.0.txt +0 -0
  54. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/cecill-2.1.txt +0 -0
  55. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/cern-ohl-p-2.0.txt +0 -0
  56. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/cern-ohl-s-2.0.txt +0 -0
  57. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/cern-ohl-w-2.0.txt +0 -0
  58. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/ecl-2.0.txt +0 -0
  59. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/epl-1.0.txt +0 -0
  60. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/epl-2.0.txt +0 -0
  61. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/eupl-1.1.txt +0 -0
  62. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/eupl-1.2.txt +0 -0
  63. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/gfdl-1.3.txt +0 -0
  64. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/gpl-2.0.txt +0 -0
  65. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/gpl-3.0.txt +0 -0
  66. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/isc.txt +0 -0
  67. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/lgpl-2.1.txt +0 -0
  68. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/lgpl-3.0.txt +0 -0
  69. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/lppl-1.3c.txt +0 -0
  70. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/mit-0.txt +0 -0
  71. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/mit.txt +0 -0
  72. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/mpl-2.0.txt +0 -0
  73. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/ms-pl.txt +0 -0
  74. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/ms-rl.txt +0 -0
  75. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/mulanpsl-2.0.txt +0 -0
  76. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/ncsa.txt +0 -0
  77. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/odbl-1.0.txt +0 -0
  78. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/ofl-1.1.txt +0 -0
  79. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/osl-3.0.txt +0 -0
  80. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/postgresql.txt +0 -0
  81. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/unlicense.txt +0 -0
  82. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/upl-1.0.txt +0 -0
  83. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/vim.txt +0 -0
  84. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/wtfpl.txt +0 -0
  85. {owasp-depscan-5.0.1 → owasp-depscan-5.0.3}/vendor/choosealicense.com/_licenses/zlib.txt +0 -0
  86. {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.1
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.4
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 dep-scan binary and the vulnerability database for effortless integration. Example workflow is [here](https://github.com/appthreat/images-info/blob/main/.github/workflows/build.yml#L13).
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
- * Scanning a local directory.
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
- * Scanning a SBOM file (present locally).
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
- * Scanning a GitHub repo.
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
- * Uploading a SBOM file and generating results based on it.
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] [-i SRC_DIR_IMAGE] [-o REPORT_FILE]
189
- [--reports-dir REPORTS_DIR] [--deep] [--no-universal] [--no-vuln-table] [--threatdb-server THREATDB_SERVER] [--threatdb-username THREATDB_USERNAME] [--threatdb-password THREATDB_PASSWORD] [--threatdb-token THREATDB_TOKEN] [--server]
190
- [--server-host SERVER_HOST] [--server-port SERVER_PORT] [--cdxgen-server CDXGEN_SERVER] [--debug] [--explain] [--reachables-slices-file REACHABLES_SLICES_FILE] [-v]
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 dep-scan binary and the vulnerability database for effortless integration. Example workflow is [here](https://github.com/appthreat/images-info/blob/main/.github/workflows/build.yml#L13).
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
- * Scanning a local directory.
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
- * Scanning a SBOM file (present locally).
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
- * Scanning a GitHub repo.
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
- * Uploading a SBOM file and generating results based on it.
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] [-i SRC_DIR_IMAGE] [-o REPORT_FILE]
151
- [--reports-dir REPORTS_DIR] [--deep] [--no-universal] [--no-vuln-table] [--threatdb-server THREATDB_SERVER] [--threatdb-username THREATDB_USERNAME] [--threatdb-password THREATDB_PASSWORD] [--threatdb-token THREATDB_TOKEN] [--server]
152
- [--server-host SERVER_HOST] [--server-port SERVER_PORT] [--cdxgen-server CDXGEN_SERVER] [--debug] [--explain] [--reachables-slices-file REACHABLES_SLICES_FILE] [-v]
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 paths_list and os.path.exists(
501
- os.path.join(rafs_data_dir.name, "data.rafs")
502
- ) and os.path.exists(os.path.join(rafs_data_dir.name, "meta.rafs")):
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.", vdb_rafs_database_url
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
- if args.bom and os.path.exists(args.bom):
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
- LOG.debug("Scanning using the bom file %s", bom_file)
869
- if not args.bom:
870
- LOG.info(
871
- "To improve performance, cache the bom file and invoke "
872
- "depscan with --bom %s instead of -i",
873
- bom_file,
874
- )
875
- pkg_list = get_pkg_list(bom_file)
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
- _, vdr_file, pkg_vulnerabilities, pkg_group_rows = summarise(
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 wuth severity attribute.
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
- version_upgrades.add(fixed_location)
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 = res["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
- vuln["scores"] = [{"cvss_v3": self.cvss_v3, "products": [self.pkg]}]
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
- not cvss_v3
1306
- or not cvss_v3.get("vector_string")
1307
- or not version
1308
- or not cvss_v3.get("base_score")
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
- return {
1313
- "baseScore": cvss_v3["base_score"],
1314
- "attackVector": cvss_v3["attack_vector"],
1315
- "privilegesRequired": cvss_v3["privileges_required"],
1316
- "userInteraction": cvss_v3["user_interaction"],
1317
- "scope": cvss_v3["scope"],
1318
- "baseSeverity": res["severity"],
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": cvss_v3["vector_string"],
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)