Skip to content

Commit 202bf81

Browse files
committed
v0.14.0
1 parent a097e2a commit 202bf81

39 files changed

Lines changed: 3534 additions & 1896 deletions

.github/workflows/publish.yml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: Publish to PyPI
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
workflow_dispatch:
8+
9+
jobs:
10+
build:
11+
name: Build sdist and wheel
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v4
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v5
19+
with:
20+
python-version: "3.11"
21+
22+
- name: Install build backend
23+
run: |
24+
python -m pip install --upgrade pip
25+
pip install build
26+
27+
- name: Build
28+
run: |
29+
python -m build
30+
31+
- name: Upload dist artifacts
32+
uses: actions/upload-artifact@v4
33+
with:
34+
name: dist
35+
path: dist/*
36+
37+
publish:
38+
name: Publish to PyPI
39+
needs: build
40+
runs-on: ubuntu-latest
41+
permissions:
42+
id-token: write # for trusted publishing (preferred) or use API token below
43+
contents: read
44+
steps:
45+
- name: Download dist artifacts
46+
uses: actions/download-artifact@v4
47+
with:
48+
name: dist
49+
path: dist
50+
51+
- name: Publish to PyPI (Trusted Publishing)
52+
uses: pypa/gh-action-pypi-publish@release/v1
53+
with:
54+
packages-dir: dist/
55+
if: ${{ github.ref_type == 'tag' }}
56+
57+
# If you prefer API token instead of Trusted Publishing, uncomment below and add PYPI_API_TOKEN secret
58+
# - name: Publish to PyPI (API token)
59+
# uses: pypa/gh-action-pypi-publish@release/v1
60+
# with:
61+
# packages-dir: dist/
62+
# password: ${{ secrets.PYPI_API_TOKEN }}
63+
# if: ${{ github.ref_type == 'tag' }}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11

22
sploitscan/config.json
3+
*.pyc
4+
sploitscan/config.json

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# 📆 Changelog
22

3+
## [07. October 2025] - Version 0.14.0
4+
5+
- **Modern modular refactor**
6+
Reorganized the codebase into clear modules (fetchers, metrics, display, AI providers, importers, exporters, utils) while keeping the CLI usage and output intact. The legacy wrapper now safely delegates to the new CLI and supports both “python -m sploitscan” and direct script execution.
7+
8+
- **HTML export redesign**
9+
Complete report refresh with Tailwind (CDN) styling: dark mode toggle, sticky header, sidebar filters, sortable summary table, responsive detail cards, and a readable AI section. Kept a zero‑build setup; distro packagers can optionally ship a precompiled CSS for offline styling.
10+
11+
- **Metasploit integration**
12+
Added default Metasploit discovery via Rapid7’s modules_metadata_base.json with conditional caching. Only modules whose references contain the exact CVE ID are counted.
13+
14+
- **Bug fixes & compatibility**
15+
316
## [25. February 2025] - Version 0.13.0
417

518
- **Google Gemini, xAI Grok & DeepSeek Integration**

Dockerfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ FROM python:3.9-slim-buster
22

33
WORKDIR /app
44
COPY . /app
5-
COPY sploitscan/config.json /app/config.json
65
RUN pip install --no-cache-dir -r requirements.txt
76

87
ENTRYPOINT ["python", "sploitscan.py"]

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ SploitScan is a powerful and user-friendly tool designed to streamline the proce
7676
- **[GitHub](https://poc-in-github.motikan2010.net/)**
7777
- **[ExploitDB](https://www.exploit-db.com/)**
7878
- **[VulnCheck](https://vulncheck.com/)** (requires a **free** VulnCheck API key)
79-
- **[Packet Storm](https://packetstormsecurity.com/)**
8079
- **[Nuclei](https://github.com/projectdiscovery/nuclei-templates)**
80+
- **[Metasploit](https://github.com/rapid7/metasploit-framework)**
8181

8282
## 📁 Supported Vulnerability Scanner Import
8383

@@ -138,7 +138,7 @@ A typical `config.json` might look like this:
138138
{
139139
"vulncheck_api_key": "",
140140
"openai_api_key": "",
141-
"google_api_key": "",
141+
"google_ai_api_key": "",
142142
"grok_api_key": "",
143143
"deepseek_api_key": ""
144144
}
@@ -155,7 +155,7 @@ $ python .\sploitscan.py -h
155155
╚════██║██╔═══╝ ██║ ██║ ██║██║ ██║ ╚════██║██║ ██╔══██║██║╚██╗██║
156156
███████║██║ ███████╗╚██████╔╝██║ ██║ ███████║╚██████╗██║ ██║██║ ╚████║
157157
╚══════╝╚═╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝
158-
v0.13.0 / Alexander Hagenah / @xaitax / ah@primepage.de
158+
v0.14.0 / Alexander Hagenah / @xaitax / ah@primepage.de
159159

160160
usage: sploitscan.py [-h] [-e {json,csv,html}] [-t {nessus,nexpose,openvas,docker}] [--ai {openai,google,grok,deepseek}] [-k KEYWORDS [KEYWORDS ...]] [-local] [-f] [-m METHODS] [-i IMPORT_FILE] [-c CONFIG] [-d] [cve_ids ...]
161161

@@ -211,7 +211,7 @@ sploitscan -local
211211
╚════██║██╔═══╝ ██║ ██║ ██║██║ ██║ ╚════██║██║ ██╔══██║██║╚██╗██║
212212
███████║██║ ███████╗╚██████╔╝██║ ██║ ███████║╚██████╗██║ ██║██║ ╚████║
213213
╚══════╝╚═╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝
214-
v0.13.0 / Alexander Hagenah / @xaitax / ah@primepage.de
214+
v0.14.0 / Alexander Hagenah / @xaitax / ah@primepage.de
215215

216216
📥 Cloning CVE List V5 into 'C:\Users\ah/.sploitscan\cvelistV5'.
217217
⚠️ Warning: The repository is several GB in size and the download may take a while.
@@ -235,7 +235,7 @@ sploitscan -k "Outlook Express"
235235
╚════██║██╔═══╝ ██║ ██║ ██║██║ ██║ ╚════██║██║ ██╔══██║██║╚██╗██║
236236
███████║██║ ███████╗╚██████╔╝██║ ██║ ███████║╚██████╗██║ ██║██║ ╚████║
237237
╚══════╝╚═╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝
238-
v0.13.0 / Alexander Hagenah / @xaitax / ah@primepage.de
238+
v0.14.0 / Alexander Hagenah / @xaitax / ah@primepage.de
239239
240240
┌───[ 🕵️ Searching local database for keywords: outlook express ]
241241
Processing CVE files: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 282372/282372 [04:38<00:00, 1013.92it/s]

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "sploitscan"
7-
version = "0.13.0"
7+
version = "0.14.0"
88
description = "SploitScan is a cybersecurity utility designed to provide detailed information on vulnerabilities and associated exploits."
99
authors = [ { name = "Alexander Hagenah", email = "ah@primepage.de" } ]
1010
license = { file = "LICENSE" }

requirements.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
requests==2.32.3
1+
requests==2.32.5
22
jinja2==3.1.6
3-
openai==1.65.4
4-
google-genai==1.4.0
5-
GitPython==3.1.44
3+
openai==2.2.0
4+
google-genai==1.41.0
5+
GitPython==3.1.45
66
tqdm==4.67.1

sploitscan/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from __future__ import annotations
2+
3+
# Public package API and metadata
4+
5+
from .constants import VERSION as __version__
6+
from .cli import cli, main
7+
8+
__all__ = [
9+
"__version__",
10+
"cli",
11+
"main",
12+
]

sploitscan/ai.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
"""
2+
AI orchestration: prompt generation and provider routing.
3+
"""
4+
5+
from __future__ import annotations
6+
7+
import json
8+
from typing import Any, Dict, Optional
9+
10+
from .config import load_config
11+
from .ai_providers.openai_provider import get_openai_risk_assessment
12+
# Other providers (Google/Grok/DeepSeek) are imported lazily inside get_risk_assessment()
13+
# to keep optional dependencies graceful and avoid import errors if packages are not installed.
14+
15+
16+
def generate_ai_prompt(cve_details: str, cve_data: Dict[str, Any]) -> str:
17+
"""
18+
Build a deterministic, hallucination-resistant prompt. We:
19+
- Force exactly four numbered sections with strict formatting.
20+
- Emphasize evidential reasoning using provided data (CVE, KEV, EPSS, exploits).
21+
- Forbid inventing patches/links and require stating “Unknown” if uncertain.
22+
- Optimize for security triage usefulness (business impact + concrete actions).
23+
"""
24+
prompt = f"""
25+
You are a senior security analyst. Using ONLY the information provided below, produce EXACTLY four sections, each starting with the numeric header shown:
26+
27+
1. Risk Assessment
28+
Explain: vulnerability nature, affected components (if clear), preconditions, attacker effort, and impact on confidentiality, integrity, and availability. Reflect signals like CVSS/EPSS, CISA KEV status, public exploit presence (GitHub, Metasploit, Exploit‑DB, Nuclei) where available. If data is unclear, write “Unknown”.
29+
30+
2. Potential Attack Scenarios
31+
Describe at least one plausible end-to-end scenario: entry point, pre-auth vs post-auth, privilege required, lateral movement, and realistic outcomes (e.g., data theft, RCE, business downtime). If multiple paths exist, choose the highest-risk one and justify briefly.
32+
33+
3. Mitigation Recommendations
34+
Provide concrete, prioritized actions. Include: patch/update guidance if a vendor fix is referenced, interim mitigations (network controls, feature disablement, WAF rules, config hardening), and detection/monitoring ideas. Only cite links that are present in the provided references; do NOT invent URLs. If patch availability is unclear, say “Patch status: Unknown”.
35+
36+
4. Executive Summary
37+
Give a concise, two-paragraph summary for non-technical stakeholders: business risk, exploitation likelihood, and urgency. End with a clear call to action.
38+
39+
Strict formatting rules:
40+
- Plain text only. No bullet points, no dashes, no markdown, no emojis.
41+
- Each of the four headings must appear verbatim on its own line as above.
42+
- Separate paragraphs with a single blank line.
43+
- Do not add extra sections or a conclusion beyond the four sections.
44+
- If you are unsure, write “Unknown” rather than speculating.
45+
46+
CVE DETAILS:
47+
{cve_details}
48+
49+
FULL CVE DATA (for reference; use cautiously, do not copy raw JSON):
50+
{json.dumps(cve_data, indent=2)}
51+
"""
52+
return prompt
53+
54+
55+
def get_risk_assessment(
56+
ai_provider: Optional[str],
57+
cve_details: str,
58+
cve_data: Dict[str, Any],
59+
*,
60+
config: Optional[Dict[str, Any]] = None,
61+
) -> str:
62+
"""
63+
Route the prompt to the selected AI provider. If no provider is selected, return an explanatory message.
64+
"""
65+
if not ai_provider:
66+
return "❌ No AI provider selected."
67+
68+
cfg = config or load_config()
69+
prompt = generate_ai_prompt(cve_details, cve_data)
70+
71+
# Normalize common aliases
72+
normalized = {
73+
"openai": "openai",
74+
"chatgpt": "openai",
75+
"gpt": "openai",
76+
"google": "google",
77+
"gemini": "google",
78+
"grok": "grok",
79+
"xai": "grok",
80+
"deepseek": "deepseek",
81+
}
82+
provider = normalized.get(ai_provider.lower(), ai_provider.lower())
83+
84+
if provider == "openai":
85+
return get_openai_risk_assessment(prompt, cfg.get("openai_api_key"))
86+
87+
if provider == "google":
88+
try:
89+
from .ai_providers.google_provider import get_google_risk_assessment
90+
except Exception as e:
91+
return f"❌ Google provider not available: {e}"
92+
return get_google_risk_assessment(prompt, cfg.get("google_ai_api_key"))
93+
94+
if provider == "grok":
95+
try:
96+
from .ai_providers.grok_provider import get_grok_risk_assessment
97+
except Exception as e:
98+
return f"❌ Grok provider not available: {e}"
99+
return get_grok_risk_assessment(prompt, cfg.get("grok_api_key"))
100+
101+
if provider == "deepseek":
102+
try:
103+
from .ai_providers.deepseek_provider import get_deepseek_risk_assessment
104+
except Exception as e:
105+
return f"❌ DeepSeek provider not available: {e}"
106+
return get_deepseek_risk_assessment(prompt, cfg.get("deepseek_api_key"))
107+
108+
return "❌ Unknown AI provider selected."
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""
2+
DeepSeek provider for AI-powered risk assessment.
3+
"""
4+
5+
from __future__ import annotations
6+
7+
from typing import Optional
8+
import time
9+
10+
from openai import OpenAI # type: ignore[import-untyped]
11+
12+
13+
def get_deepseek_risk_assessment(prompt: str, api_key: Optional[str]) -> str:
14+
"""
15+
DeepSeek via OpenAI-compatible API.
16+
- Endpoint: https://api.deepseek.com
17+
- Model: deepseek-chat
18+
- Deterministic output: temperature=0, stream=False
19+
- Retries: simple backoff to tolerate transient timeouts
20+
"""
21+
if not api_key:
22+
return "❌ DeepSeek API key is not configured correctly."
23+
try:
24+
client = OpenAI(api_key=api_key, base_url="https://api.deepseek.com")
25+
except Exception as e:
26+
return f"❌ Error initializing DeepSeek client: {e}"
27+
28+
last_err: Optional[str] = None
29+
for attempt in range(3):
30+
try:
31+
response = client.chat.completions.create(
32+
model="deepseek-chat",
33+
messages=[
34+
{"role": "system", "content": "You are a security analyst."},
35+
{"role": "user", "content": prompt},
36+
],
37+
temperature=0,
38+
stream=False,
39+
timeout=60,
40+
)
41+
if response.choices and len(response.choices) > 0:
42+
message = response.choices[0].message
43+
content = getattr(message, "content", None)
44+
return (content or str(message) or "").strip()
45+
return "DeepSeek: No response received."
46+
except Exception as e:
47+
last_err = str(e)
48+
if attempt < 2:
49+
time.sleep(3 * (attempt + 1))
50+
continue
51+
return f"❌ Error fetching data from DeepSeek: {last_err or 'Unknown error'}"
52+

0 commit comments

Comments
 (0)