@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,290 @@
1
+ #!/usr/bin/env python3
2
+ """Static-analysis scan for command-injection patterns.
3
+
4
+ References:
5
+ CWE-78 Improper Neutralization of Special Elements used in an OS Command
6
+ OWASP A03:2021 Injection
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import re
13
+ import sys
14
+ from pathlib import Path
15
+
16
+ _PLUGIN_ROOT = Path(__file__).resolve().parents[3]
17
+ if str(_PLUGIN_ROOT) not in sys.path:
18
+ sys.path.insert(0, str(_PLUGIN_ROOT))
19
+
20
+ from lib.finding import Finding, Severity # noqa: E402
21
+ from lib.report import emit, exit_code # noqa: E402
22
+
23
+ SKILL_ID = "detecting-command-injection-patterns"
24
+
25
+
26
+ PY_PATTERNS = [
27
+ (
28
+ "Python subprocess with shell=True + f-string",
29
+ Severity.CRITICAL,
30
+ r"subprocess\.(?:run|call|check_output|check_call|Popen)\s*\(\s*f['\"]",
31
+ "python",
32
+ ),
33
+ (
34
+ "Python subprocess with shell=True + concat",
35
+ Severity.CRITICAL,
36
+ r"subprocess\.[a-z_]+\s*\(\s*['\"][^'\"]*['\"]\s*\+\s*\w[^,)]*,[^)]*shell\s*=\s*True",
37
+ "python",
38
+ ),
39
+ (
40
+ "Python subprocess shell=True (any form, needs review)",
41
+ Severity.HIGH,
42
+ r"subprocess\.[a-z_]+\s*\([^)]*shell\s*=\s*True",
43
+ "python",
44
+ ),
45
+ (
46
+ "Python os.system with f-string / concat",
47
+ Severity.CRITICAL,
48
+ r"os\.system\s*\(\s*(?:f['\"]|['\"][^'\"]*['\"]\s*\+)",
49
+ "python",
50
+ ),
51
+ (
52
+ "Python os.popen with interpolation",
53
+ Severity.CRITICAL,
54
+ r"os\.popen\s*\(\s*(?:f['\"]|['\"][^'\"]*['\"]\s*\+)",
55
+ "python",
56
+ ),
57
+ (
58
+ "Python commands.getoutput / getstatusoutput (legacy)",
59
+ Severity.HIGH,
60
+ r"commands\.(?:getoutput|getstatusoutput)\s*\(",
61
+ "python",
62
+ ),
63
+ ]
64
+
65
+ JS_PATTERNS = [
66
+ (
67
+ "Node child_process.exec with template literal",
68
+ Severity.CRITICAL,
69
+ r"(?:child_process|require\(['\"]child_process['\"]\))\.exec(?:Sync)?\s*\(\s*`[^`]*\$\{",
70
+ "javascript",
71
+ ),
72
+ ("Node exec with concat", Severity.CRITICAL, r"\.exec(?:Sync)?\s*\(\s*['\"][^'\"]*['\"]\s*\+\s*\w", "javascript"),
73
+ (
74
+ "Node execFile with shell wrapper",
75
+ Severity.HIGH,
76
+ r"\.execFile(?:Sync)?\s*\(\s*['\"](?:sh|bash|/bin/sh)['\"]",
77
+ "javascript",
78
+ ),
79
+ (
80
+ "Node spawn with shell:true option",
81
+ Severity.HIGH,
82
+ r"\.spawn(?:Sync)?\s*\([^)]*\{[^}]*shell\s*:\s*true",
83
+ "javascript",
84
+ ),
85
+ ]
86
+
87
+ RUBY_PATTERNS = [
88
+ ("Ruby backticks with interpolation", Severity.CRITICAL, r"`[^`]*#\{[^}]+\}[^`]*`", "ruby"),
89
+ ("Ruby Kernel#system with interpolation", Severity.CRITICAL, r"\bsystem\s*\(\s*['\"][^'\"]*#\{", "ruby"),
90
+ ("Ruby Kernel#exec with interpolation", Severity.CRITICAL, r"\bexec\s*\(\s*['\"][^'\"]*#\{", "ruby"),
91
+ (
92
+ "Ruby Open3 with shell wrapper interpolation",
93
+ Severity.HIGH,
94
+ r"Open3\.(?:popen|capture)[a-z0-9_]*\s*\(\s*['\"][^'\"]*#\{",
95
+ "ruby",
96
+ ),
97
+ ]
98
+
99
+ GO_PATTERNS = [
100
+ (
101
+ "Go exec.Command with sh -c + concat",
102
+ Severity.HIGH,
103
+ r"""exec\.Command\s*\(\s*["'](?:sh|bash|/bin/sh|/bin/bash)["']\s*,\s*["']-c["']\s*,\s*[^,)]*\+""",
104
+ "go",
105
+ ),
106
+ ("Go exec.Command with fmt.Sprintf", Severity.HIGH, r"exec\.Command(?:Context)?\s*\([^)]*fmt\.Sprintf", "go"),
107
+ ]
108
+
109
+ PHP_PATTERNS = [
110
+ (
111
+ "PHP system() / exec() with $-interpolation",
112
+ Severity.CRITICAL,
113
+ r"\b(?:system|exec|passthru|shell_exec|popen|proc_open)\s*\(\s*['\"][^'\"]*\$",
114
+ "php",
115
+ ),
116
+ ("PHP backticks with $-interpolation", Severity.CRITICAL, r"`[^`]*\$[a-z_][^`]*`", "php"),
117
+ (
118
+ "PHP escapeshellarg missing on user input",
119
+ Severity.MEDIUM,
120
+ r"\b(?:system|exec|passthru|shell_exec)\s*\(\s*\$_(?:GET|POST|REQUEST|COOKIE)",
121
+ "php",
122
+ ),
123
+ ]
124
+
125
+ JAVA_PATTERNS = [
126
+ (
127
+ "Java Runtime.exec(String) with concat",
128
+ Severity.HIGH,
129
+ r"Runtime\.getRuntime\(\)\.exec\s*\(\s*['\"][^'\"]*['\"]\s*\+",
130
+ "java",
131
+ ),
132
+ (
133
+ "Java ProcessBuilder(String) single-string form",
134
+ Severity.MEDIUM,
135
+ r"new ProcessBuilder\s*\(\s*[a-z_]+(?:\s*\+\s*[a-z_]+)+\s*\)",
136
+ "java",
137
+ ),
138
+ ]
139
+
140
+
141
+ LANG_EXT_MAP = {
142
+ "python": {".py"},
143
+ "javascript": {".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"},
144
+ "ruby": {".rb"},
145
+ "go": {".go"},
146
+ "java": {".java", ".kt", ".scala"},
147
+ "php": {".php"},
148
+ }
149
+ LANG_PATTERNS = {
150
+ "python": PY_PATTERNS,
151
+ "javascript": JS_PATTERNS,
152
+ "ruby": RUBY_PATTERNS,
153
+ "go": GO_PATTERNS,
154
+ "java": JAVA_PATTERNS,
155
+ "php": PHP_PATTERNS,
156
+ }
157
+ SKIP_DIRS = {
158
+ "node_modules",
159
+ ".git",
160
+ "dist",
161
+ "build",
162
+ "target",
163
+ ".cache",
164
+ ".pnpm-store",
165
+ ".venv",
166
+ "venv",
167
+ "__pycache__",
168
+ ".astro",
169
+ ".next",
170
+ ".nuxt",
171
+ "vendor",
172
+ }
173
+ TEST_DIRS = {"tests", "test", "__tests__", "spec", "specs"}
174
+ MAX_FILE_SIZE = 5 * 1024 * 1024
175
+
176
+
177
+ def should_skip_path(path: Path, include_tests: bool) -> bool:
178
+ parts = set(path.parts)
179
+ if parts & SKIP_DIRS:
180
+ return True
181
+ if not include_tests and parts & TEST_DIRS:
182
+ return True
183
+ return False
184
+
185
+
186
+ def detect_language(path: Path, langs: set[str]) -> str | None:
187
+ suf = path.suffix.lower()
188
+ for lang in langs:
189
+ if suf in LANG_EXT_MAP[lang]:
190
+ return lang
191
+ return None
192
+
193
+
194
+ def scan_file(file_path: Path, repo_root: Path, langs: set[str]) -> list[Finding]:
195
+ findings = []
196
+ lang = detect_language(file_path, langs)
197
+ if lang is None:
198
+ return findings
199
+ try:
200
+ if file_path.stat().st_size > MAX_FILE_SIZE:
201
+ return findings
202
+ text = file_path.read_text(encoding="utf-8", errors="ignore")
203
+ except (OSError, ValueError):
204
+ return findings
205
+ try:
206
+ rel = str(file_path.relative_to(repo_root))
207
+ except ValueError:
208
+ rel = str(file_path)
209
+
210
+ for title, sev, pattern, _lang in LANG_PATTERNS[lang]:
211
+ for m in re.finditer(pattern, text, re.IGNORECASE | re.MULTILINE):
212
+ line_no = text[: m.start()].count("\n") + 1
213
+ snippet = text.splitlines()[line_no - 1].strip()[:160]
214
+ findings.append(
215
+ Finding(
216
+ skill_id=SKILL_ID,
217
+ title=f"{title} at {rel}:{line_no}",
218
+ severity=sev,
219
+ target=f"{rel}:{line_no}",
220
+ detail=(
221
+ f"File {rel} line {line_no} matches the {title} "
222
+ f"pattern: `{snippet}`. If the interpolated value "
223
+ "is user-reachable, this is a command-injection vector."
224
+ ),
225
+ remediation=(
226
+ "Replace the shell-string call with the argument-vector "
227
+ "form. Python: subprocess.run([cmd, arg], shell=False). "
228
+ "Node: spawn(cmd, [arg], {shell: false}). Ruby: "
229
+ "Open3.capture3(cmd, arg). Go: exec.Command(cmd, arg). "
230
+ "Java: new ProcessBuilder(Arrays.asList(cmd, arg)). "
231
+ "See references/PLAYBOOK.md."
232
+ ),
233
+ cwe_id="CWE-78",
234
+ affected_control="OWASP A03:2021",
235
+ evidence=(("file", rel), ("line", line_no), ("language", lang), ("snippet", snippet)),
236
+ )
237
+ )
238
+ return findings
239
+
240
+
241
+ def walk_repo(root: Path, include_tests: bool, langs: set[str]) -> list[Path]:
242
+ out = []
243
+ valid_exts = set()
244
+ for lang in langs:
245
+ valid_exts |= LANG_EXT_MAP[lang]
246
+ for p in root.rglob("*"):
247
+ if not p.is_file():
248
+ continue
249
+ if should_skip_path(p, include_tests):
250
+ continue
251
+ if p.suffix.lower() not in valid_exts:
252
+ continue
253
+ out.append(p)
254
+ return out
255
+
256
+
257
+ def main(argv: list[str] | None = None) -> int:
258
+ parser = argparse.ArgumentParser(description="Command-injection pattern scanner")
259
+ parser.add_argument("path", type=Path)
260
+ parser.add_argument("--output", default=None)
261
+ parser.add_argument("--format", choices=("json", "jsonl", "markdown"), default="markdown")
262
+ parser.add_argument("--min-severity", choices=("critical", "high", "medium", "low", "info"), default="info")
263
+ parser.add_argument("--include-tests", action="store_true")
264
+ parser.add_argument("--languages", default="all")
265
+ args = parser.parse_args(argv)
266
+
267
+ if args.languages == "all":
268
+ langs = set(LANG_PATTERNS.keys())
269
+ else:
270
+ langs = {lang.strip() for lang in args.languages.split(",") if lang.strip() in LANG_PATTERNS}
271
+
272
+ root = args.path.resolve()
273
+ if not root.exists():
274
+ sys.stderr.write(f"ERROR: path does not exist: {root}\n")
275
+ return 2
276
+
277
+ files = walk_repo(root, args.include_tests, langs)
278
+ findings: list[Finding] = []
279
+ for f in files:
280
+ findings.extend(scan_file(f, root, langs))
281
+
282
+ floor = Severity(args.min_severity)
283
+ findings = [f for f in findings if f.severity.numeric >= floor.numeric]
284
+
285
+ emit(findings, args.output, args.format, str(root))
286
+ return exit_code(findings)
287
+
288
+
289
+ if __name__ == "__main__":
290
+ sys.exit(main())
@@ -0,0 +1,207 @@
1
+ ---
2
+ name: detecting-debug-endpoints
3
+ description: |
4
+ Probe a target for accidentally-public admin / debug / introspection
5
+ endpoints — Spring Boot Actuator, Apache server-status, Prometheus
6
+ metrics, GraphQL playground, Swagger UI, phpMyAdmin, JMX-over-HTTP
7
+ (Jolokia), Elasticsearch _cat, Kibana / Grafana / Eureka / Consul
8
+ panels.
9
+ Use when: post-deploy verification, security audit before SOC2,
10
+ inheriting a system you didn't build, or a bug bounty hints at an
11
+ exposed introspection panel.
12
+ Threshold: any of the canonical 40+ admin/debug paths returns 200,
13
+ 302 to a login, or framework-specific JSON shape (e.g., Actuator
14
+ returning a _links object, server-status HTML body containing
15
+ the Apache Server Status title).
16
+ Trigger with: "check debug endpoints", "actuator exposure", "admin
17
+ panel scan", "graphql playground check".
18
+ allowed-tools:
19
+ - Read
20
+ - Bash(python3:*)
21
+ - Bash(curl:*)
22
+ disallowed-tools:
23
+ - Bash(rm:*)
24
+ - Edit(/etc/*)
25
+ version: 3.0.0-dev
26
+ author: Jeremy Longshore <jeremy@intentsolutions.io>
27
+ license: MIT
28
+ compatibility: Designed for Claude Code
29
+ tags:
30
+ - security
31
+ - information-disclosure
32
+ - admin-panels
33
+ - actuator
34
+ - pentest
35
+ ---
36
+
37
+ # Detecting Debug Endpoints
38
+
39
+ ## Overview
40
+
41
+ Modern web stacks ship rich introspection by default. Spring Boot
42
+ Actuator exposes `/actuator/env` (every environment variable),
43
+ `/actuator/heapdump` (a live heap snapshot that contains credentials),
44
+ `/actuator/jolokia` (JMX bean invocation = pre-auth RCE in some
45
+ configurations). Apache `mod_status` exposes `/server-status` with
46
+ internal IPs, request counts, and the URL of every active request.
47
+ Prometheus `/metrics` exposes operational telemetry that often
48
+ includes connection-string-bearing labels by accident. phpMyAdmin
49
+ exposes the entire database if unauthenticated.
50
+
51
+ These are not bugs in the frameworks. They're features that ship
52
+ enabled-by-default for development convenience and stay enabled in
53
+ production because nobody disabled them at install time. The probe
54
+ set covers the canonical 40+ paths and grades each by the response
55
+ fingerprint specific to that framework.
56
+
57
+ ## When the skill produces findings
58
+
59
+ | Finding | Severity | Threshold | Affected control |
60
+ |---|---|---|---|
61
+ | Spring Boot Actuator `/env` exposed | **CRITICAL** | 200 + body has `"propertySources"` | OWASP A05:2021 |
62
+ | Spring Boot Actuator `/heapdump` exposed | **CRITICAL** | 200 + `Content-Type: application/octet-stream` + multi-MB body | CWE-200 |
63
+ | Spring Boot Actuator `/jolokia` exposed | **CRITICAL** | 200 + body has `"agent":"jolokia"` | CWE-749 |
64
+ | phpMyAdmin reachable | **CRITICAL** | 200 + HTML body contains "phpMyAdmin" + login form | OWASP A07:2021 |
65
+ | Prometheus `/metrics` exposed | **HIGH** | 200 + body has `# HELP` or `# TYPE` lines | CWE-200 |
66
+ | Apache `mod_status` exposed | **HIGH** | 200 + body contains "Apache Server Status" | CWE-200 |
67
+ | Spring Boot Actuator `/actuator` index | **HIGH** | 200 + body has `"_links"` JSON | OWASP A05:2021 |
68
+ | Generic `/admin` returning 200 (not 401/403) | **HIGH** | 200 + HTML body with admin-shaped UI | CWE-285 |
69
+ | Elasticsearch `_cat` exposed | **HIGH** | 200 + body matches `health\s+status\s+index` | CWE-200 |
70
+ | GraphQL Playground on prod | **MEDIUM** | 200 + body contains `"GraphQLPlayground"` | CWE-200 |
71
+ | Swagger UI on prod | **MEDIUM** | 200 + body contains `"swagger-ui"` | CWE-200 |
72
+ | Spring Boot Actuator `/health` exposed | **MEDIUM** | 200 + body has `"status":"UP"` | CWE-200 |
73
+ | `phpinfo` page on prod | **MEDIUM** | 200 + body has `PHP Version` heading | CWE-200 |
74
+ | `/robots.txt` discloses admin paths | **LOW** | 200 + `Disallow:` lines mentioning `/admin` | CWE-200 |
75
+
76
+ ## Prerequisites
77
+
78
+ - Python 3.9+ with `requests`
79
+ - Authorization for non-local targets
80
+
81
+ ## Instructions
82
+
83
+ ### Step 1 — Confirm Authorization
84
+
85
+ ```text
86
+ "Do you have authorization to perform admin / debug endpoint
87
+ discovery on this target? I need confirmation before proceeding."
88
+ ```
89
+
90
+ ### Step 2 — Run the scanner
91
+
92
+ ```bash
93
+ python3 ${CLAUDE_PLUGIN_ROOT}/skills/detecting-debug-endpoints/scripts/probe_debug.py \
94
+ https://target.example.com \
95
+ --authorized
96
+ ```
97
+
98
+ Options:
99
+
100
+ ```
101
+ Usage: probe_debug.py URL [OPTIONS]
102
+
103
+ Options:
104
+ --authorized Attest authorization (required for non-local)
105
+ --output FILE Write findings to FILE
106
+ --format FMT json | jsonl | markdown (default: markdown)
107
+ --min-severity SEV (default: info)
108
+ --timeout SECS Per-probe timeout (default: 10)
109
+ --paths-file FILE Override the default probe set with a custom list
110
+ --include-redirects Treat 302/303 to /login as findings (debug panel
111
+ exists but auth gates it — still worth noting)
112
+ ```
113
+
114
+ The scanner sends a GET for each path. For 200 responses, it inspects
115
+ the body for the framework-specific fingerprint to confirm a true
116
+ positive (not the app's SPA index page). For 302 responses to common
117
+ login paths, the panel exists but auth is in front — flagged only
118
+ with `--include-redirects`.
119
+
120
+ ### Step 3 — Interpret findings
121
+
122
+ CRITICAL = direct compromise vector (env vars / heapdump / Jolokia /
123
+ phpMyAdmin). Ship same-hour fix: take the endpoint behind authn or
124
+ disable it. Audit for prior exploitation.
125
+
126
+ HIGH = information disclosure substantial enough to drive subsequent
127
+ attacks (server-status reveals request URLs including session tokens
128
+ in query strings; /metrics labels often contain connection strings;
129
+ /admin reachable means brute-force can start).
130
+
131
+ MEDIUM = posture hardening (health checks, swagger).
132
+
133
+ ### Step 4 — Cross-skill chaining
134
+
135
+ After this skill, suggest:
136
+
137
+ - `detecting-exposed-secrets-files` (#6) — same deploy mistake. If
138
+ `/server-status` is reachable, `.git/` often is too.
139
+ - `auditing-cors-policy` (#3) — if a GraphQL or admin endpoint is
140
+ reachable AND has open CORS, the attack chain compounds.
141
+
142
+ ## Examples
143
+
144
+ ### Example 1 — Inheriting a system audit
145
+
146
+ User: "We just acquired example.io. Quick audit of admin surface."
147
+
148
+ ```bash
149
+ python3 ${CLAUDE_PLUGIN_ROOT}/skills/detecting-debug-endpoints/scripts/probe_debug.py \
150
+ https://example.io --authorized --min-severity medium
151
+ ```
152
+
153
+ Commonly surfaces forgotten `/server-status` on Apache hosts,
154
+ `/actuator/*` left enabled from Spring Boot defaults, leftover
155
+ `/phpmyadmin` from initial install.
156
+
157
+ ### Example 2 — Spring Boot Actuator paranoia sweep
158
+
159
+ User: "We use Spring Boot heavily. Show me everywhere Actuator is
160
+ reachable."
161
+
162
+ ```bash
163
+ for ENDPOINT in $(cat spring-services.txt); do
164
+ python3 ${CLAUDE_PLUGIN_ROOT}/skills/detecting-debug-endpoints/scripts/probe_debug.py \
165
+ "$ENDPOINT" --authorized --format jsonl
166
+ done | jq 'select(.title | contains("Actuator"))'
167
+ ```
168
+
169
+ ### Example 3 — CI gate against accidental re-enablement
170
+
171
+ ```yaml
172
+ - name: Debug-endpoint guard
173
+ run: |
174
+ python3 plugins/security/penetration-tester/skills/detecting-debug-endpoints/scripts/probe_debug.py \
175
+ "${{ secrets.STAGING_URL }}" \
176
+ --authorized --min-severity high
177
+ ```
178
+
179
+ Exit 1 fails the deploy if any HIGH or CRITICAL endpoint exposure
180
+ appears. Catches the regression where a debug profile gets enabled
181
+ in a `application-prod.yml` by accident.
182
+
183
+ ## Output
184
+
185
+ JSON / JSONL / Markdown per `lib/report.py`. Exit codes: 0 clean, 1
186
+ high/critical, 2 error.
187
+
188
+ ## Error Handling
189
+
190
+ - **SPA catches every URL with 200** → use `--check-only` semantics
191
+ (default: fingerprint check filters out SPA matches).
192
+ - **WAF / CDN blocks the scanner** → expected for some targets.
193
+ Coordinate with the target's security team for an allowlist; or
194
+ run the scanner from inside the target's network if you have
195
+ authorized internal access.
196
+ - **Connection error** → exit 2 with underlying error.
197
+
198
+ ## Resources
199
+
200
+ - `references/THEORY.md` — Per-framework reasoning: why Actuator,
201
+ mod_status, Prometheus, GraphQL Playground, Swagger, phpMyAdmin
202
+ each matter; canonical fingerprints
203
+ - `references/PLAYBOOK.md` — Per-framework remediation: Spring Boot
204
+ Actuator authn, Apache mod_status `<Location>` deny, Prometheus
205
+ Bearer-token, GraphQL introspection toggle, Swagger profile gate
206
+ - `../analyzing-tls-config/references/AUTHORIZATION.md` — Active-scan
207
+ authorization pattern