Skip to content

Commit d0622f6

Browse files
authored
Tweaks (#120)
* Extract search by any from cli Signed-off-by: Prabhu Subramanian <[email protected]> * Fix tests Signed-off-by: Prabhu Subramanian <[email protected]> * Added usage to readme. list malware api Signed-off-by: Prabhu Subramanian <[email protected]> --------- Signed-off-by: Prabhu Subramanian <[email protected]>
1 parent f72f492 commit d0622f6

File tree

4 files changed

+149
-70
lines changed

4 files changed

+149
-70
lines changed

README.md

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,11 @@ This repo is a vulnerability database and package search for sources such as App
77
A good vulnerability database must have the following properties:
88

99
- Accuracy
10-
- Easy to download, [integrate](./INTEGRATION.md), and use
10+
- Easy to [download](#download-pre-built-database-recommended), [integrate](./INTEGRATION.md), and use
1111
- Performance
1212

1313
Multiple upstream sources are used by vdb to improve accuracy and reduce false negatives. SQLite database containing data in CVE 5.0 schema format is precompiled and distributed as files via ghcr to simplify download. With automatic purl prefix generation even for git repos, searches on the database can be performed with purl, cpe, or even http git url string. Every row in the database uses an open specification such as CVE 5.0 or Package URL (purl and vers) thus preventing the possibility of vendor lock-in.
1414

15-
Freeloaders are welcome!
16-
1715
## Vulnerability Data sources
1816

1917
- Linux [vuln-list](https://github.com/appthreat/vuln-list) (Forked from AquaSecurity)
@@ -40,8 +38,14 @@ Freeloaders are welcome!
4038

4139
## Installation
4240

43-
```bash
44-
pip install appthreat-vulnerability-db
41+
```shell
42+
pip install appthreat-vulnerability-db>=6.0.0
43+
```
44+
45+
VDB v6 is a major rewrite to use sqlite database. Current users of depscan v5 must continue using version 5.6.x
46+
47+
```shell
48+
pip install appthreat-vulnerability-db==5.6.4
4549
```
4650

4751
## Usage
@@ -98,7 +102,27 @@ It is possible to customize the cache behavior by increasing the historic data p
98102
- NVD_START_YEAR - Default: 2018. Supports up to 2002
99103
- GITHUB_PAGE_COUNT - Default: 2. Supports up to 20
100104

101-
## CLI search
105+
## Usage
106+
107+
```shell
108+
usage: vdb [-h] [--clean] [--cache] [--cache-os] [--only-osv] [--only-aqua] [--only-ghsa] [--search SEARCH] [--list-malware] [--bom BOM_FILE]
109+
110+
AppThreat's vulnerability database and package search library with a built-in sqlite based storage.
111+
112+
options:
113+
-h, --help show this help message and exit
114+
--clean Clear the vulnerability database cache from platform specific user_data_dir.
115+
--cache Cache vulnerability information in platform specific user_data_dir.
116+
--cache-os Cache OS vulnerability information in platform specific user_data_dir.
117+
--only-osv Use only OSV as the source. Use with --cache.
118+
--only-aqua Use only Aqua vuln-list as the source. Use with --cache.
119+
--only-ghsa Use only recent ghsa as the source. Use with --cache.
120+
--search SEARCH Search for the package or CVE ID in the database. Use purl, cpe, or git http url.
121+
--list-malware List latest malwares with CVE ID beginning with MAL-.
122+
--bom BOM_FILE Search for packages in the CycloneDX BOM file.
123+
```
124+
125+
### CLI search
102126
103127
It is possible to perform a range of searches using the cli.
104128
@@ -122,13 +146,26 @@ vdb --search "npm:gitblame:0.0.1"
122146
# Search by CVE id
123147
vdb --search CVE-2024-25169
124148
149+
# Search with wildcard for CVE
150+
vdb --search CVE-2024-%
151+
125152
# Search by git url
126153
vdb --search "https://github.com/electron/electron"
127154
128155
# Search by CycloneDX SBOM
129156
vdb --bom bom.json
130157
```
131158
159+
### List recent malware
160+
161+
```shell
162+
vdb --list-malware
163+
```
164+
132165
## License
133166
134167
MIT
168+
169+
## Discord support
170+
171+
The developers could be reached via the [Discord](https://discord.gg/DCNxzaeUpd) channel for free and paid enterprise support.

test/test_source.py

Lines changed: 63 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ def test_aqua_wolfi_json():
389389
)
390390
with open(test_cve_data, "r") as fp:
391391
return json.loads(fp.read())
392-
392+
393393

394394
def test_convert(test_cve_json):
395395
nvdlatest = NvdSource()
@@ -402,15 +402,17 @@ def test_convert(test_cve_json):
402402
assert detail.severity
403403
assert detail.package
404404
assert detail.package_type
405-
405+
406406
db6.clear_all()
407407
nvdlatest.store(vulnerabilities)
408408
cve_data_count, cve_index_count = db6.stats()
409409
assert cve_data_count == 496
410410
assert cve_index_count == 1155
411-
results_count = len(list(search_db("CVE-2020-0001")))
411+
results_count = len(list(search.search_by_any("CVE-2020-0001")))
412412
assert results_count == 4
413-
results_count = len(list(search_db("cpe:2.3:o:google:android:8.1:*:*:*:*:*:*:*")))
413+
results_count = len(
414+
list(search.search_by_any("cpe:2.3:o:google:android:8.1:*:*:*:*:*:*:*"))
415+
)
414416
assert results_count == 25
415417

416418
cvesource = CVESource()
@@ -442,9 +444,11 @@ def test_convert2(test_cve_wconfig_json):
442444
cve_data_count, cve_index_count = db6.stats()
443445
assert cve_data_count == 2
444446
assert cve_index_count == 4
445-
results_count = len(list(search_db("CVE-2020-8022")))
447+
results_count = len(list(search.search_by_any("CVE-2020-8022")))
446448
assert results_count == 4
447-
results_count = len(list(search_db("cpe:2.3:o:opensuse:leap:15.1:*:*:*:*:*:*:*")))
449+
results_count = len(
450+
list(search.search_by_any("cpe:2.3:o:opensuse:leap:15.1:*:*:*:*:*:*:*"))
451+
)
448452
assert results_count == 1
449453

450454
cvesource = CVESource()
@@ -458,8 +462,14 @@ def test_convert2(test_cve_wconfig_json):
458462
assert cve_index_count == 0
459463

460464

461-
def test_nvd_api_convert(test_nvd_api_json1, test_nvd_api_json2, test_nvd_api_json3, test_nvd_api_json4, test_nvd_api_git_json):
462-
#json1
465+
def test_nvd_api_convert(
466+
test_nvd_api_json1,
467+
test_nvd_api_json2,
468+
test_nvd_api_json3,
469+
test_nvd_api_json4,
470+
test_nvd_api_git_json,
471+
):
472+
# json1
463473
nvdlatest = NvdSource()
464474
vulnerabilities = nvdlatest.convert(test_nvd_api_json1)
465475
assert len(vulnerabilities) == 1
@@ -471,20 +481,22 @@ def test_nvd_api_convert(test_nvd_api_json1, test_nvd_api_json2, test_nvd_api_js
471481
assert detail.package
472482
assert detail.package_type
473483
assert not detail.fixed_location
474-
484+
475485
db6.clear_all()
476486
nvdlatest.store(vulnerabilities)
477487
cve_data_count, cve_index_count = db6.stats()
478488
assert cve_data_count == 4
479489
assert cve_index_count == 20
480-
results_count = len(list(search_db("CVE-2020-8022")))
490+
results_count = len(list(search.search_by_any("CVE-2020-8022")))
481491
assert results_count == 0
482-
results_count = len(list(search_db("CVE-2024-0057")))
492+
results_count = len(list(search.search_by_any("CVE-2024-0057")))
483493
assert results_count == 10
484-
results_count = len(list(search_db("cpe:2.3:a:microsoft:.net:*:*:*:*:*:*:*:*")))
494+
results_count = len(
495+
list(search.search_by_any("cpe:2.3:a:microsoft:.net:*:*:*:*:*:*:*:*"))
496+
)
485497
assert results_count == 1
486498

487-
#json2
499+
# json2
488500
vulnerabilities = nvdlatest.convert(test_nvd_api_json2)
489501
assert len(vulnerabilities) == 1
490502
cvesource = CVESource()
@@ -496,12 +508,12 @@ def test_nvd_api_convert(test_nvd_api_json1, test_nvd_api_json2, test_nvd_api_js
496508
cve_data_count, cve_index_count = db6.stats()
497509
assert cve_data_count == 1
498510
assert cve_index_count == 7
499-
results_count = len(list(search_db("CVE-2020-8022")))
511+
results_count = len(list(search.search_by_any("CVE-2020-8022")))
500512
assert results_count == 0
501-
results_count = len(list(search_db("CVE-2024-21312")))
513+
results_count = len(list(search.search_by_any("CVE-2024-21312")))
502514
assert results_count == 7
503515

504-
#json3
516+
# json3
505517
vulnerabilities = nvdlatest.convert(test_nvd_api_json3)
506518
assert len(vulnerabilities) == 0
507519
cve = cvesource.convert5(vulnerabilities)
@@ -512,12 +524,12 @@ def test_nvd_api_convert(test_nvd_api_json1, test_nvd_api_json2, test_nvd_api_js
512524
cve_data_count, cve_index_count = db6.stats()
513525
assert cve_data_count == 0
514526
assert cve_index_count == 0
515-
results_count = len(list(search_db("CVE-2020-8022")))
527+
results_count = len(list(search.search_by_any("CVE-2020-8022")))
516528
assert results_count == 0
517-
results_count = len(list(search_db("CVE-2024-23771")))
529+
results_count = len(list(search.search_by_any("CVE-2024-23771")))
518530
assert results_count == 0
519531

520-
#json4
532+
# json4
521533
vulnerabilities = nvdlatest.convert(test_nvd_api_json4)
522534
assert len(vulnerabilities) == 1
523535

@@ -526,14 +538,20 @@ def test_nvd_api_convert(test_nvd_api_json1, test_nvd_api_json2, test_nvd_api_js
526538
cve_data_count, cve_index_count = db6.stats()
527539
assert cve_data_count == 2
528540
assert cve_index_count == 21
529-
results_count = len(list(search_db("CVE-2020-8022")))
541+
results_count = len(list(search.search_by_any("CVE-2020-8022")))
530542
assert results_count == 0
531-
results_count = len(list(search_db("CVE-2015-3192")))
543+
results_count = len(list(search.search_by_any("CVE-2015-3192")))
532544
assert results_count == 21
533-
results_count = len(list(search_db("cpe:2.3:a:pivotal_software:spring_framework:3.2.0:*:*:*:*:*:*:*")))
545+
results_count = len(
546+
list(
547+
search.search_by_any(
548+
"cpe:2.3:a:pivotal_software:spring_framework:3.2.0:*:*:*:*:*:*:*"
549+
)
550+
)
551+
)
534552
assert results_count == 2
535553

536-
#git_json
554+
# git_json
537555
vulnerabilities = nvdlatest.convert(test_nvd_api_git_json)
538556
assert len(vulnerabilities) == 1
539557
assert len(vulnerabilities[0].details) == 2
@@ -543,11 +561,15 @@ def test_nvd_api_convert(test_nvd_api_json1, test_nvd_api_json2, test_nvd_api_js
543561
cve_data_count, cve_index_count = db6.stats()
544562
assert cve_data_count == 2
545563
assert cve_index_count == 2
546-
results_count = len(list(search_db("CVE-2020-8022")))
564+
results_count = len(list(search.search_by_any("CVE-2020-8022")))
547565
assert results_count == 0
548-
results_count = len(list(search_db("CVE-2023-52426")))
566+
results_count = len(list(search.search_by_any("CVE-2023-52426")))
549567
assert results_count == 2
550-
results_count = len(list(search_db("cpe:2.3:a:libexpat_project:libexpat:*:*:*:*:*:*:*:*")))
568+
results_count = len(
569+
list(
570+
search.search_by_any("cpe:2.3:a:libexpat_project:libexpat:*:*:*:*:*:*:*:*")
571+
)
572+
)
551573
assert results_count == 1
552574

553575

@@ -561,8 +583,7 @@ def test_nvd_download():
561583
@pytest.mark.skip(reason="This downloads and tests with live data")
562584
def test_download_all():
563585
nvdlatest = NvdSource()
564-
data = nvdlatest.download_all()
565-
assert len(data) > 128000
586+
nvdlatest.download_all()
566587

567588

568589
@pytest.mark.skip(reason="This downloads and tests with live data")
@@ -747,22 +768,19 @@ def test_wolfi_convert(test_aqua_cg_json, test_aqua_wolfi_json):
747768

748769

749770
def test_vuln_location():
750-
vl = VulnerabilityLocation.from_values("cpe:2.3:a:pivotal_software:spring_framework:3.2.0:*:*:*:*:*:*:*", "3.2.0",
751-
"3.2.0", "", "")
771+
vl = VulnerabilityLocation.from_values(
772+
"cpe:2.3:a:pivotal_software:spring_framework:3.2.0:*:*:*:*:*:*:*",
773+
"3.2.0",
774+
"3.2.0",
775+
"",
776+
"",
777+
)
752778
assert vl.version == "3.2.0"
753-
vl = VulnerabilityLocation.from_values("cpe:2.3:a:org.springframework:spring-web:*:*:*:*:*:*:*:*", "5.0.0.RC2",
754-
"*", "", "5.0.0.RC3")
779+
vl = VulnerabilityLocation.from_values(
780+
"cpe:2.3:a:org.springframework:spring-web:*:*:*:*:*:*:*:*",
781+
"5.0.0.RC2",
782+
"*",
783+
"",
784+
"5.0.0.RC3",
785+
)
755786
assert vl.version == ">=5.0.0.RC2-<5.0.0.RC3"
756-
757-
758-
def search_db(query):
759-
if query.startswith("pkg:"):
760-
results = search.search_by_purl_like(query, with_data=True)
761-
elif query.startswith("CVE-") or query.startswith("GHSA-") or query.startswith("MAL-"):
762-
results = search.search_by_cve(query, with_data=True)
763-
elif query.startswith("http"):
764-
results = search.search_by_url(query, with_data=True)
765-
else:
766-
results = search.search_by_cpe_like(query, with_data=True)
767-
768-
return results

vdb/cli.py

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import shutil
88
import types
99

10-
import orjson
1110
from rich.console import Console
1211
from rich.live import Live
1312
from rich.markdown import Markdown
@@ -90,6 +89,13 @@ def build_args():
9089
dest="search",
9190
help="Search for the package or CVE ID in the database. Use purl, cpe, or git http url.",
9291
)
92+
parser.add_argument(
93+
"--list-malware",
94+
action="store_true",
95+
default=False,
96+
dest="list_malware",
97+
help="List latest malwares with CVE ID beginning with MAL-.",
98+
)
9399
parser.add_argument(
94100
"--bom",
95101
dest="bom_file",
@@ -100,7 +106,7 @@ def build_args():
100106

101107
def add_table_row(table: Table, res: dict, added_row_keys: dict):
102108
# matched_by is the purl or cpe string
103-
row_key = f"""{res["matched_by"]}|res.get("source_data_hash")"""
109+
row_key = f"""{res["matched_by"]}|{res.get("source_data_hash")}"""
104110
# Filter duplicate rows from getting printed
105111
if added_row_keys.get(row_key):
106112
return
@@ -142,7 +148,7 @@ def print_results(results):
142148
add_table_row(table, res, added_row_keys)
143149
elif isinstance(results, list):
144150
for res in results:
145-
add_table_row(table, res)
151+
add_table_row(table, res, added_row_keys)
146152
console.print(table)
147153

148154

@@ -176,18 +182,7 @@ def main():
176182
)
177183
db_lib.optimize_and_close_all()
178184
if args.search:
179-
if args.search.startswith("pkg:"):
180-
results = search.search_by_purl_like(args.search, with_data=True)
181-
elif (
182-
args.search.startswith("CVE-")
183-
or args.search.startswith("GHSA-")
184-
or args.search.startswith("MAL-")
185-
):
186-
results = search.search_by_cve(args.search, with_data=True)
187-
elif args.search.startswith("http"):
188-
results = search.search_by_url(args.search, with_data=True)
189-
else:
190-
results = search.search_by_cpe_like(args.search, with_data=True)
185+
results = search.search_by_any(args.search, with_data=True)
191186
if results:
192187
print_results(results)
193188
else:
@@ -196,6 +191,9 @@ def main():
196191
if os.path.exists(args.bom_file):
197192
results_generator = search.search_by_cdx_bom(args.bom_file, with_data=True)
198193
print_results(results_generator)
194+
elif args.list_malware:
195+
results_generator = search.latest_malware(with_data=True)
196+
print_results(results_generator)
199197

200198

201199
if __name__ == "__main__":

0 commit comments

Comments
 (0)