Skip to content

Commit cfeb580

Browse files
Ardisclaude
andcommitted
Initial release of attackgraph library
Attack graph construction from network topology and vulnerability scan data with MITRE ATT&CK mapping and risk scoring. Includes CLI, parsers for Nessus and generic CSV, JSON/GraphML exporters, and 142 tests at 95% coverage. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
0 parents  commit cfeb580

29 files changed

Lines changed: 3695 additions & 0 deletions

.github/workflows/ci.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: ["3.9", "3.11", "3.12"]
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Set up Python ${{ matrix.python-version }}
20+
uses: actions/setup-python@v5
21+
with:
22+
python-version: ${{ matrix.python-version }}
23+
24+
- name: Install dependencies
25+
run: |
26+
python -m pip install --upgrade pip
27+
pip install -e ".[dev]" 2>/dev/null || pip install -e .
28+
pip install pytest pytest-cov ruff
29+
30+
- name: Lint with ruff
31+
run: ruff check src/ tests/
32+
33+
- name: Run tests
34+
run: pytest --cov=attackgraph --cov-report=term-missing -v

.gitignore

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Python
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
*.so
6+
*.egg-info/
7+
*.egg
8+
dist/
9+
build/
10+
.eggs/
11+
12+
# Virtual environments
13+
.venv/
14+
venv/
15+
env/
16+
17+
# IDE
18+
.idea/
19+
.vscode/
20+
*.swp
21+
*.swo
22+
*~
23+
24+
# Testing
25+
.coverage
26+
htmlcov/
27+
.pytest_cache/
28+
.mypy_cache/
29+
.ruff_cache/
30+
31+
# OS
32+
.DS_Store
33+
Thumbs.db
34+
35+
# Distribution
36+
*.tar.gz
37+
*.whl

Dockerfile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FROM python:3.12-slim
2+
3+
WORKDIR /app
4+
5+
COPY pyproject.toml README.md LICENSE ./
6+
COPY src/ ./src/
7+
8+
RUN pip install --no-cache-dir .
9+
10+
ENTRYPOINT ["attackgraph"]
11+
CMD ["--help"]

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Corey Wade
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
# attackgraph
2+
3+
**Attack graph construction from network topology and vulnerability scan data, with MITRE ATT&CK mapping and risk scoring.**
4+
5+
## Problem
6+
7+
Security teams face an overwhelming volume of vulnerability scan results but lack visibility into how those vulnerabilities chain together to form actual attack paths. A single medium-severity CVE on a web server might be the first step in a multi-hop path that reaches your crown jewel database. Without attack path analysis, teams waste time on isolated findings while critical chains go unaddressed.
8+
9+
**attackgraph** solves this by:
10+
11+
- Building directed attack graphs from network topology + vulnerability scan data
12+
- Computing which attack paths are actually feasible (network reachability, service matching, attack vector constraints)
13+
- Mapping each step to MITRE ATT&CK techniques
14+
- Scoring assets and paths by risk (CVSS, path length, asset criticality, cumulative probability)
15+
16+
## Installation
17+
18+
```bash
19+
pip install attackgraph
20+
```
21+
22+
Or from source:
23+
24+
```bash
25+
git clone https://github.com/cwccie/attackgraph.git
26+
cd attackgraph
27+
pip install -e .
28+
```
29+
30+
## Quick Start
31+
32+
### Python API
33+
34+
```python
35+
from attackgraph import Asset, Vulnerability, AttackGraphBuilder
36+
from attackgraph.scoring import RiskScorer
37+
from attackgraph.models import AttackVector
38+
39+
# Define assets
40+
web = Asset(
41+
id="webserver",
42+
ip="10.0.1.10",
43+
hostname="web-01",
44+
services=["http/80", "http/443"],
45+
criticality=0.7,
46+
network_zone="dmz",
47+
)
48+
db = Asset(
49+
id="database",
50+
ip="10.0.2.30",
51+
hostname="db-01",
52+
services=["mysql/3306"],
53+
criticality=1.0,
54+
network_zone="internal",
55+
)
56+
57+
# Define vulnerabilities
58+
web_vuln = Vulnerability(
59+
cve_id="CVE-2024-1001",
60+
cvss=9.8,
61+
affected_service="http/80",
62+
attack_vector=AttackVector.NETWORK,
63+
description="Remote code execution in HTTP server",
64+
)
65+
db_vuln = Vulnerability(
66+
cve_id="CVE-2024-1007",
67+
cvss=9.1,
68+
affected_service="mysql/3306",
69+
attack_vector=AttackVector.NETWORK,
70+
description="MySQL remote code execution",
71+
)
72+
73+
web.vulns.append(web_vuln)
74+
db.vulns.append(db_vuln)
75+
76+
# Build the attack graph
77+
builder = AttackGraphBuilder(
78+
topology={"webserver": ["database"]},
79+
assets=[web, db],
80+
)
81+
graph = builder.build()
82+
83+
print(f"Nodes: {graph.node_count}, Edges: {graph.edge_count}")
84+
85+
# Score risks
86+
scorer = RiskScorer(graph)
87+
scores = scorer.score_assets()
88+
for asset_id, score in sorted(scores.items(), key=lambda x: x[1], reverse=True):
89+
print(f" {asset_id}: {score:.2f}")
90+
91+
# Find attack paths to database
92+
paths = scorer.score_paths("database")
93+
for p in paths:
94+
print(f" Path: {' -> '.join(p['path'])} Score: {p['score']:.2f}")
95+
```
96+
97+
### CLI
98+
99+
```bash
100+
# Build an attack graph from topology and vulnerability scan
101+
attackgraph build --topology topology.yaml --vulns scan.csv --output graph.json
102+
103+
# Score assets in the graph
104+
attackgraph score graph.json
105+
106+
# Find attack paths to a target
107+
attackgraph paths graph.json --target database
108+
```
109+
110+
### Topology File Format (YAML)
111+
112+
```yaml
113+
topology:
114+
firewall: [webserver]
115+
webserver: [appserver, workstation]
116+
appserver: [database]
117+
118+
assets:
119+
firewall:
120+
id: firewall
121+
ip: 10.0.0.1
122+
services: [http/443, ssh/22]
123+
criticality: 0.9
124+
network_zone: perimeter
125+
webserver:
126+
id: webserver
127+
ip: 10.0.1.10
128+
services: [http/80, http/443]
129+
criticality: 0.7
130+
network_zone: dmz
131+
```
132+
133+
### Vulnerability CSV Format
134+
135+
```csv
136+
ip,cve_id,cvss,service,attack_vector,description
137+
10.0.1.10,CVE-2024-1001,9.8,http/80,network,Remote code execution
138+
10.0.2.20,CVE-2024-1004,9.0,smb/445,network,SMB buffer overflow
139+
```
140+
141+
Nessus CSV exports are also supported with `--format nessus`.
142+
143+
## Scoring Model
144+
145+
Risk scores combine multiple factors:
146+
147+
- **CVSS severity** — weighted blend of max and average CVSS per asset
148+
- **Asset criticality** — user-defined weight (0.0–1.0) reflecting business importance
149+
- **Exposure** — in-degree in the attack graph (more inbound attack vectors = higher risk)
150+
- **Path probability** — cumulative exploitation probability along each path
151+
- **Path length** — shorter paths are more dangerous (fewer steps to compromise)
152+
153+
All scores are normalized to a 0.0–10.0 scale.
154+
155+
## MITRE ATT&CK Mapping
156+
157+
Each attack step is automatically mapped to an ATT&CK technique using:
158+
159+
1. **Keyword matching** on CVE descriptions (e.g., "SQL injection" → T1190, "privilege escalation" → T1068)
160+
2. **Service-based heuristics** (e.g., HTTP → T1190, SMB → T1210, VPN → T1133)
161+
3. **Default fallback** to T1210 (Exploitation of Remote Services)
162+
163+
## Export Formats
164+
165+
- **JSON** — full graph with metadata, suitable for further processing
166+
- **GraphML** — for visualization in Gephi, yEd, Cytoscape
167+
168+
## Docker
169+
170+
```bash
171+
docker build -t attackgraph .
172+
docker run attackgraph --help
173+
docker run -v $(pwd)/data:/data attackgraph build -t /data/topology.yaml -V /data/vulns.csv
174+
```
175+
176+
## Development
177+
178+
```bash
179+
pip install -e .
180+
pip install pytest pytest-cov ruff
181+
182+
# Run tests
183+
pytest -v --cov=attackgraph
184+
185+
# Lint
186+
ruff check src/ tests/
187+
```
188+
189+
## License
190+
191+
MIT License. Copyright (c) 2026 Corey Wade.

pyproject.toml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
[build-system]
2+
requires = ["hatchling"]
3+
build-backend = "hatchling.build"
4+
5+
[project]
6+
name = "attackgraph"
7+
version = "0.1.0"
8+
description = "Attack graph construction from network topology and vulnerability scan data, with MITRE ATT&CK mapping and risk scoring."
9+
readme = "README.md"
10+
license = "MIT"
11+
requires-python = ">=3.9"
12+
authors = [
13+
{ name = "Corey Wade", email = "corey@example.com" },
14+
]
15+
keywords = ["security", "attack-graph", "vulnerability", "mitre-attack", "risk-scoring"]
16+
classifiers = [
17+
"Development Status :: 3 - Alpha",
18+
"Intended Audience :: Information Technology",
19+
"License :: OSI Approved :: MIT License",
20+
"Programming Language :: Python :: 3",
21+
"Programming Language :: Python :: 3.9",
22+
"Programming Language :: Python :: 3.11",
23+
"Programming Language :: Python :: 3.12",
24+
"Topic :: Security",
25+
]
26+
dependencies = [
27+
"networkx>=3.0",
28+
"numpy>=1.24",
29+
"click>=8.0",
30+
"pyyaml>=6.0",
31+
]
32+
33+
[project.scripts]
34+
attackgraph = "attackgraph.cli:cli"
35+
36+
[project.urls]
37+
Homepage = "https://github.com/cwccie/attackgraph"
38+
Repository = "https://github.com/cwccie/attackgraph"
39+
40+
[tool.ruff]
41+
target-version = "py39"
42+
line-length = 100
43+
44+
[tool.ruff.lint]
45+
select = ["E", "F", "W", "I"]
46+
47+
[tool.pytest.ini_options]
48+
testpaths = ["tests"]
49+
addopts = "--tb=short -q"
50+
51+
[tool.hatch.build.targets.wheel]
52+
packages = ["src/attackgraph"]

src/attackgraph/__init__.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""Attack graph construction from network topology and vulnerability scan data."""
2+
3+
from attackgraph.builder import AttackGraphBuilder
4+
from attackgraph.graph import AttackGraph
5+
from attackgraph.models import Asset, AttackStep, Vulnerability
6+
from attackgraph.scoring import RiskScorer
7+
8+
__version__ = "0.1.0"
9+
__all__ = [
10+
"Asset",
11+
"AttackStep",
12+
"AttackGraph",
13+
"AttackGraphBuilder",
14+
"RiskScorer",
15+
"Vulnerability",
16+
]

0 commit comments

Comments
 (0)