vamp-easm 1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vamp_easm-1.1/PKG-INFO +210 -0
- vamp_easm-1.1/README.md +190 -0
- vamp_easm-1.1/easm/__init__.py +5 -0
- vamp_easm-1.1/easm/alerter.py +207 -0
- vamp_easm-1.1/easm/differ.py +374 -0
- vamp_easm-1.1/easm/scanner.py +517 -0
- vamp_easm-1.1/easm/storage.py +295 -0
- vamp_easm-1.1/pyproject.toml +40 -0
- vamp_easm-1.1/setup.cfg +4 -0
- vamp_easm-1.1/vamp_easm.egg-info/PKG-INFO +210 -0
- vamp_easm-1.1/vamp_easm.egg-info/SOURCES.txt +15 -0
- vamp_easm-1.1/vamp_easm.egg-info/dependency_links.txt +1 -0
- vamp_easm-1.1/vamp_easm.egg-info/entry_points.txt +2 -0
- vamp_easm-1.1/vamp_easm.egg-info/requires.txt +1 -0
- vamp_easm-1.1/vamp_easm.egg-info/top_level.txt +3 -0
- vamp_easm-1.1/vamp_easm.py +563 -0
- vamp_easm-1.1/vampsec_report.py +959 -0
vamp_easm-1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vamp-easm
|
|
3
|
+
Version: 1.1
|
|
4
|
+
Summary: External Attack Surface Management (EASM) scanner for authorized audits
|
|
5
|
+
Author-email: VampSecure Studios <contact@vampsecurestudios.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Vampsecure-Labs/vamp-easm
|
|
8
|
+
Project-URL: Repository, https://github.com/Vampsecure-Labs/vamp-easm
|
|
9
|
+
Keywords: security,pentest,audit,cybersecurity,vampsecure,easm,attack-surface,external,reconnaissance
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Information Technology
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Security
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: rich>=13.7.0
|
|
20
|
+
|
|
21
|
+
# vamp-easm
|
|
22
|
+
|
|
23
|
+
**Continuous External Attack Surface Management with daily diff tracking**
|
|
24
|
+
|
|
25
|
+
Part of the [VampSecure Labs](https://github.com/Vampsecure-Labs) security toolkit.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Overview
|
|
30
|
+
|
|
31
|
+
`vamp-easm` is a lightweight EASM engine designed to run continuously (via cron or CI/CD) against one or more external domains. Unlike point-in-time scanners, its core value is **delta detection**: each run is compared against the previous snapshot stored in a local SQLite database, surfacing only what changed.
|
|
32
|
+
|
|
33
|
+
### Key Features
|
|
34
|
+
|
|
35
|
+
- **Subdomain enumeration** — queries crt.sh (Certificate Transparency) and HackerTarget via standard urllib (no external dependencies for this layer)
|
|
36
|
+
- **Port scanning** — async TCP connect scan over the top-100 most common ports using `asyncio` + stdlib `socket`; optional `nmap` backend for accuracy
|
|
37
|
+
- **TLS certificate inspection** — hostname, issuer, expiration date, SANs, self-signed detection and SHA-256 fingerprint via stdlib `ssl`
|
|
38
|
+
- **SQLite history** — every scan is stored; diffs are computed against the last completed scan for the same target
|
|
39
|
+
- **Structured findings** — diffs are normalized as VSL findings (prefix `EASM-NNN`) compatible with `vamp-penreport`
|
|
40
|
+
- **Webhook alerts** — POSTs a JSON payload (Slack/Discord/Mattermost compatible) when CRITICAL or HIGH diffs are found
|
|
41
|
+
- **Exit codes** — machine-friendly: `0` clean, `1` HIGH diffs, `2` CRITICAL diffs (CI/CD and monitoring ready)
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Diff Types and Severities
|
|
46
|
+
|
|
47
|
+
| Category | Severity | Description |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| `NUEVO_SUBDOMINIO` | HIGH | A subdomain not seen in the previous scan has appeared |
|
|
50
|
+
| `NUEVO_PUERTO` | MEDIUM | A TCP port is open that was closed in the previous scan |
|
|
51
|
+
| `CERT_EXPIRADO` | HIGH / CRITICAL | TLS certificate expires within 30 days (HIGH) or is already expired (CRITICAL) |
|
|
52
|
+
| `CERT_CAMBIADO` | CRITICAL | TLS fingerprint changed since the last scan — possible re-issue or MitM |
|
|
53
|
+
| `SERVICIO_DESAPARECIDO` | LOW | A previously open port is no longer reachable |
|
|
54
|
+
| `IP_CAMBIADA` | MEDIUM | DNS resolution for a known subdomain returned a different IP |
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# 1. Clone and enter the directory
|
|
62
|
+
git clone https://github.com/Vampsecure-Labs/vamp-easm.git
|
|
63
|
+
cd vamp-easm
|
|
64
|
+
|
|
65
|
+
# 2. Create and activate a virtual environment
|
|
66
|
+
python3 -m venv .venv
|
|
67
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
68
|
+
|
|
69
|
+
# 3. Install dependencies
|
|
70
|
+
pip install -r requirements.txt
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
> **Optional:** install `nmap` on your system and use `--nmap` for more reliable port scanning.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Usage
|
|
78
|
+
|
|
79
|
+
### Scan a target
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Default: top-100 ports, stdlib async scanner
|
|
83
|
+
python vamp_easm.py scan --target example.com
|
|
84
|
+
|
|
85
|
+
# Custom port list
|
|
86
|
+
python vamp_easm.py scan --target example.com --ports 22,80,443,8080,8443
|
|
87
|
+
|
|
88
|
+
# Use nmap as port-scan backend (requires nmap in PATH)
|
|
89
|
+
python vamp_easm.py scan --target example.com --nmap
|
|
90
|
+
|
|
91
|
+
# Export findings as HTML and JSON reports
|
|
92
|
+
python vamp_easm.py scan --target example.com --html --json
|
|
93
|
+
|
|
94
|
+
# Send alerts to a webhook on CRITICAL/HIGH diffs
|
|
95
|
+
python vamp_easm.py scan --target example.com --alert-webhook https://hooks.slack.com/...
|
|
96
|
+
|
|
97
|
+
# Full example
|
|
98
|
+
python vamp_easm.py scan \
|
|
99
|
+
--target example.com \
|
|
100
|
+
--ports top100 \
|
|
101
|
+
--html \
|
|
102
|
+
--alert-webhook "$SLACK_WEBHOOK" \
|
|
103
|
+
--client "AcmeCorp" \
|
|
104
|
+
--engagement "Q3-2026-EASM"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### View scan history
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
python vamp_easm.py history --target example.com
|
|
111
|
+
python vamp_easm.py history --target example.com --limit 20
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### List known assets
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
python vamp_easm.py assets --target example.com
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Export a snapshot
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Export last scan as both JSON and HTML
|
|
124
|
+
python vamp_easm.py export --target example.com
|
|
125
|
+
|
|
126
|
+
# JSON only
|
|
127
|
+
python vamp_easm.py export --target example.com --json
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Cron Example
|
|
133
|
+
|
|
134
|
+
Run a daily EASM scan with Slack alerts and log output:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
0 6 * * * cd /opt/vamp-easm && .venv/bin/python vamp_easm.py scan --target midominio.com --alert-webhook $SLACK_URL >> /var/log/vamp-easm.log 2>&1
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
For CI/CD, use the exit code to gate pipelines:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
python vamp_easm.py scan --target example.com
|
|
144
|
+
EXIT=$?
|
|
145
|
+
if [ $EXIT -eq 2 ]; then
|
|
146
|
+
echo "CRITICAL diffs detected — blocking pipeline"
|
|
147
|
+
exit 1
|
|
148
|
+
elif [ $EXIT -eq 1 ]; then
|
|
149
|
+
echo "HIGH diffs detected — review required"
|
|
150
|
+
fi
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Environment Variables
|
|
156
|
+
|
|
157
|
+
| Variable | Description |
|
|
158
|
+
|---|---|
|
|
159
|
+
| `EASM_ALERT_WEBHOOK` | Webhook URL for alerts (alternative to `--alert-webhook`) |
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Database
|
|
164
|
+
|
|
165
|
+
The SQLite database `vamp_easm.db` is created automatically in the working directory. It contains three tables:
|
|
166
|
+
|
|
167
|
+
- **`scans`** — one row per scan run (UUID, target, timestamps, asset/diff counts)
|
|
168
|
+
- **`assets`** — discovered hosts, subdomains, open ports and services with first/last-seen timestamps
|
|
169
|
+
- **`certs`** — TLS certificate snapshots (issuer, expiration, SANs, SHA-256 fingerprint)
|
|
170
|
+
|
|
171
|
+
The database file is excluded from version control (`.gitignore`). Back it up if you want to preserve historical data.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Integration with vamp-penreport
|
|
176
|
+
|
|
177
|
+
`vamp-easm` exports findings in the VSL standard format used across all VampSecure Labs tools. To include EASM findings in a pentest report:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# 1. Generate JSON snapshot from the last scan
|
|
181
|
+
python vamp_easm.py export --target example.com --json
|
|
182
|
+
|
|
183
|
+
# 2. Merge with vamp-penreport (pass the JSON as an additional findings source)
|
|
184
|
+
python ../vamp-penreport/vamp_penreport.py \
|
|
185
|
+
--findings easm_export_example.com_*.json \
|
|
186
|
+
--client "AcmeCorp" \
|
|
187
|
+
--engagement "Pentest-2026-Q3" \
|
|
188
|
+
--html report_acmecorp.html
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The `EASM-NNN` finding IDs are stable within a single scan run and can be referenced in report narratives.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Dependencies
|
|
196
|
+
|
|
197
|
+
| Package | Purpose |
|
|
198
|
+
|---|---|
|
|
199
|
+
| `aiohttp>=3.9.0` | Async HTTP client for subdomain source queries |
|
|
200
|
+
| `rich>=13.7.0` | Terminal output tables and panels |
|
|
201
|
+
| stdlib only | Port scanning, TLS inspection, DNS queries, alerts |
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## License
|
|
206
|
+
|
|
207
|
+
MIT License — see [LICENSE](LICENSE) for details.
|
|
208
|
+
|
|
209
|
+
© VampSecure Studios — VampSecure Labs Security Research Division
|
|
210
|
+
For authorized penetration testing use only.
|
vamp_easm-1.1/README.md
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# vamp-easm
|
|
2
|
+
|
|
3
|
+
**Continuous External Attack Surface Management with daily diff tracking**
|
|
4
|
+
|
|
5
|
+
Part of the [VampSecure Labs](https://github.com/Vampsecure-Labs) security toolkit.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
`vamp-easm` is a lightweight EASM engine designed to run continuously (via cron or CI/CD) against one or more external domains. Unlike point-in-time scanners, its core value is **delta detection**: each run is compared against the previous snapshot stored in a local SQLite database, surfacing only what changed.
|
|
12
|
+
|
|
13
|
+
### Key Features
|
|
14
|
+
|
|
15
|
+
- **Subdomain enumeration** — queries crt.sh (Certificate Transparency) and HackerTarget via standard urllib (no external dependencies for this layer)
|
|
16
|
+
- **Port scanning** — async TCP connect scan over the top-100 most common ports using `asyncio` + stdlib `socket`; optional `nmap` backend for accuracy
|
|
17
|
+
- **TLS certificate inspection** — hostname, issuer, expiration date, SANs, self-signed detection and SHA-256 fingerprint via stdlib `ssl`
|
|
18
|
+
- **SQLite history** — every scan is stored; diffs are computed against the last completed scan for the same target
|
|
19
|
+
- **Structured findings** — diffs are normalized as VSL findings (prefix `EASM-NNN`) compatible with `vamp-penreport`
|
|
20
|
+
- **Webhook alerts** — POSTs a JSON payload (Slack/Discord/Mattermost compatible) when CRITICAL or HIGH diffs are found
|
|
21
|
+
- **Exit codes** — machine-friendly: `0` clean, `1` HIGH diffs, `2` CRITICAL diffs (CI/CD and monitoring ready)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Diff Types and Severities
|
|
26
|
+
|
|
27
|
+
| Category | Severity | Description |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `NUEVO_SUBDOMINIO` | HIGH | A subdomain not seen in the previous scan has appeared |
|
|
30
|
+
| `NUEVO_PUERTO` | MEDIUM | A TCP port is open that was closed in the previous scan |
|
|
31
|
+
| `CERT_EXPIRADO` | HIGH / CRITICAL | TLS certificate expires within 30 days (HIGH) or is already expired (CRITICAL) |
|
|
32
|
+
| `CERT_CAMBIADO` | CRITICAL | TLS fingerprint changed since the last scan — possible re-issue or MitM |
|
|
33
|
+
| `SERVICIO_DESAPARECIDO` | LOW | A previously open port is no longer reachable |
|
|
34
|
+
| `IP_CAMBIADA` | MEDIUM | DNS resolution for a known subdomain returned a different IP |
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# 1. Clone and enter the directory
|
|
42
|
+
git clone https://github.com/Vampsecure-Labs/vamp-easm.git
|
|
43
|
+
cd vamp-easm
|
|
44
|
+
|
|
45
|
+
# 2. Create and activate a virtual environment
|
|
46
|
+
python3 -m venv .venv
|
|
47
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
48
|
+
|
|
49
|
+
# 3. Install dependencies
|
|
50
|
+
pip install -r requirements.txt
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
> **Optional:** install `nmap` on your system and use `--nmap` for more reliable port scanning.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
### Scan a target
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Default: top-100 ports, stdlib async scanner
|
|
63
|
+
python vamp_easm.py scan --target example.com
|
|
64
|
+
|
|
65
|
+
# Custom port list
|
|
66
|
+
python vamp_easm.py scan --target example.com --ports 22,80,443,8080,8443
|
|
67
|
+
|
|
68
|
+
# Use nmap as port-scan backend (requires nmap in PATH)
|
|
69
|
+
python vamp_easm.py scan --target example.com --nmap
|
|
70
|
+
|
|
71
|
+
# Export findings as HTML and JSON reports
|
|
72
|
+
python vamp_easm.py scan --target example.com --html --json
|
|
73
|
+
|
|
74
|
+
# Send alerts to a webhook on CRITICAL/HIGH diffs
|
|
75
|
+
python vamp_easm.py scan --target example.com --alert-webhook https://hooks.slack.com/...
|
|
76
|
+
|
|
77
|
+
# Full example
|
|
78
|
+
python vamp_easm.py scan \
|
|
79
|
+
--target example.com \
|
|
80
|
+
--ports top100 \
|
|
81
|
+
--html \
|
|
82
|
+
--alert-webhook "$SLACK_WEBHOOK" \
|
|
83
|
+
--client "AcmeCorp" \
|
|
84
|
+
--engagement "Q3-2026-EASM"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### View scan history
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
python vamp_easm.py history --target example.com
|
|
91
|
+
python vamp_easm.py history --target example.com --limit 20
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### List known assets
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
python vamp_easm.py assets --target example.com
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Export a snapshot
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# Export last scan as both JSON and HTML
|
|
104
|
+
python vamp_easm.py export --target example.com
|
|
105
|
+
|
|
106
|
+
# JSON only
|
|
107
|
+
python vamp_easm.py export --target example.com --json
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Cron Example
|
|
113
|
+
|
|
114
|
+
Run a daily EASM scan with Slack alerts and log output:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
0 6 * * * cd /opt/vamp-easm && .venv/bin/python vamp_easm.py scan --target midominio.com --alert-webhook $SLACK_URL >> /var/log/vamp-easm.log 2>&1
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
For CI/CD, use the exit code to gate pipelines:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
python vamp_easm.py scan --target example.com
|
|
124
|
+
EXIT=$?
|
|
125
|
+
if [ $EXIT -eq 2 ]; then
|
|
126
|
+
echo "CRITICAL diffs detected — blocking pipeline"
|
|
127
|
+
exit 1
|
|
128
|
+
elif [ $EXIT -eq 1 ]; then
|
|
129
|
+
echo "HIGH diffs detected — review required"
|
|
130
|
+
fi
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Environment Variables
|
|
136
|
+
|
|
137
|
+
| Variable | Description |
|
|
138
|
+
|---|---|
|
|
139
|
+
| `EASM_ALERT_WEBHOOK` | Webhook URL for alerts (alternative to `--alert-webhook`) |
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Database
|
|
144
|
+
|
|
145
|
+
The SQLite database `vamp_easm.db` is created automatically in the working directory. It contains three tables:
|
|
146
|
+
|
|
147
|
+
- **`scans`** — one row per scan run (UUID, target, timestamps, asset/diff counts)
|
|
148
|
+
- **`assets`** — discovered hosts, subdomains, open ports and services with first/last-seen timestamps
|
|
149
|
+
- **`certs`** — TLS certificate snapshots (issuer, expiration, SANs, SHA-256 fingerprint)
|
|
150
|
+
|
|
151
|
+
The database file is excluded from version control (`.gitignore`). Back it up if you want to preserve historical data.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Integration with vamp-penreport
|
|
156
|
+
|
|
157
|
+
`vamp-easm` exports findings in the VSL standard format used across all VampSecure Labs tools. To include EASM findings in a pentest report:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# 1. Generate JSON snapshot from the last scan
|
|
161
|
+
python vamp_easm.py export --target example.com --json
|
|
162
|
+
|
|
163
|
+
# 2. Merge with vamp-penreport (pass the JSON as an additional findings source)
|
|
164
|
+
python ../vamp-penreport/vamp_penreport.py \
|
|
165
|
+
--findings easm_export_example.com_*.json \
|
|
166
|
+
--client "AcmeCorp" \
|
|
167
|
+
--engagement "Pentest-2026-Q3" \
|
|
168
|
+
--html report_acmecorp.html
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The `EASM-NNN` finding IDs are stable within a single scan run and can be referenced in report narratives.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Dependencies
|
|
176
|
+
|
|
177
|
+
| Package | Purpose |
|
|
178
|
+
|---|---|
|
|
179
|
+
| `aiohttp>=3.9.0` | Async HTTP client for subdomain source queries |
|
|
180
|
+
| `rich>=13.7.0` | Terminal output tables and panels |
|
|
181
|
+
| stdlib only | Port scanning, TLS inspection, DNS queries, alerts |
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## License
|
|
186
|
+
|
|
187
|
+
MIT License — see [LICENSE](LICENSE) for details.
|
|
188
|
+
|
|
189
|
+
© VampSecure Studios — VampSecure Labs Security Research Division
|
|
190
|
+
For authorized penetration testing use only.
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# © VampSecure Studios — VampSecure Labs Security Research Division
|
|
2
|
+
"""
|
|
3
|
+
alerter.py — Sistema de alertas para vamp-easm
|
|
4
|
+
================================================
|
|
5
|
+
Envía notificaciones cuando se detectan diffs de severidad CRITICAL o HIGH.
|
|
6
|
+
|
|
7
|
+
Métodos de alerta disponibles:
|
|
8
|
+
· Webhook HTTP — POST JSON al endpoint configurado
|
|
9
|
+
· Variable de entorno EASM_ALERT_WEBHOOK como alternativa al flag --alert-webhook
|
|
10
|
+
|
|
11
|
+
El payload enviado al webhook sigue el esquema estándar VSL compatible
|
|
12
|
+
con Slack (incoming webhooks), Discord, Mattermost y cualquier receptor
|
|
13
|
+
que acepte JSON.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import urllib.request
|
|
21
|
+
import urllib.error
|
|
22
|
+
from datetime import datetime, timezone
|
|
23
|
+
from typing import List, Optional
|
|
24
|
+
|
|
25
|
+
from .differ import Diff
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
# Severidades que disparan alertas
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
SEVERIDADES_ALERTA = {"CRITICAL", "HIGH"}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
# Construcción del payload de alerta
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
def _construir_payload(
|
|
40
|
+
target: str,
|
|
41
|
+
diffs: List[Diff],
|
|
42
|
+
scan_id: str,
|
|
43
|
+
) -> dict:
|
|
44
|
+
"""
|
|
45
|
+
Construye el payload JSON que se envía al webhook.
|
|
46
|
+
|
|
47
|
+
El formato es compatible con Slack incoming webhooks mediante el campo
|
|
48
|
+
'text' y 'attachments', y también incluye un campo estructurado 'data'
|
|
49
|
+
con todos los diffs para integraciones personalizadas.
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
target : Dominio escaneado
|
|
54
|
+
diffs : Lista de diffs CRITICAL/HIGH a alertar
|
|
55
|
+
scan_id : UUID del escaneo que generó los diffs
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
dict : Payload listo para serializar a JSON
|
|
60
|
+
"""
|
|
61
|
+
ahora = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
|
|
62
|
+
|
|
63
|
+
# Resumen para Slack/Discord (campo 'text')
|
|
64
|
+
resumen_lineas = [f"*[EASM] Alerta de superficie de ataque — {target}*"]
|
|
65
|
+
for d in diffs:
|
|
66
|
+
emoji = "🚨" if d.severidad == "CRITICAL" else "⚠️"
|
|
67
|
+
resumen_lineas.append(f"{emoji} `{d.categoria}` [{d.severidad}] — {d.activo}")
|
|
68
|
+
resumen = "\n".join(resumen_lineas)
|
|
69
|
+
|
|
70
|
+
# Detalles estructurados
|
|
71
|
+
detalles = []
|
|
72
|
+
for d in diffs:
|
|
73
|
+
detalles.append({
|
|
74
|
+
"categoria": d.categoria,
|
|
75
|
+
"severidad": d.severidad,
|
|
76
|
+
"activo": d.activo,
|
|
77
|
+
"descripcion": d.descripcion,
|
|
78
|
+
"evidencia": d.evidencia,
|
|
79
|
+
"finding_id": d.finding.id if d.finding else None,
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
"text": resumen,
|
|
84
|
+
"username": "vamp-easm",
|
|
85
|
+
"attachments": [
|
|
86
|
+
{
|
|
87
|
+
"color": "#c0392b" if any(d.severidad == "CRITICAL" for d in diffs) else "#d35400",
|
|
88
|
+
"title": f"EASM — {target} — {ahora}",
|
|
89
|
+
"text": f"scan_id: `{scan_id}`\n{len(diffs)} diffs alertables",
|
|
90
|
+
"footer": "VampSecure Labs · vamp-easm",
|
|
91
|
+
}
|
|
92
|
+
],
|
|
93
|
+
"data": {
|
|
94
|
+
"tool": "vamp-easm",
|
|
95
|
+
"version": "1.0",
|
|
96
|
+
"target": target,
|
|
97
|
+
"scan_id": scan_id,
|
|
98
|
+
"ts": ahora,
|
|
99
|
+
"diffs": detalles,
|
|
100
|
+
},
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# ---------------------------------------------------------------------------
|
|
105
|
+
# Envío de alertas
|
|
106
|
+
# ---------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
def enviar_webhook(
|
|
109
|
+
url: str,
|
|
110
|
+
target: str,
|
|
111
|
+
diffs: List[Diff],
|
|
112
|
+
scan_id: str,
|
|
113
|
+
timeout: int = 10,
|
|
114
|
+
) -> bool:
|
|
115
|
+
"""
|
|
116
|
+
Envía un POST JSON al webhook especificado con los diffs alertables.
|
|
117
|
+
|
|
118
|
+
Solo procesa diffs de severidad CRITICAL o HIGH. Si no hay ninguno,
|
|
119
|
+
no envía nada.
|
|
120
|
+
|
|
121
|
+
Parameters
|
|
122
|
+
----------
|
|
123
|
+
url : URL del webhook receptor
|
|
124
|
+
target : Dominio escaneado
|
|
125
|
+
diffs : Todos los diffs del escaneo (se filtra por severidad)
|
|
126
|
+
scan_id : UUID del escaneo
|
|
127
|
+
timeout : Segundos de timeout para la petición HTTP
|
|
128
|
+
|
|
129
|
+
Returns
|
|
130
|
+
-------
|
|
131
|
+
bool : True si el envío fue exitoso (HTTP 2xx), False si falló
|
|
132
|
+
"""
|
|
133
|
+
diffs_alertables = [d for d in diffs if d.severidad in SEVERIDADES_ALERTA]
|
|
134
|
+
if not diffs_alertables:
|
|
135
|
+
return True # nada que alertar → éxito
|
|
136
|
+
|
|
137
|
+
payload = _construir_payload(target, diffs_alertables, scan_id)
|
|
138
|
+
body = json.dumps(payload, ensure_ascii=False).encode("utf-8")
|
|
139
|
+
|
|
140
|
+
req = urllib.request.Request(
|
|
141
|
+
url,
|
|
142
|
+
data=body,
|
|
143
|
+
headers={
|
|
144
|
+
"Content-Type": "application/json",
|
|
145
|
+
"User-Agent": "vamp-easm/1.0",
|
|
146
|
+
},
|
|
147
|
+
method="POST",
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
|
152
|
+
return 200 <= resp.status < 300
|
|
153
|
+
except urllib.error.HTTPError as exc:
|
|
154
|
+
return False
|
|
155
|
+
except Exception:
|
|
156
|
+
return False
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def resolver_webhook_url(flag_url: Optional[str]) -> Optional[str]:
|
|
160
|
+
"""
|
|
161
|
+
Resuelve la URL del webhook: prioriza el flag CLI, luego la variable
|
|
162
|
+
de entorno EASM_ALERT_WEBHOOK.
|
|
163
|
+
|
|
164
|
+
Parameters
|
|
165
|
+
----------
|
|
166
|
+
flag_url : Valor del flag --alert-webhook (None si no se proporcionó)
|
|
167
|
+
|
|
168
|
+
Returns
|
|
169
|
+
-------
|
|
170
|
+
str | None : URL del webhook, o None si no está configurado
|
|
171
|
+
"""
|
|
172
|
+
if flag_url:
|
|
173
|
+
return flag_url
|
|
174
|
+
return os.environ.get("EASM_ALERT_WEBHOOK")
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def gestionar_alertas(
|
|
178
|
+
webhook_url: Optional[str],
|
|
179
|
+
target: str,
|
|
180
|
+
diffs: List[Diff],
|
|
181
|
+
scan_id: str,
|
|
182
|
+
) -> None:
|
|
183
|
+
"""
|
|
184
|
+
Punto de entrada principal del sistema de alertas.
|
|
185
|
+
|
|
186
|
+
Comprueba si hay diffs alertables y, si existe un webhook configurado,
|
|
187
|
+
intenta el envío. Los errores de envío no interrumpen la ejecución
|
|
188
|
+
del programa principal.
|
|
189
|
+
|
|
190
|
+
Parameters
|
|
191
|
+
----------
|
|
192
|
+
webhook_url : URL del webhook (None → solo log en consola)
|
|
193
|
+
target : Dominio escaneado
|
|
194
|
+
diffs : Lista de diffs del escaneo
|
|
195
|
+
scan_id : UUID del escaneo
|
|
196
|
+
"""
|
|
197
|
+
diffs_criticos = [d for d in diffs if d.severidad == "CRITICAL"]
|
|
198
|
+
diffs_altos = [d for d in diffs if d.severidad == "HIGH"]
|
|
199
|
+
|
|
200
|
+
if not diffs_criticos and not diffs_altos:
|
|
201
|
+
return
|
|
202
|
+
|
|
203
|
+
if not webhook_url:
|
|
204
|
+
# Sin webhook configurado: el usuario verá los diffs en la salida estándar
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
enviar_webhook(webhook_url, target, diffs, scan_id)
|