optional-skills/security/web-pentest/references/scope-enforcement.md
The pentest skill is dangerous because Hermes can drive network tools unattended. The single most important rule: every active request must target a host the operator authorized. This file is the procedure.
engagement/authorization.md — what the operator wrote down.engagement/scope.txt — the machine-readable allowlist.If any of those three disagree, you STOP and ask. Don't try to reconcile.
One target per line. Comments with #.
# Hostnames — resolved at use time
localhost
127.0.0.1
::1
staging.example.com
api-staging.example.com
# CIDR — internal labs only, requires operator OK in writing
192.168.50.0/24
10.0.5.0/24
Wildcards are NOT supported. If you need *.staging.example.com, list
each host explicitly. This is on purpose: subdomain wildcards in
authorization scope are how unauthorized testing happens.
Before any active request, extract the target host from the command or URL and confirm it's in scope.
| Surface | Where the host lives | Example |
|---|---|---|
curl URL | The URL | curl https://staging.example.com/login |
curl --resolve HOST:PORT:ADDR | HOST | reject — resolve overrides scope |
nmap TARGET | Each TARGET arg | nmap 10.0.5.5 staging.example.com |
whatweb URL | The URL | whatweb https://staging.example.com |
browser_navigate(url) | The URL | python-side: extract host from url |
| Tool-driven HTTP (sqlmap, wfuzz, gobuster) | -u, -h, target arg | depends on tool |
For URLs: urllib.parse.urlparse(url).hostname.lower().
For raw IPs: keep as IP, check against CIDR entries with
ipaddress.ip_address(host) in ipaddress.ip_network(cidr).
For every active request, before you press enter:
--resolve aliasing.)If any answer is "no" or "not sure," STOP and ask the operator.
staging.example.com →
auth.example.com is a different host. Stop, re-confirm.app.staging.example.com may CNAME to
prod-cluster.aws.example.com. Resolve and check IP, not just name.169.254.169.254 is not in any sane
scope.txt. If your SSRF candidate resolves there, you're probably
testing against a real cloud host and need explicit approval before
the probe.localhost may be someone else's service.
Confirm with the operator that 127.0.0.1 means what they think.If you can't decide whether a host is in scope:
DEFAULT: out of scope.
Stop the agent. Ask the operator. Resume only after written confirmation. There is no penalty for asking; there is significant penalty for testing the wrong host.
Every active request should append to engagement/request-log.jsonl:
{"ts": "2026-05-25T03:14:15Z", "method": "GET", "url": "https://staging.example.com/api/users", "host": "staging.example.com", "in_scope": true, "phase": "recon", "result_status": 200, "evidence_ref": "evidence/recon.md#endpoints"}
This is your audit trail. If anyone ever asks "why did the pentest agent hit X?" you can answer from this log.