create-dwy 0.5.0 → 0.6.0

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 (156) hide show
  1. package/bin/index.js +1 -1
  2. package/package.json +2 -2
  3. package/src/create.js +61 -37
  4. package/src/sync.js +213 -53
  5. package/templates/claude-global/CLAUDE.md +92 -0
  6. package/templates/claude-global/rules/dwy-android-core.md +610 -0
  7. package/templates/claude-global/rules/dwy-api-design.md +110 -0
  8. package/templates/claude-global/rules/dwy-db-migration.md +46 -0
  9. package/templates/claude-global/rules/dwy-flutter-core.md +564 -0
  10. package/templates/claude-global/rules/dwy-flutter-network.md +222 -0
  11. package/templates/claude-global/rules/dwy-flutter-router.md +97 -0
  12. package/templates/claude-global/rules/dwy-flutter-testing.md +94 -0
  13. package/templates/claude-global/rules/dwy-git-commit-convention.md +82 -0
  14. package/templates/claude-global/rules/dwy-git-security.md +53 -0
  15. package/templates/claude-global/rules/dwy-payload-limits.md +178 -0
  16. package/templates/claude-global/rules/{prefer-open-source.md → dwy-prefer-open-source.md} +4 -0
  17. package/templates/claude-global/rules/dwy-project-structure.md +181 -0
  18. package/templates/claude-global/rules/{python-code-style.md → dwy-python-core.md} +314 -357
  19. package/templates/claude-global/rules/dwy-python-fastapi.md +272 -0
  20. package/templates/claude-global/rules/dwy-python-orm.md +61 -0
  21. package/templates/claude-global/rules/{backend-security.md → dwy-python-security.md} +116 -79
  22. package/templates/claude-global/rules/dwy-python-testing.md +53 -0
  23. package/templates/claude-global/rules/{server-security.md → dwy-server-security.md} +1 -2
  24. package/templates/claude-global/rules/dwy-tdd-development.md +70 -0
  25. package/templates/claude-global/rules/dwy-tech-stack.md +60 -0
  26. package/templates/claude-global/rules/dwy-vue-api.md +146 -0
  27. package/templates/claude-global/rules/{vue-code-style.md → dwy-vue-core.md} +361 -152
  28. package/templates/claude-global/rules/dwy-vue-pinia.md +75 -0
  29. package/templates/claude-global/rules/dwy-vue-testing.md +102 -0
  30. package/templates/claude-global/settings.json +20 -0
  31. package/templates/claude-global/skills/dwy-deploy-audit/SKILL.md +481 -0
  32. package/templates/claude-global/skills/dwy-deploy-audit/references/checks-data.md +117 -0
  33. package/templates/claude-global/skills/dwy-deploy-audit/references/checks-network.md +74 -0
  34. package/templates/claude-global/skills/dwy-deploy-audit/references/checks-runtime.md +184 -0
  35. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_base.sh +39 -0
  36. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_capacity.sh +278 -0
  37. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_db.sh +122 -0
  38. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_docker.sh +249 -0
  39. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_env.sh +64 -0
  40. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_https.sh +60 -0
  41. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_logs.sh +209 -0
  42. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_nginx.sh +92 -0
  43. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_resilience.sh +157 -0
  44. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_secrets.sh +327 -0
  45. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_services.sh +77 -0
  46. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_ssh.sh +56 -0
  47. package/templates/claude-global/skills/dwy-deploy-audit/scripts/run_all.sh +105 -0
  48. package/templates/claude-global/skills/dwy-docker/SKILL.md +187 -0
  49. package/templates/claude-global/skills/dwy-docker/references/ask-templates.md +142 -0
  50. package/templates/claude-global/skills/dwy-docker/references/templates.md +479 -0
  51. package/templates/claude-global/skills/dwy-docker/references/version-rules.md +76 -0
  52. package/templates/claude-global/skills/dwy-docker/scripts/query_dockerhub.py +204 -0
  53. package/templates/claude-global/skills/dwy-docker/scripts/scan_images.sh +185 -0
  54. package/templates/claude-global/skills/dwy-eapi/SKILL.md +433 -0
  55. package/templates/claude-global/skills/dwy-eapi/references/tasks-integration-guide.md +387 -0
  56. package/templates/claude-global/skills/dwy-ekit/SKILL.md +85 -0
  57. package/templates/claude-global/skills/dwy-ekit/references/data.md +135 -0
  58. package/templates/claude-global/skills/dwy-ekit/references/form.md +68 -0
  59. package/templates/claude-global/skills/dwy-ekit/references/request.md +183 -0
  60. package/templates/claude-global/skills/dwy-ekit/references/ui.md +109 -0
  61. package/templates/claude-global/skills/{dwy-frontend-eui → dwy-eui}/SKILL.md +129 -15
  62. package/templates/claude-global/skills/dwy-eui/references/eui-design-guide.md +257 -0
  63. package/templates/claude-global/skills/dwy-eui/references/eui-integration-guide.md +302 -0
  64. package/templates/claude-global/skills/dwy-eui/references/eui-landing-design-guide.md +437 -0
  65. package/templates/claude-global/skills/dwy-fullstack-scaffold/SKILL.md +214 -0
  66. package/templates/claude-global/skills/dwy-mirror-source/SKILL.md +275 -0
  67. package/templates/claude-global/skills/dwy-mirror-source/references/dockerfile-snippets.md +228 -0
  68. package/templates/claude-global/skills/dwy-mirror-source/references/mirror-providers.md +150 -0
  69. package/templates/claude-global/skills/dwy-mirror-source/scripts/__pycache__/check_mirrors.cpython-313.pyc +0 -0
  70. package/templates/claude-global/skills/dwy-mirror-source/scripts/apply_mirrors.py +666 -0
  71. package/templates/claude-global/skills/dwy-mirror-source/scripts/check_mirrors.py +868 -0
  72. package/templates/claude-global/skills/dwy-pentest/SKILL.md +344 -0
  73. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__init__.py +1 -0
  74. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/__init__.cpython-311.pyc +0 -0
  75. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/config.cpython-311.pyc +0 -0
  76. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/executor.cpython-311.pyc +0 -0
  77. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/severity.cpython-311.pyc +0 -0
  78. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/validator.cpython-311.pyc +0 -0
  79. package/templates/claude-global/skills/dwy-pentest/scripts/lib/config.py +109 -0
  80. package/templates/claude-global/skills/dwy-pentest/scripts/lib/executor.py +157 -0
  81. package/templates/claude-global/skills/dwy-pentest/scripts/lib/severity.py +96 -0
  82. package/templates/claude-global/skills/dwy-pentest/scripts/lib/validator.py +113 -0
  83. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_auth.py +289 -0
  84. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_recon.py +217 -0
  85. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_report.py +343 -0
  86. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_scan.py +205 -0
  87. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_ssl.py +208 -0
  88. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_vuln.py +205 -0
  89. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_web.py +330 -0
  90. package/templates/claude-global/skills/dwy-pentest/scripts/pyproject.toml +14 -0
  91. package/templates/claude-global/skills/dwy-pentest/scripts/setup_tools.sh +97 -0
  92. package/templates/claude-global/skills/dwy-pentest/scripts/uv.lock +415 -0
  93. package/templates/claude-global/skills/dwy-publish/SKILL.md +135 -0
  94. package/templates/claude-global/skills/dwy-vue-core/SKILL.md +890 -0
  95. package/templates/project/backend/.dwy.hbs +5 -0
  96. package/templates/project/backend/.python-version +1 -0
  97. package/templates/project/backend/CLAUDE.md.hbs +40 -0
  98. package/templates/project/{web/backend → backend}/alembic/script.py.mako +2 -0
  99. package/templates/project/{web/backend → backend}/alembic.ini.hbs +0 -1
  100. package/templates/project/backend/deploy.sh.hbs +18 -0
  101. package/templates/project/{web → backend}/docker-compose.prod.yml.hbs +1 -1
  102. package/templates/project/{web/backend → backend}/pyproject.toml.hbs +4 -5
  103. package/templates/project/{web/backend → backend}/src/app/config.py.hbs +1 -1
  104. package/templates/project/backend/src/app/database.py +10 -0
  105. package/templates/project/backend/src/app/dependencies.py +5 -0
  106. package/templates/project/{web/backend → backend}/src/app/main.py.hbs +3 -3
  107. package/templates/project/backend/src/app/models/base.py +5 -0
  108. package/templates/project/web/CLAUDE.md.hbs +29 -36
  109. package/templates/project/web/{frontend → apps/web}/index.html.hbs +1 -0
  110. package/templates/project/web/{frontend → apps/web}/package.json.hbs +4 -3
  111. package/templates/project/web/apps/web/src/App.vue +9 -0
  112. package/templates/project/web/apps/web/src/api/client.ts +5 -0
  113. package/templates/project/web/apps/web/src/composables/.gitkeep +0 -0
  114. package/templates/project/web/apps/web/src/layouts/DefaultLayout.vue +8 -0
  115. package/templates/project/web/{frontend → apps/web}/src/router/index.ts +1 -1
  116. package/templates/project/web/{frontend → apps/web}/src/stores/auth.ts +4 -2
  117. package/templates/project/web/apps/web/src/style.css +2 -0
  118. package/templates/project/web/apps/web/src/types/router.d.ts +8 -0
  119. package/templates/project/web/apps/web/src/views/HomeView.vue +8 -0
  120. package/templates/project/web/apps/web/tsconfig.app.json +18 -0
  121. package/templates/project/web/apps/web/tsconfig.json +6 -0
  122. package/templates/project/web/{frontend → apps/web}/vite.config.ts.hbs +7 -1
  123. package/templates/project/web/package.json.hbs +16 -0
  124. package/templates/project/web/packages/.gitkeep +0 -0
  125. package/templates/project/web/pnpm-workspace.yaml +3 -0
  126. package/templates/claude-global/rules/agent-team-execution.md +0 -138
  127. package/templates/claude-global/rules/agent-team-patterns.md +0 -157
  128. package/templates/claude-global/rules/git-security.md +0 -144
  129. package/templates/claude-global/skills/dwy-auto-sync/SKILL.md +0 -52
  130. package/templates/claude-global/skills/dwy-backend-base/SKILL.md +0 -279
  131. package/templates/claude-global/skills/dwy-frontend-core/SKILL.md +0 -181
  132. package/templates/project/web/.claude/commands/deploy.md +0 -4
  133. package/templates/project/web/backend/src/app/models/base.py +0 -5
  134. package/templates/project/web/deploy.sh.hbs +0 -113
  135. package/templates/project/web/frontend/src/App.vue +0 -3
  136. package/templates/project/web/frontend/src/api/client.ts +0 -13
  137. package/templates/project/web/frontend/src/style.css +0 -1
  138. package/templates/project/web/frontend/src/views/HomeView.vue +0 -5
  139. package/templates/project/web/frontend/tsconfig.app.json +0 -14
  140. package/templates/project/web/frontend/tsconfig.json +0 -4
  141. /package/templates/{project/web/backend → claude-global/skills/dwy-pentest/scripts}/.python-version +0 -0
  142. /package/templates/project/{web/backend → backend}/Dockerfile +0 -0
  143. /package/templates/project/{web/backend → backend}/alembic/env.py.hbs +0 -0
  144. /package/templates/{claude-global/skills → project/backend/alembic/versions}/.gitkeep +0 -0
  145. /package/templates/project/{web → backend}/docker-compose.dev.yml.hbs +0 -0
  146. /package/templates/project/{web/backend → backend}/src/app/__init__.py +0 -0
  147. /package/templates/project/{web/backend → backend}/src/app/models/__init__.py +0 -0
  148. /package/templates/project/{web/backend → backend}/src/app/routers/__init__.py +0 -0
  149. /package/templates/project/{web/backend → backend}/src/app/routers/health.py +0 -0
  150. /package/templates/project/{web/backend → backend}/src/app/schemas/__init__.py +0 -0
  151. /package/templates/project/{web/backend → backend}/src/app/services/__init__.py +0 -0
  152. /package/templates/project/{web/backend → backend}/tests/__init__.py +0 -0
  153. /package/templates/project/{web/backend → backend}/tests/conftest.py.hbs +0 -0
  154. /package/templates/project/{web/backend/alembic/versions → mobile}/.gitkeep +0 -0
  155. /package/templates/project/web/{frontend → apps/web}/src/components/.gitkeep +0 -0
  156. /package/templates/project/web/{frontend → apps/web}/src/main.ts +0 -0
@@ -0,0 +1,330 @@
1
+ """阶段 4:Web 应用测试 — 目录枚举、SQL 注入探测、XSS 探测(HTTP 探测全异步并发)。
2
+
3
+ 用法:
4
+ uv run pentest_web.py --target <url> --mode dir|sqli|xss --output-dir <dir>
5
+ """
6
+
7
+ import argparse
8
+ import asyncio
9
+ import sys
10
+ import time
11
+ from pathlib import Path
12
+
13
+ import httpx
14
+ from rich.console import Console
15
+
16
+ from lib.executor import AuditLog, run_command, save_json_result
17
+ from lib.validator import validate_target
18
+
19
+ console = Console()
20
+
21
+ XSS_PROBE = "<dwy-xss-test>"
22
+ XSS_PAYLOADS = [
23
+ '<dwy-xss-test>',
24
+ '"><dwy-xss-test>',
25
+ "'-dwy-xss-test-'",
26
+ 'javascript:dwy-xss-test',
27
+ '<img src=x onerror=dwy-xss-test>',
28
+ ]
29
+
30
+ COMMON_PATHS = [
31
+ "/admin", "/login", "/api", "/backup", "/.env", "/.git/HEAD",
32
+ "/.htaccess", "/wp-admin", "/wp-login.php", "/phpmyadmin",
33
+ "/robots.txt", "/sitemap.xml", "/server-status", "/server-info",
34
+ "/.DS_Store", "/web.config", "/crossdomain.xml", "/favicon.ico",
35
+ "/api/v1", "/api/docs", "/swagger.json", "/openapi.json",
36
+ "/graphql", "/.well-known/security.txt", "/config.json",
37
+ "/package.json", "/composer.json", "/Dockerfile",
38
+ ]
39
+ SENSITIVE_PATTERNS = [".env", ".git", ".htaccess", "backup", ".sql", ".bak", "config", "wp-admin", "phpmyadmin"]
40
+ SQLI_PROBES = ["'", "\"", "' OR '1'='1", "1' OR '1'='1' --", "1 UNION SELECT NULL--"]
41
+ SQL_ERROR_PATTERNS = [
42
+ "SQL syntax", "mysql_fetch", "ORA-", "PostgreSQL",
43
+ "SQLite", "SQLITE_ERROR", "Unclosed quotation",
44
+ "unterminated string", "syntax error",
45
+ ]
46
+ SECURITY_HEADERS = {
47
+ "X-Content-Type-Options": "防止 MIME 嗅探",
48
+ "X-Frame-Options": "防止点击劫持",
49
+ "Content-Security-Policy": "内容安全策略",
50
+ "X-XSS-Protection": "XSS 过滤",
51
+ "Strict-Transport-Security": "HSTS",
52
+ }
53
+
54
+
55
+ async def _async_probe_path(client: httpx.AsyncClient, base_url: str, path: str) -> dict | None:
56
+ try:
57
+ resp = await client.get(f"{base_url}{path}")
58
+ if resp.status_code in (200, 201, 204, 301, 302, 307, 401, 403):
59
+ return {
60
+ "path": path,
61
+ "status": resp.status_code,
62
+ "size": len(resp.content),
63
+ "redirect": resp.headers.get("location", ""),
64
+ }
65
+ except Exception:
66
+ pass
67
+ return None
68
+
69
+
70
+ async def _async_enum_paths(url: str, paths: list[str]) -> list[dict]:
71
+ """并发探测路径列表。"""
72
+ limits = httpx.Limits(max_connections=20, max_keepalive_connections=10)
73
+ async with httpx.AsyncClient(timeout=5, verify=False, follow_redirects=False, limits=limits) as client:
74
+ tasks = [_async_probe_path(client, url, p) for p in paths]
75
+ results = await asyncio.gather(*tasks, return_exceptions=True)
76
+ return [r for r in results if isinstance(r, dict)]
77
+
78
+
79
+ def scan_directory(url: str, audit: AuditLog, output_dir: Path) -> list[dict]:
80
+ """目录/文件枚举(gobuster → ffuf → 内置并发探测)。"""
81
+ findings = []
82
+ console.print(f"[bold]目录枚举: {url}[/bold]")
83
+
84
+ gobuster_output = output_dir / "gobuster_raw.txt"
85
+ cmd = [
86
+ "gobuster", "dir",
87
+ "-u", url,
88
+ "-w", "/usr/share/wordlists/dirb/common.txt",
89
+ "-o", str(gobuster_output),
90
+ "-t", "20",
91
+ "-q",
92
+ "--no-error",
93
+ "-s", "200,201,204,301,302,307,401,403",
94
+ ]
95
+
96
+ result = run_command(cmd, timeout=300, audit=audit)
97
+
98
+ if result.returncode == -3:
99
+ console.print("[yellow]gobuster 未安装,尝试 ffuf...[/yellow]")
100
+ ffuf_output = output_dir / "ffuf_raw.json"
101
+ cmd = [
102
+ "ffuf",
103
+ "-u", f"{url}/FUZZ",
104
+ "-w", "/usr/share/wordlists/dirb/common.txt",
105
+ "-o", str(ffuf_output),
106
+ "-of", "json",
107
+ "-t", "20",
108
+ "-mc", "200,201,204,301,302,307,401,403",
109
+ "-s",
110
+ ]
111
+ result = run_command(cmd, timeout=300, audit=audit)
112
+
113
+ if result.returncode == -3:
114
+ console.print("[yellow]gobuster/ffuf 均未安装,使用内置路径列表(并发)...[/yellow]")
115
+ findings = asyncio.run(_async_enum_paths(url, COMMON_PATHS))
116
+ elif result.returncode == 0 and gobuster_output.exists():
117
+ with open(gobuster_output) as f:
118
+ for line in f:
119
+ parts = line.strip().split()
120
+ if parts:
121
+ findings.append({
122
+ "path": parts[0],
123
+ "status": int(parts[1].strip("()")) if len(parts) > 1 else 200,
124
+ "size": 0,
125
+ })
126
+
127
+ for f in findings:
128
+ f["sensitive"] = any(p in f["path"].lower() for p in SENSITIVE_PATTERNS)
129
+ if f["sensitive"]:
130
+ console.print(f" [red][敏感][/red] {f['path']} — {f['status']}")
131
+ else:
132
+ console.print(f" [green]{f['path']}[/green] — {f['status']}")
133
+
134
+ console.print(f" 发现 {len(findings)} 个路径")
135
+ return findings
136
+
137
+
138
+ def scan_sqli(url: str, audit: AuditLog, output_dir: Path) -> list[dict]:
139
+ """SQL 注入探测(sqlmap,仅检测不利用)。"""
140
+ findings = []
141
+ console.print(f"[bold]SQL 注入探测: {url}[/bold]")
142
+
143
+ sqlmap_output = output_dir / "sqlmap_output"
144
+ cmd = [
145
+ "sqlmap",
146
+ "-u", url,
147
+ "--batch",
148
+ "--level", "3",
149
+ "--risk", "2",
150
+ "--technique=BEU",
151
+ "--output-dir", str(sqlmap_output),
152
+ "--forms",
153
+ "--crawl=2",
154
+ "--threads=4",
155
+ "--random-agent",
156
+ ]
157
+
158
+ result = run_command(cmd, timeout=600, audit=audit)
159
+
160
+ if result.blocked:
161
+ console.print(f"[red]sqlmap 被安全策略拦截: {result.block_reason}[/red]")
162
+ return findings
163
+
164
+ if result.returncode == -3:
165
+ console.print("[yellow]sqlmap 未安装,使用基础 SQL 注入并发探针...[/yellow]")
166
+ return asyncio.run(_async_basic_sqli_probe(url))
167
+
168
+ if result.returncode == 0:
169
+ output_text = result.stdout
170
+ if "is vulnerable" in output_text.lower() or "injectable" in output_text.lower():
171
+ for line in output_text.split("\n"):
172
+ if "Parameter:" in line or "injectable" in line.lower():
173
+ findings.append({
174
+ "type": "SQL Injection",
175
+ "detail": line.strip(),
176
+ "url": url,
177
+ "severity": "High",
178
+ "cvss": 8.0,
179
+ })
180
+ console.print(f" [red][SQLi] {line.strip()}[/red]")
181
+
182
+ if not findings:
183
+ console.print(" [green]未发现 SQL 注入点[/green]")
184
+
185
+ return findings
186
+
187
+
188
+ async def _async_sqli_one(client: httpx.AsyncClient, url: str, probe: str) -> dict | None:
189
+ test_url = f"{url}{probe}" if "?" in url else f"{url}?id={probe}"
190
+ try:
191
+ resp = await client.get(test_url)
192
+ for pattern in SQL_ERROR_PATTERNS:
193
+ if pattern.lower() in resp.text.lower():
194
+ return {
195
+ "type": "SQL Injection (Basic Probe)",
196
+ "detail": f"SQL 错误特征 '{pattern}' 在探针 '{probe}' 后出现",
197
+ "url": test_url,
198
+ "severity": "High",
199
+ "cvss": 8.0,
200
+ "_pattern": pattern,
201
+ }
202
+ except Exception:
203
+ return None
204
+ return None
205
+
206
+
207
+ async def _async_basic_sqli_probe(url: str) -> list[dict]:
208
+ """基础 SQL 注入并发探针。"""
209
+ findings: list[dict] = []
210
+ async with httpx.AsyncClient(timeout=10, verify=False, follow_redirects=True) as client:
211
+ tasks = [_async_sqli_one(client, url, probe) for probe in SQLI_PROBES]
212
+ results = await asyncio.gather(*tasks, return_exceptions=True)
213
+
214
+ for r in results:
215
+ if isinstance(r, dict):
216
+ console.print(f" [red][SQLi] 发现 SQL 错误: {r['_pattern']}[/red]")
217
+ r.pop("_pattern", None)
218
+ findings.append(r)
219
+ return findings
220
+
221
+
222
+ async def _async_xss_one(client: httpx.AsyncClient, url: str, payload: str) -> dict | None:
223
+ test_url = f"{url}&xss_test={payload}" if "?" in url else f"{url}?q={payload}"
224
+ try:
225
+ resp = await client.get(test_url)
226
+ if XSS_PROBE in resp.text or "dwy-xss-test" in resp.text:
227
+ return {
228
+ "type": "Reflected XSS",
229
+ "detail": f"探针 '{payload}' 被反射到响应中",
230
+ "url": test_url,
231
+ "severity": "Medium",
232
+ "cvss": 6.1,
233
+ }
234
+ except Exception:
235
+ return None
236
+ return None
237
+
238
+
239
+ async def _async_check_security_headers(client: httpx.AsyncClient, url: str) -> list[dict]:
240
+ findings: list[dict] = []
241
+ try:
242
+ resp = await client.get(url)
243
+ headers_lower = {k.lower() for k in resp.headers.keys()}
244
+ for header, desc in SECURITY_HEADERS.items():
245
+ if header.lower() not in headers_lower:
246
+ findings.append({
247
+ "type": "Missing Security Header",
248
+ "detail": f"缺少 {header} 头({desc})",
249
+ "url": url,
250
+ "severity": "Low",
251
+ "cvss": 2.0,
252
+ })
253
+ except Exception:
254
+ pass
255
+ return findings
256
+
257
+
258
+ async def _async_xss_scan(url: str) -> list[dict]:
259
+ findings: list[dict] = []
260
+ async with httpx.AsyncClient(timeout=10, verify=False, follow_redirects=True) as client:
261
+ # XSS payload 探测 + 安全头检查 全部并发
262
+ payload_tasks = [_async_xss_one(client, url, p) for p in XSS_PAYLOADS]
263
+ header_task = _async_check_security_headers(client, url)
264
+ results = await asyncio.gather(*payload_tasks, header_task, return_exceptions=True)
265
+
266
+ for r in results[:-1]:
267
+ if isinstance(r, dict):
268
+ console.print(f" [yellow][XSS] 反射探针被检测到: {r['detail']}[/yellow]")
269
+ findings.append(r)
270
+
271
+ header_findings = results[-1] if isinstance(results[-1], list) else []
272
+ for h in header_findings:
273
+ console.print(f" [blue][Header] {h['detail']}[/blue]")
274
+ findings.append(h)
275
+
276
+ return findings
277
+
278
+
279
+ def scan_xss(url: str, audit: AuditLog) -> list[dict]:
280
+ """XSS 反射探测(payload + 安全头并发)。"""
281
+ console.print(f"[bold]XSS 反射探测: {url}[/bold]")
282
+ findings = asyncio.run(_async_xss_scan(url))
283
+ if not findings:
284
+ console.print(" [green]未发现 XSS 反射点[/green]")
285
+ return findings
286
+
287
+
288
+ def main():
289
+ parser = argparse.ArgumentParser(description="渗透测试 - Web 应用测试(并发)")
290
+ parser.add_argument("--target", required=True, help="目标 URL")
291
+ parser.add_argument("--mode", required=True, choices=["dir", "sqli", "xss"], help="测试模式")
292
+ parser.add_argument("--output-dir", required=True, help="输出目录")
293
+ args = parser.parse_args()
294
+
295
+ target, warning = validate_target(args.target)
296
+ if target is None:
297
+ console.print(f"[red]{warning}[/red]")
298
+ sys.exit(1)
299
+ if warning:
300
+ console.print(f"[yellow]{warning}[/yellow]")
301
+
302
+ output_dir = Path(args.output_dir)
303
+ output_dir.mkdir(parents=True, exist_ok=True)
304
+ audit = AuditLog(output_dir / "audit.log")
305
+
306
+ url = target.web_url or f"http://{target.host}"
307
+ findings = []
308
+
309
+ if args.mode == "dir":
310
+ findings = scan_directory(url, audit, output_dir)
311
+ elif args.mode == "sqli":
312
+ findings = scan_sqli(url, audit, output_dir)
313
+ elif args.mode == "xss":
314
+ findings = scan_xss(url, audit)
315
+
316
+ result = {
317
+ "target": url,
318
+ "mode": args.mode,
319
+ "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
320
+ "findings": findings,
321
+ "total": len(findings),
322
+ }
323
+
324
+ output_file = output_dir / f"web_{args.mode}.json"
325
+ save_json_result(result, output_file)
326
+ console.print(f"\n[green]结果已保存: {output_file}[/green]")
327
+
328
+
329
+ if __name__ == "__main__":
330
+ main()
@@ -0,0 +1,14 @@
1
+ [project]
2
+ name = "dwy-pentest"
3
+ version = "0.1.0"
4
+ description = "White-hat penetration testing scripts for Claude Code skill"
5
+ requires-python = ">=3.11"
6
+ dependencies = [
7
+ "python-nmap>=0.7.1",
8
+ "httpx>=0.27.0",
9
+ "dnspython>=2.6.0",
10
+ "python-whois>=0.9.4",
11
+ "rich>=13.0.0",
12
+ "jinja2>=3.1.0",
13
+ "pydantic>=2.0.0",
14
+ ]
@@ -0,0 +1,97 @@
1
+ #!/bin/bash
2
+ # setup_tools.sh — 检测并安装渗透测试所需的 CLI 工具
3
+ # 仅支持 macOS (brew)
4
+
5
+ set -e
6
+
7
+ RED='\033[0;31m'
8
+ GREEN='\033[0;32m'
9
+ YELLOW='\033[1;33m'
10
+ NC='\033[0m'
11
+
12
+ # 工具列表:命令名 → brew 包名
13
+ declare -A TOOLS=(
14
+ ["nmap"]="nmap"
15
+ ["nuclei"]="nuclei"
16
+ ["testssl"]="testssl"
17
+ ["gobuster"]="gobuster"
18
+ ["sqlmap"]="sqlmap"
19
+ ["hydra"]="hydra"
20
+ ["ffuf"]="ffuf"
21
+ ["whatweb"]="whatweb"
22
+ )
23
+
24
+ echo "========================================="
25
+ echo " dwy-pentest 环境检测与安装"
26
+ echo "========================================="
27
+ echo ""
28
+
29
+ # 检查 brew
30
+ if ! command -v brew &> /dev/null; then
31
+ echo -e "${RED}[ERROR] Homebrew 未安装,请先安装: https://brew.sh${NC}"
32
+ exit 1
33
+ fi
34
+
35
+ MISSING=()
36
+ INSTALLED=()
37
+
38
+ for cmd in "${!TOOLS[@]}"; do
39
+ if command -v "$cmd" &> /dev/null; then
40
+ version=$($cmd --version 2>/dev/null | head -1 || echo "已安装")
41
+ echo -e "${GREEN}[OK]${NC} $cmd — $version"
42
+ INSTALLED+=("$cmd")
43
+ else
44
+ echo -e "${YELLOW}[MISSING]${NC} $cmd"
45
+ MISSING+=("$cmd")
46
+ fi
47
+ done
48
+
49
+ echo ""
50
+
51
+ if [ ${#MISSING[@]} -eq 0 ]; then
52
+ echo -e "${GREEN}所有工具已就绪!${NC}"
53
+ else
54
+ echo -e "${YELLOW}缺少 ${#MISSING[@]} 个工具,正在安装...${NC}"
55
+ echo ""
56
+
57
+ for cmd in "${MISSING[@]}"; do
58
+ pkg="${TOOLS[$cmd]}"
59
+ echo -e " 安装 $pkg ..."
60
+ brew install "$pkg" 2>/dev/null || echo -e "${RED} [WARN] $pkg 安装失败,请手动安装${NC}"
61
+ done
62
+
63
+ echo ""
64
+ echo "安装完成,重新检测:"
65
+ for cmd in "${MISSING[@]}"; do
66
+ if command -v "$cmd" &> /dev/null; then
67
+ echo -e " ${GREEN}[OK]${NC} $cmd"
68
+ else
69
+ echo -e " ${RED}[FAIL]${NC} $cmd — 请手动安装"
70
+ fi
71
+ done
72
+ fi
73
+
74
+ # 更新 nuclei 模板
75
+ if command -v nuclei &> /dev/null; then
76
+ echo ""
77
+ echo "更新 nuclei 漏洞模板..."
78
+ nuclei -update-templates 2>/dev/null || echo -e "${YELLOW}[WARN] nuclei 模板更新失败,可能需要网络${NC}"
79
+ fi
80
+
81
+ # 检查 Python 环境
82
+ echo ""
83
+ echo "检查 Python 环境..."
84
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
85
+
86
+ if command -v uv &> /dev/null; then
87
+ echo "使用 uv 同步 Python 依赖..."
88
+ cd "$SCRIPT_DIR" && uv sync 2>/dev/null || echo -e "${YELLOW}[WARN] uv sync 失败${NC}"
89
+ echo -e "${GREEN}Python 环境就绪${NC}"
90
+ else
91
+ echo -e "${YELLOW}[WARN] uv 未安装,请安装: pip install uv${NC}"
92
+ fi
93
+
94
+ echo ""
95
+ echo "========================================="
96
+ echo -e "${GREEN} 环境准备完成${NC}"
97
+ echo "========================================="