Skip to content

Commit be7ad05

Browse files
committed
Reset CVE v5 rank state before import
1 parent 611c312 commit be7ad05

5 files changed

Lines changed: 44 additions & 10 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ curl -s -X POST http://localhost:8000/search -d '{"query": ["tomcat"]}' | jq .
2424
1. `git clone https://github.com/cve-search/cpe-guesser.git`
2525
2. `cd cpe-guesser`
2626
3. Download the CPE dictionary & populate the database with `python3 ./bin/import.py` (the NVD JSON importer now uses parallel workers by default; tune with `--workers` and `--batch-size` if needed).
27-
4. Optionally enrich the vendor/product ranking with CVE v5 data using `python3 ./bin/import_cvelistv5.py`. This downloads `./data/cvelistv5.ndjson` only when missing unless you pass `--download`. Use `--index-words` if you also want to populate `w:<word>` and `s:<word>` from CVE v5 data, and review `set:missing_words_from_cvelistv5` for words that were not already indexed in Valkey.
27+
4. Optionally enrich the vendor/product ranking with CVE v5 data using `python3 ./bin/import_cvelistv5.py`. This downloads `./data/cvelistv5.ndjson` only when missing unless you pass `--download`, and it now resets `rank:cpe`, `rank:vendor_product`, and `set:missing_words_from_cvelistv5` before each import unless you opt into `--preserve-rank`. Use `--index-words` if you also want to populate `w:<word>` and `s:<word>` from CVE v5 data, and review `set:missing_words_from_cvelistv5` for words that were not already indexed in Valkey.
2828
5. Take a cup of black or green tea ().
2929
6. `python3 ./bin/server.py` to run the local HTTP server.
3030

bin/import_cvelistv5.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from dynaconf import Dynaconf
1111

1212
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
13-
from lib.cpeimport import CPEDownloader, CVEListV5Handler
13+
from lib.cpeimport import CPEDownloader, CVEListV5Handler, reset_rank_state
1414

1515
# Configuration
1616
settings = Dynaconf(settings_files=["../config/settings.yaml"])
@@ -39,12 +39,12 @@
3939
help="Force downloading the CVE v5 NDJSON even if it already exists locally.",
4040
)
4141
argparser.add_argument(
42-
"--replace-rank",
42+
"--preserve-rank",
4343
action="store_true",
4444
default=False,
4545
help=(
46-
"Delete existing rank:cpe, rank:vendor_product, and the missing-word set "
47-
"before importing."
46+
"Keep existing rank:cpe, rank:vendor_product, and the missing-word set "
47+
"instead of resetting them before importing."
4848
),
4949
)
5050
argparser.add_argument(
@@ -81,9 +81,14 @@
8181
print(f"Using existing file {cvelist_path} ...")
8282
cvelist_file = cvelist_path
8383

84-
if args.replace_rank:
85-
removed = rdb.delete("rank:cpe", "rank:vendor_product", args.missing_word_set)
86-
print(f"Deleted {removed} existing rank key(s) from the database.")
84+
if args.preserve_rank:
85+
print(
86+
"Preserving existing rank:cpe, rank:vendor_product, and "
87+
f"{args.missing_word_set}."
88+
)
89+
else:
90+
removed = reset_rank_state(rdb, args.missing_word_set)
91+
print(f"Reset {removed} existing rank key(s) before importing.")
8792

8893
handler = CVEListV5Handler(
8994
rdb,

lib/cpeimport/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
from .base import CPEImportHandler
2-
from .cvelistv5 import CVEListV5Handler
2+
from .cvelistv5 import CVEListV5Handler, reset_rank_state
33
from .nvd_json import NVDCPEHandler
44
from .xml_dictionary import XMLCPEHandler
55
from .downloader import CPEDownloader
66

77
__all__ = [
88
"CPEImportHandler",
99
"CVEListV5Handler",
10+
"reset_rank_state",
1011
"NVDCPEHandler",
1112
"XMLCPEHandler",
1213
"CPEDownloader",

lib/cpeimport/cvelistv5.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
from .base import CPEImportHandler
44

55

6+
def reset_rank_state(rdb, missing_word_key="set:missing_words_from_cvelistv5"):
7+
"""Delete the CVE v5 ranking keys so each import starts from a clean state."""
8+
return rdb.delete("rank:cpe", "rank:vendor_product", missing_word_key)
9+
10+
611
class CVEListV5Handler(CPEImportHandler):
712
"""Handler for CVE Record Format v5 NDJSON exports."""
813

tests/test_cvelistv5.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import json
33
import unittest
44

5-
from lib.cpeimport.cvelistv5 import CVEListV5Handler
5+
from lib.cpeimport.cvelistv5 import CVEListV5Handler, reset_rank_state
66

77

88
class FakePipeline:
@@ -32,6 +32,14 @@ def __init__(self):
3232
self.sets = {}
3333
self.sorted_sets = {}
3434

35+
def delete(self, *keys):
36+
removed = 0
37+
for key in keys:
38+
removed += int(key in self.sets or key in self.sorted_sets)
39+
self.sets.pop(key, None)
40+
self.sorted_sets.pop(key, None)
41+
return removed
42+
3543
def pipeline(self, transaction=False):
3644
return FakePipeline(self)
3745

@@ -51,6 +59,21 @@ def exists(self, key):
5159

5260

5361
class CVEListV5HandlerTestCase(unittest.TestCase):
62+
def test_reset_rank_state_clears_previous_import_data(self):
63+
rdb = FakeRDB()
64+
rdb.zadd("rank:cpe", {"cpe:2.3:a:acme:widget": 3})
65+
rdb.zadd("rank:vendor_product", {"cpe:2.3:a:acme:widget": 3})
66+
rdb.sadd("set:missing_words_from_cvelistv5", "widget")
67+
rdb.sadd("w:widget", "cpe:2.3:a:acme:widget")
68+
69+
removed = reset_rank_state(rdb)
70+
71+
self.assertEqual(removed, 3)
72+
self.assertNotIn("rank:cpe", rdb.sorted_sets)
73+
self.assertNotIn("rank:vendor_product", rdb.sorted_sets)
74+
self.assertNotIn("set:missing_words_from_cvelistv5", rdb.sets)
75+
self.assertIn("w:widget", rdb.sets)
76+
5477
def test_extracts_cpes_from_metadata_and_configurations(self):
5578
record = {
5679
"containers": {

0 commit comments

Comments
 (0)