Skip to content
This repository was archived by the owner on Nov 12, 2025. It is now read-only.

Commit e1d1062

Browse files
Merge pull request #15 from krystianbajno/feature/cisa-kev
Add CISA-KEV, more enrichment, general refactor, more features, filte…
2 parents 1a5515f + 4b55336 commit e1d1062

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+803
-382
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Byte-compiled / optimized / DLL files
22
.DS_Store
3+
cache/
34
cveseeker_*_report.csv
45
cveseeker_*_report.json
56
cveseeker_*_report.html

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,21 @@ python3 cveseeker.py windows remote code execution
1818
python3 cveseeker.py cve-2024
1919
python3 cveseeker.py cve-2024 --max-per-provider 2000 # max results per provider, default 100
2020
python3 cveseeker.py cve-2024 --report # generate CSV, JSON and HTML report
21+
python3 cveseeker.py cve-2024 --critical --high --medium --low # include critical, high, medium, and low severities
22+
2123
```
2224

2325
# Sources
2426
- [www.exploit-db.com](https://www.exploit-db.com) (IMPLEMENTED)
2527
- [services.nvd.nist.gov](https://services.nvd.nist.gov/rest/json/cves/2.0?noRejected) (IMPLEMENTED)
2628
- [www.opencve.io](https://www.opencve.io) (IMPLEMENTED)
2729
- [www.packetstormsecurity.com](https://packetstormsecurity.com) (IMPLEMENTED)
28-
- [github.com advisories](https://github.com/advisories) (IMPLEMENTED)
2930
- [vulners.com](https://vulners.com/search) (IMPLEMENTED)
30-
- [www.cisa.gov](https://www.cisa.gov/known-exploited-vulnerabilities-catalog) (WIP)
31+
- [www.cisa.gov - KEV](https://www.cisa.gov/known-exploited-vulnerabilities-catalog) (IMPLEMENTED)
3132
- [www.rapid7.com](https://www.rapid7.com) (WIP)
3233
- [cve.mitre.org](https://cve.mitre.org/cve/search_cve_list.html) (WIP)
3334
- [github.com](https://github.com) (WIP)
35+
- [github.com advisories](https://github.com/advisories) (IMPLEMENTED)
3436
- [github.com/trickest/cve](https://github.com/search?q=repo%3Atrickest%2Fcve%20cve-2024&type=code) (IMPLEMENTED)
3537

3638
# Reporting

config.yaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,10 @@ providers:
55
ExploitDBAPI: true
66
GitHubAdvisoryAPI: true
77
VulnersAPI: false
8-
enrichment: true
8+
CISAKEVAPI: true
9+
10+
enrichment:
11+
sources:
12+
vulners: true
13+
github: true
14+
cisa_kev: true

cveseeker.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ def main():
2727
help="Generate CSV report"
2828
)
2929

30+
parser.add_argument('--low', action='store_true', help='Include low severity vulnerabilities')
31+
parser.add_argument('--medium', action='store_true', help='Include medium severity vulnerabilities')
32+
parser.add_argument('--high', action='store_true', help='Include high severity vulnerabilities')
33+
parser.add_argument('--critical', action='store_true', help='Include critical severity vulnerabilities')
34+
3035
parser.add_argument(
3136
'--playwright',
3237
action="store_true",
@@ -40,10 +45,14 @@ def main():
4045

4146
keywords = args.keywords
4247

43-
search_provider = SearchProvider(playwright_enabled=args.playwright)
48+
search_provider = SearchProvider(playwright_enabled=args.playwright, config_file='config.yaml')
4449
search_service = search_provider.make_service_api()
4550

46-
results = search_service.search(keywords, args.max_per_provider)
51+
desired_severities = [
52+
severity for severity in ['low', 'medium', 'high', 'critical'] if getattr(args, severity)
53+
]
54+
55+
results = search_service.search(keywords, args.max_per_provider, desired_severities=desired_severities)
4756

4857
VulnerabilityIntelligencePrinter.print(results)
4958

models/__init__.py

Whitespace-only changes.

providers/__init__.py

Whitespace-only changes.

providers/search_provider.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import yaml
2-
from services.search_manager import SearchManager
2+
from services.api.sources.cisa_kev import CISAKEVAPI
33

44
from services.api.sources.exploitdb import ExploitDBAPI
55
from services.api.sources.github_advisories import GitHubAdvisoryAPI
@@ -8,7 +8,12 @@
88
from services.api.sources.packetstormsecurity import PacketStormSecurityAPI
99
from services.api.sources.vulners import VulnersAPI
1010

11-
class SearchProvider():
11+
from typing import Dict
12+
13+
from services.search.search_manager import SearchManager
14+
from services.search.engine.progress import ProgressManager
15+
16+
class SearchProvider:
1217
def __init__(self, playwright_enabled=False, config_file='config.yaml'):
1318
self.search_service: SearchManager = None
1419
self.playwright_enabled = playwright_enabled
@@ -21,17 +26,18 @@ def __init__(self, playwright_enabled=False, config_file='config.yaml'):
2126
'ExploitDBAPI': ExploitDBAPI,
2227
'GitHubAdvisoryAPI': GitHubAdvisoryAPI,
2328
'VulnersAPI': VulnersAPI,
29+
"CISAKEVAPI": CISAKEVAPI
2430
}
2531

2632
def make_service_api(self) -> SearchManager:
2733
if self.search_service is None:
2834
self.boot()
2935
return self.search_service
30-
36+
3137
def boot(self):
3238
config = self.load_config()
3339
providers_config = config.get('providers', {})
34-
enrichment_config = config.get("enrichment", False)
40+
enrichment_config = config.get("enrichment", {})
3541

3642
providers = []
3743

@@ -48,10 +54,11 @@ def boot(self):
4854
if self.playwright_enabled:
4955
playwright_providers = []
5056
providers.extend(playwright_providers)
57+
58+
progress_manager = ProgressManager()
59+
self.search_service = SearchManager(providers, enrichment_config, progress_manager=progress_manager)
5160

52-
self.search_service = SearchManager(providers, enrichment_enabled=enrichment_config)
53-
54-
def load_config(self):
61+
def load_config(self) -> Dict:
5562
try:
5663
with open(self.config_file, 'r') as f:
5764
config = yaml.safe_load(f)

services/__init__.py

Whitespace-only changes.

services/api/__init__.py

Whitespace-only changes.

services/api/sources/cisa_kev.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import os
2+
import time
3+
import json
4+
from typing import List, Dict
5+
import httpx
6+
from dateutil import parser as dateutil_parser
7+
8+
from models.vulnerability import Vulnerability
9+
from services.api.source import Source
10+
from services.vulnerabilities.factories.vulnerability_factory import VulnerabilityFactory
11+
12+
class CISAKEVAPI(Source):
13+
CACHE_DIR = "cache"
14+
CACHE_FILE = os.path.join(CACHE_DIR, "cisa_kev_cache.json")
15+
CACHE_DURATION = 600
16+
17+
def __init__(self):
18+
self.url = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
19+
self.ensure_cache_dir()
20+
21+
def ensure_cache_dir(self):
22+
if not os.path.exists(self.CACHE_DIR):
23+
try:
24+
os.makedirs(self.CACHE_DIR)
25+
print(f"[*] Created cache directory at '{self.CACHE_DIR}'.")
26+
except Exception as e:
27+
print(f"[!] Failed to create cache directory '{self.CACHE_DIR}': {e}")
28+
29+
def is_cache_valid(self) -> bool:
30+
if os.path.exists(self.CACHE_FILE):
31+
cache_mtime = os.path.getmtime(self.CACHE_FILE)
32+
current_time = time.time()
33+
if (current_time - cache_mtime) < self.CACHE_DURATION:
34+
return True
35+
return False
36+
37+
def load_cache(self) -> Dict:
38+
try:
39+
with open(self.CACHE_FILE, 'r', encoding='utf-8') as f:
40+
data = json.load(f)
41+
print("[*] Loaded CISA KEV catalog from cache.")
42+
return data
43+
except Exception as e:
44+
print(f"[!] Error reading CISA KEV cache: {e}")
45+
return {}
46+
47+
def update_cache(self, data: Dict):
48+
try:
49+
with open(self.CACHE_FILE, 'w', encoding='utf-8') as f:
50+
json.dump(data, f, ensure_ascii=False, indent=4)
51+
print("[+] CISA KEV catalog downloaded and cached.")
52+
except Exception as e:
53+
print(f"[!] Error updating CISA KEV cache: {e}")
54+
55+
def fetch_data(self) -> Dict:
56+
try:
57+
print("[*] Downloading CISA KEV catalog...")
58+
response = httpx.get(self.url, timeout=15)
59+
if response.status_code == 200:
60+
data = response.json()
61+
self.update_cache(data)
62+
return data
63+
else:
64+
print(f"[!] Failed to fetch CISA KEV catalog. Status code: {response.status_code}")
65+
except Exception as e:
66+
print(f"[!] Error fetching CISA KEV data: {e}")
67+
return {}
68+
69+
def get_data(self) -> Dict:
70+
if self.is_cache_valid():
71+
return self.load_cache()
72+
else:
73+
return self.fetch_data()
74+
75+
def search(self, keywords: List[str], max_results: int) -> List[Vulnerability]:
76+
77+
vulnerabilities = []
78+
try:
79+
data = self.get_data()
80+
kev_vulnerabilities = data.get("vulnerabilities", [])
81+
82+
for item in kev_vulnerabilities:
83+
cve_id = item.get("cveID")
84+
85+
if not cve_id:
86+
continue
87+
88+
date_added = item.get("dateAdded")
89+
try:
90+
parsed_date = dateutil_parser.parse(date_added)
91+
date = parsed_date.strftime('%Y-%m-%d')
92+
except Exception:
93+
date = date_added or "N/A"
94+
95+
notes = item.get("notes", "")
96+
reference_urls = [url.strip() for url in notes.split(" ; ") if url.strip()]
97+
weaknesses = item.get("cwes", [])
98+
99+
vulnerability = VulnerabilityFactory.make(
100+
id=cve_id,
101+
source=self.__class__.__name__,
102+
url="https://www.cisa.gov/known-exploited-vulnerabilities-catalog",
103+
date=date,
104+
reference_urls=reference_urls,
105+
description=item.get("shortDescription", "N/A"),
106+
vulnerable_components=[item.get("product", "N/A")],
107+
tags=[item.get("vendorProject", "N/A")],
108+
weaknesses=weaknesses
109+
)
110+
vulnerabilities.append(vulnerability)
111+
112+
except Exception as e:
113+
print(f"[!] Error processing CISA KEV data: {e}")
114+
115+
return vulnerabilities

0 commit comments

Comments
 (0)