pdd-skills 3.0.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 (261) hide show
  1. package/README.md +1478 -0
  2. package/bin/pdd.js +354 -0
  3. package/config/bpmn-rules.yaml +166 -0
  4. package/config/checkstyle.xml +105 -0
  5. package/config/eslint.config.js +48 -0
  6. package/config/pmd.xml +91 -0
  7. package/config/prd-rules.yaml +113 -0
  8. package/config/ruff.toml +45 -0
  9. package/config/sqlfluff.cfg +82 -0
  10. package/hooks/hook-executor.js +332 -0
  11. package/index.js +43 -0
  12. package/lib/api-routes.js +750 -0
  13. package/lib/api-server.js +408 -0
  14. package/lib/cache/cache-config.js +209 -0
  15. package/lib/cache/system-cache.js +852 -0
  16. package/lib/config-manager.js +373 -0
  17. package/lib/generate.js +528 -0
  18. package/lib/grpc/grpc-routes.js +1134 -0
  19. package/lib/grpc/grpc-server.js +912 -0
  20. package/lib/grpc/proto-definitions.js +1033 -0
  21. package/lib/init.js +172 -0
  22. package/lib/iteration/auto-fixer.js +1025 -0
  23. package/lib/iteration/auto-reviewer.js +923 -0
  24. package/lib/iteration/controller.js +577 -0
  25. package/lib/list.js +130 -0
  26. package/lib/mcp-server.js +548 -0
  27. package/lib/openclaw/api-integration.js +535 -0
  28. package/lib/openclaw/cli-integration.js +567 -0
  29. package/lib/openclaw/data-sync.js +845 -0
  30. package/lib/openclaw/openclaw-adapter.js +783 -0
  31. package/lib/plugin/example-plugins/code-stats/index.js +332 -0
  32. package/lib/plugin/example-plugins/code-stats/plugin.json +1 -0
  33. package/lib/plugin/example-plugins/custom-linter/index.js +472 -0
  34. package/lib/plugin/example-plugins/custom-linter/plugin.json +1 -0
  35. package/lib/plugin/example-plugins/hello-world/index.js +86 -0
  36. package/lib/plugin/example-plugins/hello-world/plugin.json +1 -0
  37. package/lib/plugin/plugin-manager.js +655 -0
  38. package/lib/plugin/plugin-sdk.js +565 -0
  39. package/lib/plugin/sandbox.js +627 -0
  40. package/lib/quality/rules/maintainability.js +418 -0
  41. package/lib/quality/rules/performance.js +498 -0
  42. package/lib/quality/rules/readability.js +441 -0
  43. package/lib/quality/rules/robustness.js +504 -0
  44. package/lib/quality/rules/security.js +444 -0
  45. package/lib/quality/scorer.js +576 -0
  46. package/lib/report.js +669 -0
  47. package/lib/sdk-base.js +301 -0
  48. package/lib/sdk-js.js +446 -0
  49. package/lib/sdk-python/README.md +546 -0
  50. package/lib/sdk-python/examples/basic_usage.py +450 -0
  51. package/lib/sdk-python/pdd_sdk/__init__.py +180 -0
  52. package/lib/sdk-python/pdd_sdk/client.py +1170 -0
  53. package/lib/sdk-python/pdd_sdk/events.py +423 -0
  54. package/lib/sdk-python/pdd_sdk/exceptions.py +158 -0
  55. package/lib/sdk-python/pdd_sdk/models.py +518 -0
  56. package/lib/sdk-python/pdd_sdk/utils.py +759 -0
  57. package/lib/token/budget-alert.js +367 -0
  58. package/lib/token/budget-manager.js +485 -0
  59. package/lib/update.js +54 -0
  60. package/lib/utils/logger.js +88 -0
  61. package/lib/verify.js +741 -0
  62. package/lib/version.js +52 -0
  63. package/lib/vm/README.md +102 -0
  64. package/lib/vm/dashboard/api-routes.js +669 -0
  65. package/lib/vm/dashboard/server.js +391 -0
  66. package/lib/vm/dashboard/sse.js +358 -0
  67. package/lib/vm/dashboard/static/css/dashboard.css +1378 -0
  68. package/lib/vm/dashboard/static/index.html +118 -0
  69. package/lib/vm/dashboard/static/js/app.js +949 -0
  70. package/lib/vm/dashboard/static/js/charts.js +913 -0
  71. package/lib/vm/dashboard/static/js/kanban-view.js +1053 -0
  72. package/lib/vm/dashboard/static/js/pipeline-view.js +463 -0
  73. package/lib/vm/dashboard/static/js/quality-view.js +598 -0
  74. package/lib/vm/dashboard/static/js/system-view.js +1021 -0
  75. package/lib/vm/data-provider.js +1191 -0
  76. package/lib/vm/event-bus.js +402 -0
  77. package/lib/vm/hooks/extract-hook.js +307 -0
  78. package/lib/vm/hooks/generate-hook.js +374 -0
  79. package/lib/vm/hooks/hook-interface.js +458 -0
  80. package/lib/vm/hooks/report-hook.js +331 -0
  81. package/lib/vm/hooks/verify-hook.js +454 -0
  82. package/lib/vm/models.js +1003 -0
  83. package/lib/vm/reconciler.js +855 -0
  84. package/lib/vm/scanner.js +988 -0
  85. package/lib/vm/state-schema.js +955 -0
  86. package/lib/vm/state-store.js +733 -0
  87. package/lib/vm/tui/components/card.js +339 -0
  88. package/lib/vm/tui/components/progress-bar.js +368 -0
  89. package/lib/vm/tui/components/sparkline.js +327 -0
  90. package/lib/vm/tui/components/status-light.js +294 -0
  91. package/lib/vm/tui/components/table.js +370 -0
  92. package/lib/vm/tui/input.js +335 -0
  93. package/lib/vm/tui/renderer.js +548 -0
  94. package/lib/vm/tui/screens/kanban-screen.js +397 -0
  95. package/lib/vm/tui/screens/overview-screen.js +357 -0
  96. package/lib/vm/tui/screens/quality-screen.js +336 -0
  97. package/lib/vm/tui/screens/system-screen.js +379 -0
  98. package/lib/vm/tui/tui.js +805 -0
  99. package/package.json +1 -0
  100. package/scripts/cso-analyzer.js +198 -0
  101. package/scripts/eval-runner.js +359 -0
  102. package/scripts/i18n-checker.js +109 -0
  103. package/scripts/linter/activiti-linter.js +272 -0
  104. package/scripts/linter/prd-linter.js +162 -0
  105. package/scripts/linter/report-generator.js +207 -0
  106. package/scripts/linter/run-linters.js +285 -0
  107. package/scripts/linter/sql-linter.js +166 -0
  108. package/scripts/token-analyzer.js +162 -0
  109. package/scripts/vm-test.js +180 -0
  110. package/skills/core/official-doc-writer/LICENSE +21 -0
  111. package/skills/core/official-doc-writer/README.md +232 -0
  112. package/skills/core/official-doc-writer/SKILL.md +475 -0
  113. package/skills/core/official-doc-writer/_meta.json +1 -0
  114. package/skills/core/official-doc-writer/document_generator.py +580 -0
  115. package/skills/core/official-doc-writer/evals/default-evals.json +1 -0
  116. package/skills/core/official-doc-writer/examples.md +150 -0
  117. package/skills/core/official-doc-writer/fonts/FONTS_LIST.md +45 -0
  118. package/skills/core/official-doc-writer/fonts/README.md +141 -0
  119. package/skills/core/official-doc-writer/fonts/SIMFANG.TTF +0 -0
  120. package/skills/core/official-doc-writer/fonts/SIMHEI.TTF +0 -0
  121. package/skills/core/official-doc-writer/fonts/SIMKAI.TTF +0 -0
  122. package/skills/core/official-doc-writer/fonts/SIMSUN.TTC +0 -0
  123. package/skills/core/official-doc-writer/fonts//346/226/271/346/255/243/345/260/217/346/240/207/345/256/213GBK.TTF +0 -0
  124. package/skills/core/official-doc-writer/references/GBT_9704-2012_/345/205/232/346/224/277/346/234/272/345/205/263/345/205/254/346/226/207/346/240/274/345/274/217.md +422 -0
  125. package/skills/core/official-doc-writer/scripts/__pycache__/generate_official_doc.cpython-313.pyc +0 -0
  126. package/skills/core/official-doc-writer/scripts/dialog_manager.py +564 -0
  127. package/skills/core/official-doc-writer/scripts/generate_official_doc.py +252 -0
  128. package/skills/core/official-doc-writer/scripts/install_fonts.py +390 -0
  129. package/skills/core/official-doc-writer/scripts/smart_prompts.py +363 -0
  130. package/skills/core/pdd-ba/SKILL.md +305 -0
  131. package/skills/core/pdd-ba/_meta.json +1 -0
  132. package/skills/core/pdd-ba/evals/default-evals.json +1 -0
  133. package/skills/core/pdd-code-reviewer/SKILL.md +378 -0
  134. package/skills/core/pdd-code-reviewer/_meta.json +1 -0
  135. package/skills/core/pdd-code-reviewer/evals/default-evals.json +1 -0
  136. package/skills/core/pdd-doc-change/SKILL.md +350 -0
  137. package/skills/core/pdd-doc-change/_meta.json +1 -0
  138. package/skills/core/pdd-doc-change/evals/default-evals.json +1 -0
  139. package/skills/core/pdd-doc-gardener/SKILL.md +248 -0
  140. package/skills/core/pdd-doc-gardener/_meta.json +1 -0
  141. package/skills/core/pdd-doc-gardener/evals/default-evals.json +1 -0
  142. package/skills/core/pdd-entropy-reduction/SKILL.md +360 -0
  143. package/skills/core/pdd-entropy-reduction/_meta.json +1 -0
  144. package/skills/core/pdd-entropy-reduction/evals/default-evals.json +1 -0
  145. package/skills/core/pdd-entropy-reduction/references/entropy-report-template.md +287 -0
  146. package/skills/core/pdd-entropy-reduction/references/golden-principles.md +573 -0
  147. package/skills/core/pdd-entropy-reduction/scripts/entropy_scan.py +712 -0
  148. package/skills/core/pdd-extract-features/SKILL.md +320 -0
  149. package/skills/core/pdd-extract-features/_meta.json +1 -0
  150. package/skills/core/pdd-extract-features/evals/default-evals.json +1 -0
  151. package/skills/core/pdd-generate-spec/SKILL.md +418 -0
  152. package/skills/core/pdd-generate-spec/_meta.json +1 -0
  153. package/skills/core/pdd-generate-spec/evals/default-evals.json +1 -0
  154. package/skills/core/pdd-implement-feature/SKILL.md +332 -0
  155. package/skills/core/pdd-implement-feature/_meta.json +1 -0
  156. package/skills/core/pdd-implement-feature/evals/default-evals.json +1 -0
  157. package/skills/core/pdd-main/SKILL.md +540 -0
  158. package/skills/core/pdd-main/_meta.json +1 -0
  159. package/skills/core/pdd-main/evals/default-evals.json +1 -0
  160. package/skills/core/pdd-main/evals/evals.json +215 -0
  161. package/skills/core/pdd-verify-feature/SKILL.md +474 -0
  162. package/skills/core/pdd-verify-feature/_meta.json +1 -0
  163. package/skills/core/pdd-verify-feature/evals/default-evals.json +1 -0
  164. package/skills/core/pdd-vm/evals/default-evals.json +1 -0
  165. package/skills/core/traffic-accident-assessor/LICENSE +29 -0
  166. package/skills/core/traffic-accident-assessor/SKILL.md +439 -0
  167. package/skills/core/traffic-accident-assessor/evals/evals.json +1 -0
  168. package/skills/core/traffic-accident-assessor/references/accident-types.md +369 -0
  169. package/skills/core/traffic-accident-assessor/references/liability-rules.md +287 -0
  170. package/skills/core/traffic-accident-assessor/references/traffic-laws.md +226 -0
  171. package/skills/core/traffic-accident-assessor/references//351/253/230/345/260/224/345/244/253/350/257/264/346/230/216/344/271/246.pdf +32576 -106
  172. package/skills/core/traffic-accident-assessor/scripts/generate_official_statement.py +588 -0
  173. package/skills/core/traffic-accident-assessor/scripts/generate_report.py +495 -0
  174. package/skills/core/traffic-accident-assessor/scripts/generate_statement.py +528 -0
  175. package/skills/core/traffic-accident-assessor.zip +0 -0
  176. package/skills/entropy/expert-arch-enforcer/SKILL.md +292 -0
  177. package/skills/entropy/expert-arch-enforcer/_meta.json +1 -0
  178. package/skills/entropy/expert-arch-enforcer/evals/default-evals.json +1 -0
  179. package/skills/entropy/expert-auto-refactor/SKILL.md +327 -0
  180. package/skills/entropy/expert-auto-refactor/_meta.json +1 -0
  181. package/skills/entropy/expert-auto-refactor/evals/default-evals.json +1 -0
  182. package/skills/entropy/expert-code-quality/SKILL.md +468 -0
  183. package/skills/entropy/expert-code-quality/_meta.json +1 -0
  184. package/skills/entropy/expert-code-quality/evals/default-evals.json +1 -0
  185. package/skills/entropy/expert-code-quality/evals/evals.json +109 -0
  186. package/skills/entropy/expert-code-quality/references/code-smells.md +605 -0
  187. package/skills/entropy/expert-code-quality/references/design-patterns.md +1111 -0
  188. package/skills/entropy/expert-code-quality/references/refactoring-catalog.md +1281 -0
  189. package/skills/entropy/expert-code-quality/references/solid-principles.md +524 -0
  190. package/skills/entropy/expert-entropy-auditor/SKILL.md +276 -0
  191. package/skills/entropy/expert-entropy-auditor/_meta.json +1 -0
  192. package/skills/entropy/expert-entropy-auditor/evals/default-evals.json +1 -0
  193. package/skills/expert/expert-activiti/SKILL.md +497 -0
  194. package/skills/expert/expert-activiti/_meta.json +1 -0
  195. package/skills/expert/expert-mysql/SKILL.md +832 -0
  196. package/skills/expert/expert-mysql/_meta.json +1 -0
  197. package/skills/expert/expert-performance/SKILL.md +379 -0
  198. package/skills/expert/expert-performance/_meta.json +1 -0
  199. package/skills/expert/expert-performance/evals/default-evals.json +1 -0
  200. package/skills/expert/expert-ruoyi/SKILL.md +472 -0
  201. package/skills/expert/expert-ruoyi/_meta.json +1 -0
  202. package/skills/expert/expert-security/SKILL.md +1341 -0
  203. package/skills/expert/expert-security/_meta.json +1 -0
  204. package/skills/expert/expert-security/evals/default-evals.json +1 -0
  205. package/skills/expert/software-architect/SKILL.md +350 -0
  206. package/skills/expert/software-architect/_meta.json +1 -0
  207. package/skills/expert/software-engineer/SKILL.md +437 -0
  208. package/skills/expert/software-engineer/_meta.json +1 -0
  209. package/skills/expert/software-engineer/architecture.md +130 -0
  210. package/skills/expert/software-engineer/patterns.md +151 -0
  211. package/skills/expert/software-engineer/testing.md +135 -0
  212. package/skills/expert/system-architect/SKILL.md +628 -0
  213. package/skills/expert/system-architect/_meta.json +1 -0
  214. package/skills/expert/system-architect/assets/templates/ARCHITECTURE.md +25 -0
  215. package/skills/expert/system-architect/assets/templates/README.md +44 -0
  216. package/skills/expert/system-architect/references/js-ts-standards.md +18 -0
  217. package/skills/expert/system-architect/references/python-standards.md +19 -0
  218. package/skills/expert/system-architect/references/scaffolding.md +61 -0
  219. package/skills/expert/system-architect/references/security-checklist.md +21 -0
  220. package/skills/openspec/openspec-apply-change/SKILL.md +156 -0
  221. package/skills/openspec/openspec-apply-change/_meta.json +1 -0
  222. package/skills/openspec/openspec-archive-change/SKILL.md +114 -0
  223. package/skills/openspec/openspec-archive-change/_meta.json +1 -0
  224. package/skills/openspec/openspec-bulk-archive-change/SKILL.md +246 -0
  225. package/skills/openspec/openspec-bulk-archive-change/_meta.json +1 -0
  226. package/skills/openspec/openspec-continue-change/SKILL.md +118 -0
  227. package/skills/openspec/openspec-continue-change/_meta.json +1 -0
  228. package/skills/openspec/openspec-explore/SKILL.md +288 -0
  229. package/skills/openspec/openspec-explore/_meta.json +1 -0
  230. package/skills/openspec/openspec-ff-change/SKILL.md +101 -0
  231. package/skills/openspec/openspec-ff-change/_meta.json +1 -0
  232. package/skills/openspec/openspec-new-change/SKILL.md +74 -0
  233. package/skills/openspec/openspec-new-change/_meta.json +1 -0
  234. package/skills/openspec/openspec-onboard/SKILL.md +554 -0
  235. package/skills/openspec/openspec-onboard/_meta.json +1 -0
  236. package/skills/openspec/openspec-sync-specs/SKILL.md +138 -0
  237. package/skills/openspec/openspec-sync-specs/_meta.json +1 -0
  238. package/skills/openspec/openspec-verify-change/SKILL.md +168 -0
  239. package/skills/openspec/openspec-verify-change/_meta.json +1 -0
  240. package/skills/pr/pdd-multi-review/SKILL.md +534 -0
  241. package/skills/pr/pdd-multi-review/_meta.json +1 -0
  242. package/skills/pr/pdd-pr-batch/SKILL.md +303 -0
  243. package/skills/pr/pdd-pr-batch/_meta.json +1 -0
  244. package/skills/pr/pdd-pr-create/SKILL.md +344 -0
  245. package/skills/pr/pdd-pr-create/_meta.json +1 -0
  246. package/skills/pr/pdd-pr-merge/SKILL.md +286 -0
  247. package/skills/pr/pdd-pr-merge/_meta.json +1 -0
  248. package/skills/pr/pdd-pr-review/SKILL.md +217 -0
  249. package/skills/pr/pdd-pr-review/_meta.json +1 -0
  250. package/skills/pr/pdd-task-manager/SKILL.md +636 -0
  251. package/skills/pr/pdd-task-manager/_meta.json +1 -0
  252. package/skills/pr/pdd-template-engine/SKILL.md +306 -0
  253. package/skills/pr/pdd-template-engine/_meta.json +1 -0
  254. package/templates/behavior-shaping/iron-law-template.md +87 -0
  255. package/templates/behavior-shaping/rationalization-template.md +62 -0
  256. package/templates/behavior-shaping/red-flags-template.md +70 -0
  257. package/templates/bilingual-template.md +139 -0
  258. package/templates/config/default.yaml +47 -0
  259. package/templates/project/default/README.md +31 -0
  260. package/templates/project/frontend/README.md +46 -0
  261. package/templates/project/java/README.md +48 -0
@@ -0,0 +1,444 @@
1
+ // lib/quality/rules/security.js - 安全规则集
2
+ // 评估代码安全性: 输入验证、SQL注入、XSS、硬编码密钥、权限控制等
3
+
4
+ /**
5
+ * 安全规则集
6
+ *
7
+ * 规则列表:
8
+ * - sqlInjection: SQL注入风险检测
9
+ * - xssRisk: XSS跨站脚本攻击风险
10
+ * - hardcodedSecrets: 硬编码敏感信息
11
+ * - inputValidation: 用户输入校验
12
+ * - dependencySecurity: 依赖安全
13
+ * - insecureConfig: 不安全配置
14
+ */
15
+
16
+ const rules = [
17
+ {
18
+ name: 'sqlInjection',
19
+ description: 'SQL注入: 使用参数化查询,禁止字符串拼接SQL',
20
+ maxScore: 25,
21
+ check(code, ext) {
22
+ const lines = code.split('\n');
23
+ const vulnerabilities = [];
24
+
25
+ // 危险的SQL拼接模式
26
+ const dangerousPatterns = [
27
+ { pattern: /(?:SELECT|INSERT|UPDATE|DELETE|DROP)\s+.*\+\s*["']/i, desc: '字符串拼接SQL' },
28
+ { pattern: /(?:SELECT|INSERT|UPDATE|DELETE|DROP)\s+.*`\$\{.*?\}`/i, desc: '模板字面量拼接SQL' },
29
+ { pattern: /\.(query|execute|raw|run)\s*\(\s*["`][^"]*\$\{|[^"]*\+/, desc: '动态构建SQL语句' },
30
+ { pattern: /"[\s\S]*(?:SELECT|INSERT|UPDATE|DELETE)[\s\S]*"\s*\+/i, desc: '字符串连接包含SQL关键字' }
31
+ ];
32
+
33
+ for (let i = 0; i < lines.length; i++) {
34
+ const line = lines[i];
35
+
36
+ for (const { pattern, desc } of dangerousPatterns) {
37
+ if (pattern.test(line)) {
38
+ // 排除注释行
39
+ if (!line.trim().startsWith('//') && !line.trim().startsWith('*')) {
40
+ vulnerabilities.push({
41
+ type: 'sql_injection',
42
+ description: desc,
43
+ line: i + 1,
44
+ context: line.trim().substring(0, 70)
45
+ });
46
+ }
47
+ }
48
+ }
49
+ }
50
+
51
+ // 检测未使用参数化查询的ORM操作
52
+ const unsafeOrmPatterns = [
53
+ { pattern: /\.where\s*\(\s*["`].*\$\{/, desc: 'where条件使用插值' },
54
+ { pattern: /sequelize\.query\s*\(\s*[^,)]*\+/, desc: 'sequelize.query使用拼接' },
55
+ { pattern: /knex\.raw\s*\(/, desc: 'knex原始查询(需确认参数化)' },
56
+ { pattern: /mongoose\.aggregate\s*\(\s*\[.*\$where/i, desc: 'MongoDB $where操作符' }
57
+ ];
58
+
59
+ for (let i = 0; i < lines.length; i++) {
60
+ const line = lines[i];
61
+ for (const { pattern, desc } of unsafeOrmPatterns) {
62
+ if (pattern.test(line) && !line.trim().startsWith('//')) {
63
+ vulnerabilities.push({
64
+ type: 'unsafe_orm',
65
+ description: desc,
66
+ line: i + 1,
67
+ context: line.trim().substring(0, 60)
68
+ });
69
+ }
70
+ }
71
+ }
72
+
73
+ // 检查是否有安全的替代方案存在
74
+ const hasParameterizedQuery =
75
+ /\?/.test(code) && /(?:prepare|bind|execute|run)/.test(code) ||
76
+ /sequelize\.query\(.*replacements/.test(code) ||
77
+ /knex\(.*)\.where\([^)]*\)/.test(code);
78
+
79
+ return {
80
+ passed: vulnerabilities.length === 0,
81
+ deduction: Math.min(vulnerabilities.length * 8, 25),
82
+ message: vulnerabilities.length > 0
83
+ ? `发现${vulnerabilities.length}个潜在的SQL注入风险点`
84
+ : hasParameterizedQuery ? '使用了参数化查询' : '未检测到明显的SQL拼接',
85
+ suggestion: '始终使用参数化查询(prepared statement)或ORM提供的参数绑定方法;绝对不要将用户输入直接拼接到SQL中',
86
+ line: vulnerabilities.length > 0 ? vulnerabilities[0].line : null
87
+ };
88
+ }
89
+ },
90
+
91
+ {
92
+ name: 'xssRisk',
93
+ description: 'XSS攻击: 对用户输入进行转义和过滤后再输出到HTML',
94
+ maxScore: 20,
95
+ check(code, ext) {
96
+ const lines = code.split('\n');
97
+ const risks = [];
98
+
99
+ // 危险模式:直接输出未经处理的数据
100
+ const dangerousPatterns = [
101
+ { pattern: /innerHTML\s*=.*(?:req|request|params|query|body|input|user|form|data)\b/, desc: 'innerHTML赋值含用户数据' },
102
+ { pattern: /document\.write\s*\(/, desc: 'document.write使用' },
103
+ { pattern: /eval\s*\(/, desc: 'eval()使用' },
104
+ { pattern: /new\s+Function\s*\(/, desc: 'new Function()使用' },
105
+ { pattern: /setTimeout\s*\(\s*["'`]/, desc: 'setTimeout传入字符串' },
106
+ { pattern: /setInterval\s*\(\s*["'`]/, desc: 'setInterval传入字符串' },
107
+ { pattern: /\.outerHTML\s*=/, desc: 'outerHTML赋值' },
108
+ { pattern: /jquery|\$\(.*\)\.html\s*\(/i, desc: 'jQuery.html()使用' }
109
+ ];
110
+
111
+ for (let i = 0; i < lines.length; i++) {
112
+ const line = lines[i];
113
+ for (const { pattern, desc } of dangerousPatterns) {
114
+ if (pattern.test(line) && !line.trim().startsWith('//') && !line.trim().startsWith('*')) {
115
+ risks.push({
116
+ type: 'xss_risk',
117
+ description: desc,
118
+ line: i + 1,
119
+ context: line.trim().substring(0, 65)
120
+ });
121
+ }
122
+ }
123
+ }
124
+
125
+ // 检查URL中的用户输入(开放重定向风险)
126
+ if (/location\.(href|assign|replace)\s*=/.test(code) &&
127
+ /(req|request|params|query|body|input)/.test(code)) {
128
+ risks.push({
129
+ type: 'open_redirect',
130
+ description: '可能存在开放重定向风险',
131
+ severity: 'high'
132
+ });
133
+ }
134
+
135
+ // 检查是否有安全措施
136
+ const hasSanitization =
137
+ /sanitize|escape|encodeURI|encodeURIComponent|DOMPurify|he\.encode|xss/i.test(code);
138
+
139
+ return {
140
+ passed: risks.filter(r => r.severity === 'high').length === 0 && risks.length <= 1,
141
+ deduction: Math.min(
142
+ risks.filter(r => r.severity === 'high').length * 10 +
143
+ risks.filter(r => r.severity !== 'high').length * 4,
144
+ 20
145
+ ),
146
+ message: risks.length > 0
147
+ ? `发现${risks.length}个潜在XSS风险点`
148
+ : hasSanitization ? '有基本的XSS防护措施' : '未发现明显XSS风险',
149
+ suggestion: '使用textContent代替innerHTML;对动态内容使用DOMPurify等库进行HTML净化;对所有用户输入进行编码后输出',
150
+ line: risks.length > 0 ? risks[0].line : null
151
+ };
152
+ }
153
+ },
154
+
155
+ {
156
+ name: 'hardcodedSecrets',
157
+ description: '硬编码敏感信息: 密钥、密码、Token不应写在源码中',
158
+ maxScore: 25,
159
+ check(code, ext) {
160
+ const lines = code.split('\n');
161
+ const secrets = [];
162
+
163
+ // 敏感信息模式
164
+ const secretPatterns = [
165
+ { regex: /(?:password|passwd|pwd)\s*[:=]\s*["'][^"']{3,}["']/i, label: '密码' },
166
+ { regex: /(?:api[_-]?key|apikey)\s*[:=]\s*["'][^"']{5,}["']/i, label: 'API Key' },
167
+ { regex: /(?:secret|token|auth[_-]?token|access[_-]?token)\s*[:=]\s*["'][^"']{10,}["']/i, label: 'Secret/Token' },
168
+ { regex: /(?:private[_-]?key|privkey)\s*[:=]\s*["']BEGIN/i, label: '私钥' },
169
+ { regex: /(?:connection[_-]?string|connstr|mongodb|mysql|redis)[:\s]*["'][^"']+password/i, label: '数据库连接串(含密码)' },
170
+ { regex: /aws_access_key_id\s*[:=]\s*["'][A-Z0-9]{16,}["']/i, label: 'AWS Access Key' },
171
+ { regex: /aws_secret_access_key\s*[:=]\s*["'][A-Za-z0-9\/+=]{30,}["']/i, label: 'AWS Secret Key' },
172
+ { regex: /(?:jwt[_-]?secret|jwt[_-]?key)\s*[:=]\s*["'][^"']{10,}["']/i, label: 'JWT Secret' },
173
+ { regex: /bcrypt\.genSaltSync\(\)|bcrypt\.hashSync\(/, label: '同步加密调用' }
174
+ ];
175
+
176
+ for (let i = 0; i < lines.length; i++) {
177
+ const line = lines[i];
178
+
179
+ for (const { regex, label } of secretPatterns) {
180
+ if (regex.test(line)) {
181
+ // 排除示例代码、注释、占位符
182
+ if (this._isRealSecret(line)) {
183
+ secrets.push({
184
+ type: label,
185
+ line: i + 1,
186
+ context: this._maskSensitive(line.trim())
187
+ });
188
+ }
189
+ }
190
+ }
191
+ }
192
+
193
+ // 检查是否正确使用环境变量
194
+ const usesEnvVars = /process\.env\.\w+/.test(code);
195
+ const usesConfigModule = /config\.\w+/.test(code) || /require\(['"]config['"]\)/.test(code);
196
+ const hasSecretManagement = usesEnvVars || usesConfigModule;
197
+
198
+ return {
199
+ passed: secrets.length === 0,
200
+ deduction: Math.min(secrets.length * 6, 25),
201
+ message: secrets.length > 0
202
+ ? `发现${secrets.length}处可能的硬编码敏感信息: ${[...new Set(secrets.map(s => s.type))].join(', ')}`
203
+ : hasSecretManagement ? '敏感信息通过环境变量/配置管理' : '未发现硬编码密钥',
204
+ suggestion: '使用process.env或配置管理服务存储敏感信息;将密钥移至.env文件并加入.gitignore;使用密钥管理服务(AWS KMS/Vault)',
205
+ line: secrets.length > 0 ? secrets[0].line : null
206
+ };
207
+ },
208
+
209
+ _isRealSecret(line) {
210
+ // 排除以下情况:
211
+ // 注释行
212
+ if (line.trim().startsWith('//') || line.trim().startsWith('*') || line.trim().startsWith('#')) return false;
213
+ // 占位符
214
+ if (/your-|<.*>|xxx|TODO|FIXME|placeholder|change.me|replace/i.test(line)) return false;
215
+ // 空值
216
+ if (/["']\s*["']|""|''/.test(line)) return false;
217
+ // 示例值
218
+ if (/example|sample|test|demo|fake|mock/i.test(line)) return false;
219
+
220
+ return true;
221
+ },
222
+
223
+ _maskSensitive(str) {
224
+ // 隐藏敏感值的中间部分
225
+ return str.replace(/(["'])([^"']{2})([^"]*)(\1)/g, '$1$2***$4');
226
+ }
227
+ },
228
+
229
+ {
230
+ name: 'inputValidation',
231
+ description: '输入验证: 所有外部输入必须经过校验和清理',
232
+ maxScore: 15,
233
+ check(code, ext) {
234
+ const lines = code.split('\n');
235
+ const unvalidatedInputs = [];
236
+
237
+ // 外部输入来源
238
+ const inputSources = [
239
+ { pattern: /req\.body\b/, source: '请求体(req.body)' },
240
+ { pattern: /req\.query\b/, source: '查询参数(req.query)' },
241
+ { pattern: /req\.params\b/, source: '路径参数(req.params)' },
242
+ { pattern: /req\.headers\b/, source: '请求头(req.headers)' },
243
+ { pattern: /request\.body\b/, source: '请求体(request.body)' },
244
+ { pattern: /urlSearchParams|URLSearchParams|searchParams/, source: 'URL参数' },
245
+ { pattern: /formData|FormData/, source: '表单数据' },
246
+ { pattern: /process\.argv/, source: '命令行参数' },
247
+ { pattern: /localStorage|sessionStorage|cookie/i, source: '浏览器存储' }
248
+ ];
249
+
250
+ for (let i = 0; i < lines.length; i++) {
251
+ const line = lines[i];
252
+
253
+ for (const { pattern, source } of inputSources) {
254
+ if (pattern.test(line)) {
255
+ // 检查后续几行是否有校验逻辑
256
+ const nextLines = lines.slice(i, Math.min(i + 8, lines.length));
257
+ const hasValidation = nextLines.some(l =>
258
+ /validate|check|verify|schema|joi|yup|zod|sanitiz|escape|trim|typeof|instanceof|guard|assert/.test(l)
259
+ );
260
+
261
+ // 检查是否有类型检查
262
+ const hasTypeCheck = nextLines.some(l =>
263
+ /===|!==|typeof |instanceof |Number\(|parseInt|parseFloat/.test(l)
264
+ );
265
+
266
+ if (!hasValidation && !hasTypeCheck) {
267
+ // 排除简单的属性访问(如 req.body.user)
268
+ if (!/req\.(body|query|params)\.\w+$/.test(line.trim())) {
269
+ unvalidatedInputs.push({
270
+ source,
271
+ line: i + 1,
272
+ context: line.trim().substring(0, 55)
273
+ });
274
+ }
275
+ }
276
+ }
277
+ }
278
+ }
279
+
280
+ // 去重:同一来源只报告一次
281
+ const uniqueSources = [...new Set(unvalidatedInputs.map(u => u.source))];
282
+
283
+ return {
284
+ passed: unvalidatedInputs.length <= 1,
285
+ deduction: Math.min(unvalidatedInputs.length * 3, 15),
286
+ message: unvalidatedInputs.length > 0
287
+ ? `${unvalidatedInputs.length}处外部输入缺少校验: ${uniqueSources.join(', ')}`
288
+ : '外部输入均有适当的校验',
289
+ suggestion: '在路由入口层使用Joi/Yup/Zod等Schema验证库;对用户输入进行类型强制转换和白名单过滤',
290
+ line: unvalidatedInputs.length > 0 ? unvalidatedInputs[0].line : null
291
+ };
292
+ }
293
+ },
294
+
295
+ {
296
+ name: 'dependencySecurity',
297
+ description: '依赖安全: 使用已知安全的依赖版本,避免有漏洞的包',
298
+ maxScore: 10,
299
+ check(code, ext) {
300
+ const issues = [];
301
+
302
+ // 已知的有安全问题或废弃的依赖
303
+ const vulnerableDeps = [
304
+ { pkg: 'lodash', versionPattern: /^<4\.17\.21/, reason: '原型污染漏洞(CVE-2021-23337)' },
305
+ { pkg: 'express', versionPattern: /^<4\.18\.2/, reason: '旧版本可能有已知漏洞' },
306
+ { pkg: 'debug', versionPattern: /^<4\.3\.3/, reason: '远程代码执行风险' },
307
+ { pkg: 'minimist', versionPattern: /^<1\.2\.6/, reason: '原型链污染' },
308
+ { pkg: 'ws', versionPattern: /^<8\.5\.0/, reason: '多个安全修复' }
309
+ ];
310
+
311
+ // 检测使用的危险函数
312
+ const dangerousFunctions = [
313
+ { pattern: /eval\s*\(/, name: 'eval()', risk: '代码注入' },
314
+ { pattern: /child_process|exec\s*\(|spawn\s*\(/, name: '子进程执行', risk: '命令注入' },
315
+ { pattern: /vm\.runIn|vm\.Script/, name: 'VM模块', risk: '沙箱逃逸' },
316
+ { pattern: /fs\.\w+File.*req\.|fs\.\w+File.*input/, name: '文件路径来自用户', risk: '路径遍历' }
317
+ ];
318
+
319
+ const lines = code.split('\n');
320
+ for (let i = 0; i < lines.length; i++) {
321
+ const line = lines[i];
322
+ for (const { pattern, name, risk } of dangerousFunctions) {
323
+ if (pattern.test(line) && !line.trim().startsWith('//')) {
324
+ issues.push({
325
+ type: 'dangerous_function',
326
+ function: name,
327
+ risk,
328
+ line: i + 1
329
+ });
330
+ }
331
+ }
332
+ }
333
+
334
+ // 检查package.json中的依赖(简化版:仅检测import)
335
+ const importedPackages = new Set();
336
+ const importMatches = code.matchAll(/from\s+['"]([^'"]+)['"]/g);
337
+ for (const match of importMatches) {
338
+ const pkg = match[1].split('/')[0];
339
+ if (!pkg.startsWith('.') && pkg !== 'node:' && !pkg.startsWith('@types/')) {
340
+ importedPackages.add(pkg);
341
+ }
342
+ }
343
+
344
+ // 检查是否有已知的废弃或不安全依赖
345
+ for (const pkg of importedPackages) {
346
+ const vulnDep = vulnerableDeps.find(v => v.pkg === pkg);
347
+ if (vulnDep) {
348
+ issues.push({
349
+ type: 'vulnerable_dependency',
350
+ package: pkg,
351
+ reason: vulnDep.reason
352
+ });
353
+ }
354
+ }
355
+
356
+ return {
357
+ passed: issues.filter(i => i.type === 'dangerous_function').length === 0,
358
+ deduction: Math.min(
359
+ issues.filter(i => i.type === 'dangerous_function').length * 4 +
360
+ issues.filter(i => i.type === 'vulnerable_dependency').length * 2,
361
+ 10
362
+ ),
363
+ message: issues.length > 0
364
+ ? `发现${issues.length}个依赖安全问题`
365
+ : '依赖使用看起来安全',
366
+ suggestion: '避免使用eval()和child_process.exec()处理用户输入;定期运行npm audit修复已知漏洞;使用npm outdated检查过时依赖',
367
+ line: issues.length > 0 ? issues[0].line : null
368
+ };
369
+ }
370
+ },
371
+
372
+ {
373
+ name: 'insecureConfig',
374
+ description: '不安全配置: CORS、Cookie、HTTPS等安全相关配置检查',
375
+ maxScore: 5,
376
+ check(code, ext) {
377
+ const issues = [];
378
+ const lines = code.split('\n');
379
+
380
+ // CORS通配符
381
+ if (/origin\s*:\s*["']\*["']|Access-Control-Allow-Origin\s*:\s*\*/.test(code)) {
382
+ // 检查是否是开发环境
383
+ const isDevEnv = /development|dev|local/i.test(code);
384
+ if (!isDevEnv) {
385
+ issues.push({ type: 'cors_wildcard', detail: 'CORS允许所有来源(*)' });
386
+ }
387
+ }
388
+
389
+ // Cookie安全设置缺失
390
+ if (/cookie|Cookie/.test(code)) {
391
+ const hasSecureFlag = /secure\s*:\s*true|{.*secure:.*true/.test(code);
392
+ const hasHttpOnly = /httpOnly\s*:\s*true|{.*httpOnly:.*true/.test(code);
393
+ const hasSameSite = /sameSite|same-site/.test(code);
394
+
395
+ if (!hasSecureFlag || !hasHttpOnly || !hasSameSite) {
396
+ issues.push({
397
+ type: 'insecure_cookie',
398
+ detail: `Cookie缺少安全选项: ${[
399
+ !hasSecureFlag ? 'secure标志' : null,
400
+ !hasHttpOnly ? 'httpOnly标志' : null,
401
+ !hasSameSite ? 'SameSite属性' : null
402
+ ].filter(Boolean).join(', ')}`,
403
+ severity: 'medium'
404
+ });
405
+ }
406
+ }
407
+
408
+ // HTTPS强制跳转缺失(Web应用)
409
+ if (/express|koa|fastify|http\.createServer/.test(code)) {
410
+ const hasHttpsEnforcement = /forceSSL|enforceHTTPS|helmet|hsts|strict-transport-security/.test(code);
411
+ if (!hasHttpsEnforcement) {
412
+ issues.push({
413
+ type: 'no_https_enforcement',
414
+ detail: '未检测到HTTPS强制跳转/HSTS设置',
415
+ severity: 'low'
416
+ });
417
+ }
418
+ }
419
+
420
+ // 错误信息泄露
421
+ if (/err\.stack|error\.stack|console\.(log|error)\(err/.test(code)) {
422
+ const isInProductionHandler = /production|NODE_ENV\s*===\s*['"]production['"]/.test(code);
423
+ if (!isInProductionHandler) {
424
+ issues.push({
425
+ type: 'error_info_leak',
426
+ detail: '可能向客户端暴露详细错误堆栈'
427
+ });
428
+ }
429
+ }
430
+
431
+ return {
432
+ passed: issues.filter(i => i.severity !== 'low').length === 0,
433
+ deduction: Math.min(issues.length * 2, 5),
434
+ message: issues.length > 0
435
+ ? `发现${issues.length}项安全配置问题`
436
+ : '安全配置基本合理',
437
+ suggestion: 'CORS限制为特定域名;Cookie设置secure/httpOnly/sameSite属性;生产环境隐藏错误详情;启用HSTS',
438
+ line: issues.length > 0 ? issues[0]?.line || null : null
439
+ };
440
+ }
441
+ }
442
+ ];
443
+
444
+ export default rules;