@intentsolutionsio/penetration-tester 2.0.0 → 3.0.4

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.
Files changed (112) hide show
  1. package/.claude-plugin/plugin.json +8 -3
  2. package/README.md +8 -0
  3. package/commands/pentest.md +5 -0
  4. package/package.json +8 -3
  5. package/skills/analyzing-tls-config/SKILL.md +221 -0
  6. package/skills/analyzing-tls-config/references/AUTHORIZATION.md +133 -0
  7. package/skills/analyzing-tls-config/references/PLAYBOOK.md +267 -0
  8. package/skills/analyzing-tls-config/references/THEORY.md +128 -0
  9. package/skills/analyzing-tls-config/scripts/analyze_tls.py +415 -0
  10. package/skills/auditing-cors-policy/SKILL.md +186 -0
  11. package/skills/auditing-cors-policy/references/PLAYBOOK.md +220 -0
  12. package/skills/auditing-cors-policy/references/THEORY.md +142 -0
  13. package/skills/auditing-cors-policy/scripts/audit_cors.py +350 -0
  14. package/skills/auditing-npm-dependencies/SKILL.md +254 -0
  15. package/skills/auditing-npm-dependencies/references/PLAYBOOK.md +175 -0
  16. package/skills/auditing-npm-dependencies/references/THEORY.md +122 -0
  17. package/skills/auditing-npm-dependencies/scripts/audit_npm.py +408 -0
  18. package/skills/auditing-python-dependencies/SKILL.md +251 -0
  19. package/skills/auditing-python-dependencies/references/PLAYBOOK.md +193 -0
  20. package/skills/auditing-python-dependencies/references/THEORY.md +122 -0
  21. package/skills/auditing-python-dependencies/scripts/audit_python.py +459 -0
  22. package/skills/checking-http-security-headers/SKILL.md +176 -0
  23. package/skills/checking-http-security-headers/references/PLAYBOOK.md +212 -0
  24. package/skills/checking-http-security-headers/references/THEORY.md +137 -0
  25. package/skills/checking-http-security-headers/scripts/check_headers.py +362 -0
  26. package/skills/checking-license-compliance/SKILL.md +225 -0
  27. package/skills/checking-license-compliance/references/PLAYBOOK.md +161 -0
  28. package/skills/checking-license-compliance/references/THEORY.md +152 -0
  29. package/skills/checking-license-compliance/scripts/check_licenses.py +461 -0
  30. package/skills/composing-vulnerability-report/SKILL.md +212 -0
  31. package/skills/composing-vulnerability-report/references/PLAYBOOK.md +180 -0
  32. package/skills/composing-vulnerability-report/references/THEORY.md +178 -0
  33. package/skills/composing-vulnerability-report/scripts/compose_report.py +396 -0
  34. package/skills/confirming-pentest-authorization/SKILL.md +247 -0
  35. package/skills/confirming-pentest-authorization/references/PLAYBOOK.md +189 -0
  36. package/skills/confirming-pentest-authorization/references/THEORY.md +167 -0
  37. package/skills/confirming-pentest-authorization/scripts/check_authorization.py +457 -0
  38. package/skills/defining-pentest-scope/SKILL.md +227 -0
  39. package/skills/defining-pentest-scope/references/PLAYBOOK.md +238 -0
  40. package/skills/defining-pentest-scope/references/THEORY.md +170 -0
  41. package/skills/defining-pentest-scope/scripts/define_scope.py +472 -0
  42. package/skills/detecting-command-injection-patterns/SKILL.md +144 -0
  43. package/skills/detecting-command-injection-patterns/references/PLAYBOOK.md +302 -0
  44. package/skills/detecting-command-injection-patterns/references/THEORY.md +206 -0
  45. package/skills/detecting-command-injection-patterns/scripts/scan_cmdi.py +290 -0
  46. package/skills/detecting-debug-endpoints/SKILL.md +207 -0
  47. package/skills/detecting-debug-endpoints/references/PLAYBOOK.md +402 -0
  48. package/skills/detecting-debug-endpoints/references/THEORY.md +218 -0
  49. package/skills/detecting-debug-endpoints/scripts/probe_debug.py +518 -0
  50. package/skills/detecting-directory-listing/SKILL.md +206 -0
  51. package/skills/detecting-directory-listing/references/PLAYBOOK.md +277 -0
  52. package/skills/detecting-directory-listing/references/THEORY.md +203 -0
  53. package/skills/detecting-directory-listing/scripts/probe_directory_listing.py +180 -0
  54. package/skills/detecting-eval-exec-usage/SKILL.md +128 -0
  55. package/skills/detecting-eval-exec-usage/references/PLAYBOOK.md +306 -0
  56. package/skills/detecting-eval-exec-usage/references/THEORY.md +159 -0
  57. package/skills/detecting-eval-exec-usage/scripts/scan_eval.py +223 -0
  58. package/skills/detecting-exposed-secrets-files/SKILL.md +179 -0
  59. package/skills/detecting-exposed-secrets-files/references/PLAYBOOK.md +274 -0
  60. package/skills/detecting-exposed-secrets-files/references/THEORY.md +174 -0
  61. package/skills/detecting-exposed-secrets-files/scripts/probe_secrets.py +207 -0
  62. package/skills/detecting-insecure-deserialization/SKILL.md +148 -0
  63. package/skills/detecting-insecure-deserialization/references/PLAYBOOK.md +333 -0
  64. package/skills/detecting-insecure-deserialization/references/THEORY.md +199 -0
  65. package/skills/detecting-insecure-deserialization/scripts/scan_deserialization.py +250 -0
  66. package/skills/detecting-sql-injection-patterns/SKILL.md +161 -0
  67. package/skills/detecting-sql-injection-patterns/references/PLAYBOOK.md +317 -0
  68. package/skills/detecting-sql-injection-patterns/references/THEORY.md +261 -0
  69. package/skills/detecting-sql-injection-patterns/scripts/scan_sqli.py +354 -0
  70. package/skills/detecting-ssl-cert-issues/SKILL.md +182 -0
  71. package/skills/detecting-ssl-cert-issues/references/PLAYBOOK.md +203 -0
  72. package/skills/detecting-ssl-cert-issues/references/THEORY.md +133 -0
  73. package/skills/detecting-ssl-cert-issues/scripts/check_cert_chain.py +481 -0
  74. package/skills/detecting-weak-cryptography/SKILL.md +147 -0
  75. package/skills/detecting-weak-cryptography/references/PLAYBOOK.md +466 -0
  76. package/skills/detecting-weak-cryptography/references/THEORY.md +194 -0
  77. package/skills/detecting-weak-cryptography/scripts/scan_weak_crypto.py +417 -0
  78. package/skills/fingerprinting-server-software/SKILL.md +191 -0
  79. package/skills/fingerprinting-server-software/references/PLAYBOOK.md +337 -0
  80. package/skills/fingerprinting-server-software/references/THEORY.md +183 -0
  81. package/skills/fingerprinting-server-software/scripts/fingerprint_server.py +347 -0
  82. package/skills/generating-executive-summary/SKILL.md +261 -0
  83. package/skills/generating-executive-summary/references/PLAYBOOK.md +201 -0
  84. package/skills/generating-executive-summary/references/THEORY.md +195 -0
  85. package/skills/generating-executive-summary/scripts/exec_summary.py +538 -0
  86. package/skills/mapping-findings-to-owasp-top10/SKILL.md +235 -0
  87. package/skills/mapping-findings-to-owasp-top10/references/PLAYBOOK.md +193 -0
  88. package/skills/mapping-findings-to-owasp-top10/references/THEORY.md +160 -0
  89. package/skills/mapping-findings-to-owasp-top10/scripts/map_owasp.py +540 -0
  90. package/skills/performing-penetration-testing/SKILL.md +282 -190
  91. package/skills/performing-penetration-testing/references/OWASP_TOP_10.md +22 -0
  92. package/skills/performing-penetration-testing/references/REMEDIATION_PLAYBOOK.md +46 -0
  93. package/skills/performing-penetration-testing/references/SECURITY_HEADERS.md +41 -0
  94. package/skills/performing-penetration-testing/scripts/code_security_scanner.py +144 -79
  95. package/skills/performing-penetration-testing/scripts/dependency_auditor.py +116 -93
  96. package/skills/performing-penetration-testing/scripts/security_scanner.py +574 -446
  97. package/skills/probing-dangerous-http-methods/SKILL.md +182 -0
  98. package/skills/probing-dangerous-http-methods/references/PLAYBOOK.md +234 -0
  99. package/skills/probing-dangerous-http-methods/references/THEORY.md +145 -0
  100. package/skills/probing-dangerous-http-methods/scripts/probe_methods.py +263 -0
  101. package/skills/recording-pentest-engagement/SKILL.md +253 -0
  102. package/skills/recording-pentest-engagement/references/PLAYBOOK.md +203 -0
  103. package/skills/recording-pentest-engagement/references/THEORY.md +195 -0
  104. package/skills/recording-pentest-engagement/scripts/record_engagement.py +461 -0
  105. package/skills/scanning-for-hardcoded-secrets/SKILL.md +215 -0
  106. package/skills/scanning-for-hardcoded-secrets/references/PLAYBOOK.md +325 -0
  107. package/skills/scanning-for-hardcoded-secrets/references/THEORY.md +175 -0
  108. package/skills/scanning-for-hardcoded-secrets/scripts/scan_secrets.py +395 -0
  109. package/skills/tracing-transitive-vulnerabilities/SKILL.md +235 -0
  110. package/skills/tracing-transitive-vulnerabilities/references/PLAYBOOK.md +233 -0
  111. package/skills/tracing-transitive-vulnerabilities/references/THEORY.md +138 -0
  112. package/skills/tracing-transitive-vulnerabilities/scripts/trace_vulns.py +484 -0
@@ -0,0 +1,238 @@
1
+ # PLAYBOOK — Scope Templates and Hand-Off Patterns
2
+
3
+ ## Per-engagement-type scope templates
4
+
5
+ ### External web app pentest
6
+
7
+ ```yaml
8
+ in_scope_targets:
9
+ - host: app.acme.example
10
+ notes: production web app
11
+ - host: api.acme.example
12
+ notes: REST API; rate-limit 60 req/min observed
13
+ - url: https://app.acme.example/admin/
14
+ notes: admin UI (test as authorized admin user)
15
+ out_of_scope_targets:
16
+ - host: payments.acme.example
17
+ reason: PCI scope, separate authz required
18
+ - host: legal.acme.example
19
+ reason: third-party hosted; not customer's
20
+ ```
21
+
22
+ ### Internal network pentest
23
+
24
+ ```yaml
25
+ in_scope_targets:
26
+ - cidr: 10.50.0.0/16
27
+ notes: corporate user range
28
+ - cidr: 10.51.0.0/16
29
+ notes: developer subnet
30
+ - cidr: 10.52.0.0/24
31
+ notes: test infrastructure
32
+ out_of_scope_targets:
33
+ - cidr: 10.99.0.0/16
34
+ reason: production DB tier, separate authz
35
+ - cidr: 10.250.0.0/24
36
+ reason: physical surveillance VLAN
37
+ - cidr: 10.251.0.0/24
38
+ reason: VOIP (avoid call disruption)
39
+ ```
40
+
41
+ ### Red-team engagement
42
+
43
+ ```yaml
44
+ in_scope_targets:
45
+ - cidr: 203.0.113.0/24
46
+ notes: customer ASN block 1
47
+ - cidr: 198.51.100.0/24
48
+ notes: customer ASN block 2
49
+ - host: "*.acme.example"
50
+ notes: any subdomain of acme.example
51
+ - cloud_account: aws:123456789012
52
+ notes: production AWS account
53
+ out_of_scope_targets:
54
+ - host: ceo.acme.example
55
+ reason: executive personal device
56
+ - cidr: 203.0.113.250/32
57
+ reason: SOC bastion (would tip off SOC)
58
+ ```
59
+
60
+ ### Cloud account pentest (AWS)
61
+
62
+ ```yaml
63
+ in_scope_targets:
64
+ - cloud_account: aws:123456789012
65
+ notes: prod-1 account; full read scope
66
+ - cloud_account: aws:210987654321
67
+ notes: prod-2 account; identity-services only
68
+ out_of_scope_targets:
69
+ - cloud_account: aws:999999999999
70
+ reason: management account; out of agreed scope
71
+ ```
72
+
73
+ ### SaaS tenant pentest
74
+
75
+ ```yaml
76
+ in_scope_targets:
77
+ - saas_tenant: okta:acme-corp
78
+ notes: customer Okta tenant; SSO config audit
79
+ - saas_tenant: auth0:acme
80
+ notes: customer Auth0 tenant
81
+ out_of_scope_targets:
82
+ - saas_tenant: salesforce:acme
83
+ reason: separate vendor-authz required
84
+ ```
85
+
86
+ Many SaaS vendors require explicit prior notification before
87
+ pentesting (Salesforce, Okta, etc. all have their own pentest
88
+ authorization forms). The skill flags `saas:` entries informationally;
89
+ the operator should verify the vendor's own authz separately.
90
+
91
+ ## Allowlist emission patterns per scanner
92
+
93
+ ### nmap
94
+
95
+ ```bash
96
+ python3 ./scripts/define_scope.py --roe roe.yaml --emit-allowlist /tmp/allowed.txt
97
+ nmap -iL /tmp/allowed.txt -sV -oN nmap-scan.txt
98
+ ```
99
+
100
+ ### Burp Suite Target → Scope
101
+
102
+ Burp uses regex-based scope rules. Convert the allowlist by
103
+ escaping dots and accepting any port:
104
+
105
+ ```python
106
+ # Quick converter
107
+ import re
108
+ with open("/tmp/allowed.txt") as f:
109
+ for line in f:
110
+ entry = line.strip()
111
+ # Convert hostname/CIDR to Burp regex
112
+ print(re.escape(entry))
113
+ ```
114
+
115
+ Paste output into Burp's "Include in scope" with "Use advanced
116
+ scope control" enabled.
117
+
118
+ ### AWS WAF allowlist
119
+
120
+ ```bash
121
+ python3 - <<EOF
122
+ import json
123
+ ips = open("/tmp/allowed.txt").read().splitlines()
124
+ v4 = [ip for ip in ips if ":" not in ip]
125
+ print(json.dumps({"Addresses": v4}, indent=2))
126
+ EOF
127
+ ```
128
+
129
+ Pipe the output to `aws wafv2 update-ip-set` to install the
130
+ allowlist.
131
+
132
+ ### Cloud security tools (ScoutSuite, Prowler, CloudSploit)
133
+
134
+ Cloud scope is by account ID, not IP. Read the `cloud_account`
135
+ entries from the normalized targets JSON:
136
+
137
+ ```bash
138
+ jq -r '.[] | select(.type=="cloud") | .normalized' /tmp/targets.json
139
+ ```
140
+
141
+ ## Scope-extension protocol
142
+
143
+ When testing reveals a target that should have been in scope but
144
+ wasn't:
145
+
146
+ 1. Halt testing of the extension target.
147
+ 2. Request a scope amendment from the authorizer.
148
+ 3. Authorizer signs an extension YAML:
149
+
150
+ ```yaml
151
+ amendment_id: ACME-2026-Q2-PENTEST-001-AMEND-01
152
+ amendment_to: ACME-2026-Q2-PENTEST-001
153
+ amendment_date: 2026-06-15T10:00:00Z
154
+ in_scope_targets:
155
+ - host: newly-discovered.acme.example
156
+ notes: discovered during port-scan of cidr 203.0.113.0/24; serves admin UI
157
+ signature_block:
158
+ signer: jane.doe@acme.example
159
+ signed_at: 2026-06-15T11:00:00Z
160
+ signature: |
161
+ <signature>
162
+ ```
163
+
164
+ 4. Re-run this skill with `--extension`.
165
+ 5. Verify the new scope is clean before resuming testing.
166
+
167
+ ## Allowlist file format
168
+
169
+ The `--emit-allowlist` output is one entry per line:
170
+
171
+ ```
172
+ 203.0.113.10
173
+ 203.0.113.0/24
174
+ 2001:db8::10
175
+ 2001:db8::/64
176
+ ```
177
+
178
+ Hostnames are NOT in the allowlist (DNS resolves at scan time;
179
+ see THEORY.md). The normalized targets JSON is the source of
180
+ truth for hostnames + URLs + cloud accounts + SaaS tenants.
181
+
182
+ ## Normalized targets JSON format
183
+
184
+ ```json
185
+ [
186
+ {"raw": "app.acme.example", "type": "hostname", "normalized": "app.acme.example"},
187
+ {"raw": "203.0.113.0/24", "type": "cidrv4", "normalized": "203.0.113.0/24"},
188
+ {"raw": "https://api.acme.example/v2", "type": "url", "normalized": "https://api.acme.example/v2"},
189
+ {"raw": "aws:123456789012", "type": "cloud", "normalized": "aws:123456789012"}
190
+ ]
191
+ ```
192
+
193
+ Downstream skills consume this JSON via `--targets-file` argument
194
+ patterns (planned across the cluster 1-4 skills).
195
+
196
+ ## Common scope-mistake patterns
197
+
198
+ | Pattern | Fix |
199
+ |---|---|
200
+ | `host: 203.0.113.0/24` | Use `cidr:` not `host:` |
201
+ | `host: https://app.acme.example` | Use `url:` not `host:` |
202
+ | `cidr: 203.0.113.10` | Single IP without mask; CIDR /32 implied but explicit is better |
203
+ | Hostname with trailing dot (`acme.example.`) | DNS-canonical form; strip the dot |
204
+ | `*.acme.example/admin` | Mixes wildcard + path; not supported |
205
+ | Cloud account with hyphenated number `aws:1234-5678-9012` | Use bare number, no hyphens |
206
+ | Unicode in hostname (`münich.acme.example`) | Convert to Punycode (`xn--mnich-rta.acme.example`) |
207
+
208
+ ## Pre-scan gate (CI)
209
+
210
+ ```bash
211
+ python3 ./scripts/define_scope.py --roe roe.yaml --min-severity high \
212
+ --format json --output scope-issues.json
213
+ jq -e '. == []' scope-issues.json || {
214
+ echo "::error::Scope issues block testing"
215
+ exit 1
216
+ }
217
+ ```
218
+
219
+ This pattern works for engagements where the ROE is checked into
220
+ a private engagement repo with CI.
221
+
222
+ ## Scope drift detection between engagements
223
+
224
+ When running multiple engagements for the same customer over time,
225
+ diff this engagement's scope against the previous:
226
+
227
+ ```bash
228
+ python3 ./scripts/define_scope.py --roe engagements/acme-2026-q1/roe.yaml \
229
+ --emit-targets engagements/acme-2026-q1/targets.json
230
+ python3 ./scripts/define_scope.py --roe engagements/acme-2026-q2/roe.yaml \
231
+ --emit-targets engagements/acme-2026-q2/targets.json
232
+ diff <(jq -S .[].normalized engagements/acme-2026-q1/targets.json) \
233
+ <(jq -S .[].normalized engagements/acme-2026-q2/targets.json)
234
+ ```
235
+
236
+ Scope changes between engagements are common (customer added new
237
+ infra, decommissioned old) — but they should be explicit and
238
+ documented, not silent.
@@ -0,0 +1,170 @@
1
+ # THEORY — Scope as the Load-Bearing Artifact
2
+
3
+ ## Why scope is the load-bearing legal artifact
4
+
5
+ Authorization is the gate (see `confirming-pentest-authorization`),
6
+ but SCOPE is the perimeter the authorization applies to. A signed
7
+ ROE that says "test ACME's stuff" authorizes nothing in practice
8
+ because it doesn't define ACME's stuff. The scope list is the
9
+ projection of "what we're allowed to touch" into a list of concrete
10
+ targets a tester or tool can verify against.
11
+
12
+ The skill's purpose is to convert the human-readable ROE scope
13
+ section into a machine-readable, syntactically-validated, conflict-
14
+ checked artifact that downstream cluster 1-4 skills can consume
15
+ without ambiguity.
16
+
17
+ ## Target-type taxonomy
18
+
19
+ Real-world scope lists mix several target types. The taxonomy:
20
+
21
+ | Type | Use | Validation |
22
+ |---|---|---|
23
+ | Hostname | `app.acme.example` | Valid DNS character set, length |
24
+ | Wildcard | `*.acme.example` | Wildcard prefix + valid suffix |
25
+ | IPv4 | `203.0.113.10` | Valid 4-octet form |
26
+ | IPv4 CIDR | `203.0.113.0/24` | Valid network + mask |
27
+ | IPv6 | `2001:db8::10` | Valid hex form |
28
+ | IPv6 CIDR | `2001:db8::/32` | Valid network + mask |
29
+ | URL with path | `https://app.acme.example/api` | Valid URL parse + non-empty netloc |
30
+ | Cloud account | `aws:123456789012` | Recognized cloud prefix |
31
+ | SaaS tenant | `okta:acme-corp` | Recognized SaaS prefix |
32
+
33
+ Mixing types in one list is normal; the skill's `--emit-targets`
34
+ output preserves the type tagging so downstream tools can route
35
+ each entry to the right scanner.
36
+
37
+ ## Why DNS resolution is NOT done at scope-definition time
38
+
39
+ It would be tempting to resolve every hostname to its current IP
40
+ at scope-definition time and bake the IPs into the allowlist. This
41
+ is wrong, for two reasons:
42
+
43
+ 1. **DNS resolution is itself a probe.** A DNS query against the
44
+ target's authoritative DNS server (or against a recursive
45
+ resolver that forwards to it) is detectable by sophisticated
46
+ defenders. By the governance model, no probes happen before
47
+ scope is locked.
48
+
49
+ 2. **DNS changes during the engagement.** Resolving `app.acme.example`
50
+ to `203.0.113.10` at scope-definition time, then having the
51
+ customer's load balancer rotate to `203.0.113.11` mid-engagement,
52
+ means the tester's IP allowlist is stale. Hostname-based scope
53
+ resolves at scan time and tracks current DNS state.
54
+
55
+ The downstream cluster 1 skills resolve hostnames at scan time
56
+ themselves. The allowlist this skill emits contains the explicit
57
+ IPs/CIDRs the ROE listed, not resolved hostnames.
58
+
59
+ ## CIDR overlap detection
60
+
61
+ Python's `ipaddress` module provides exact CIDR-overlap checks
62
+ via `IPv4Network.overlaps()` / `IPv6Network.overlaps()`. The skill
63
+ uses these directly.
64
+
65
+ A subtle case: `203.0.113.0/24` overlaps `203.0.113.128/25` (the
66
+ second is a sub-range of the first). If the ROE has the /24 in
67
+ in-scope and the /25 in out-of-scope, the skill emits a CRITICAL
68
+ finding: probing within the /25 sub-range would violate the
69
+ exclusion.
70
+
71
+ The customer's authorizer decides the resolution:
72
+
73
+ - Narrow in-scope to `203.0.113.0/25` (excludes the /25 explicitly)
74
+ - Remove the out-of-scope /25 entry (broaden authorization)
75
+ - Keep both and require per-target check at scan time
76
+
77
+ ## Known third-party SaaS ranges
78
+
79
+ The skill ships an illustrative (NOT exhaustive) map of well-known
80
+ cloud / CDN / SaaS ranges. The most common ones in customer scope
81
+ lists by accident:
82
+
83
+ - AWS — customer mistakes their AWS-hosted endpoint's CDN front
84
+ for their own infrastructure.
85
+ - Cloudflare — customer's domain resolves to Cloudflare; the IP
86
+ is Cloudflare's, not the customer's.
87
+ - GitHub — customer's hosted-status page or docs site is on GitHub
88
+ Pages.
89
+ - Vercel / Netlify — customer's marketing site frontend.
90
+
91
+ Probing these without separate authorization is testing the SaaS
92
+ vendor's infrastructure, which is almost universally prohibited
93
+ without the SaaS vendor's own pentest-authorization process.
94
+
95
+ Real engagements should consult the authoritative published IP
96
+ ranges:
97
+
98
+ - AWS: https://ip-ranges.amazonaws.com/ip-ranges.json
99
+ - Cloudflare: https://www.cloudflare.com/ips/
100
+ - GCP: https://www.gstatic.com/ipranges/cloud.json
101
+ - Azure: published quarterly as a JSON file
102
+
103
+ A future skill enhancement could ingest these authoritative
104
+ sources at scope-definition time and verify against current data.
105
+
106
+ ## Reserved ranges
107
+
108
+ The skill flags RFC1918 (`10/8`, `172.16/12`, `192.168/16`),
109
+ link-local (`169.254/16`), multicast (`224/4`), loopback
110
+ (`127/8`), and IPv6 equivalents.
111
+
112
+ In an internal pentest, RFC1918 ranges are expected — these are
113
+ the customer's internal networks. The skill flags them as MEDIUM
114
+ rather than HIGH/CRITICAL: "verify intentional." The customer's
115
+ acknowledgement of the internal-pentest context resolves the
116
+ finding.
117
+
118
+ In an external pentest, RFC1918 in scope is a configuration error —
119
+ the tester would be testing their own network, not the customer's.
120
+ The skill cannot determine engagement type from the ROE alone, so
121
+ the operator interprets the finding in context.
122
+
123
+ ## Wildcard subdomain semantics
124
+
125
+ `*.acme.example` is a scope-by-pattern. It says "any subdomain of
126
+ acme.example is in scope." The skill flags wildcards as INFO
127
+ because they're not malformed but they ARE broad — the customer
128
+ should confirm intent.
129
+
130
+ Wildcard expansion happens at scan time, when the tester:
131
+
132
+ 1. Enumerates subdomains via passive sources (Certificate
133
+ Transparency, DNS history, brute-force).
134
+ 2. Each enumerated subdomain is checked against the wildcard's
135
+ pattern.
136
+ 3. Matched subdomains become concrete targets.
137
+
138
+ The cluster 2 fingerprinting skills handle subdomain enumeration.
139
+
140
+ ## Scope hygiene patterns
141
+
142
+ Three patterns characterize well-defined scope lists:
143
+
144
+ 1. **Explicit inclusion + explicit exclusion.** Always have both
145
+ lists, even if one is empty. "Nothing else is included"
146
+ beats "I assume the rest is excluded."
147
+
148
+ 2. **Type-tagged entries.** Use the schema's `host:` / `cidr:` /
149
+ `url:` keys explicitly. Bare string entries work but require
150
+ the skill to guess the type, which can produce subtle errors.
151
+
152
+ 3. **Notes on every entry.** Why is this in scope? Why is this
153
+ out? Future you, six months from now, won't remember.
154
+
155
+ ## When scope changes mid-engagement
156
+
157
+ The protocol:
158
+
159
+ 1. Tester requests amendment via the engagement's documented
160
+ communication channel.
161
+ 2. Authorizer signs an amendment YAML referencing the original
162
+ ROE's `engagement_id`.
163
+ 3. Tester archives the amendment alongside the original ROE.
164
+ 4. Re-run this skill with `--extension` pointing at the
165
+ amendment.
166
+ 5. The new combined scope becomes the authoritative scope from
167
+ the amendment timestamp forward.
168
+
169
+ Never extend scope by verbal agreement. The whole point of the
170
+ ROE schema is that scope changes are auditable.