frontend-guardian-core 2.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 (152) hide show
  1. package/LICENSE +21 -0
  2. package/bin/fg-core.js +1238 -0
  3. package/bin/watch-mode.js +123 -0
  4. package/dist/engine/cache.d.ts +68 -0
  5. package/dist/engine/cache.d.ts.map +1 -0
  6. package/dist/engine/cache.js +164 -0
  7. package/dist/engine/cache.js.map +1 -0
  8. package/dist/engine/rule-engine.d.ts +135 -0
  9. package/dist/engine/rule-engine.d.ts.map +1 -0
  10. package/dist/engine/rule-engine.js +716 -0
  11. package/dist/engine/rule-engine.js.map +1 -0
  12. package/dist/formatters/github-annotation.d.ts +36 -0
  13. package/dist/formatters/github-annotation.d.ts.map +1 -0
  14. package/dist/formatters/github-annotation.js +122 -0
  15. package/dist/formatters/github-annotation.js.map +1 -0
  16. package/dist/formatters/pr-comment.d.ts +43 -0
  17. package/dist/formatters/pr-comment.d.ts.map +1 -0
  18. package/dist/formatters/pr-comment.js +171 -0
  19. package/dist/formatters/pr-comment.js.map +1 -0
  20. package/dist/formatters/sarif.d.ts +104 -0
  21. package/dist/formatters/sarif.d.ts.map +1 -0
  22. package/dist/formatters/sarif.js +130 -0
  23. package/dist/formatters/sarif.js.map +1 -0
  24. package/dist/index.d.ts +46 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +108 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/integrations/base.d.ts +44 -0
  29. package/dist/integrations/base.d.ts.map +1 -0
  30. package/dist/integrations/base.js +104 -0
  31. package/dist/integrations/base.js.map +1 -0
  32. package/dist/integrations/eslint.d.ts +8 -0
  33. package/dist/integrations/eslint.d.ts.map +1 -0
  34. package/dist/integrations/eslint.js +67 -0
  35. package/dist/integrations/eslint.js.map +1 -0
  36. package/dist/integrations/formatter.d.ts +35 -0
  37. package/dist/integrations/formatter.d.ts.map +1 -0
  38. package/dist/integrations/formatter.js +182 -0
  39. package/dist/integrations/formatter.js.map +1 -0
  40. package/dist/integrations/index.d.ts +17 -0
  41. package/dist/integrations/index.d.ts.map +1 -0
  42. package/dist/integrations/index.js +25 -0
  43. package/dist/integrations/index.js.map +1 -0
  44. package/dist/integrations/stylelint.d.ts +8 -0
  45. package/dist/integrations/stylelint.d.ts.map +1 -0
  46. package/dist/integrations/stylelint.js +59 -0
  47. package/dist/integrations/stylelint.js.map +1 -0
  48. package/dist/integrations/typescript.d.ts +8 -0
  49. package/dist/integrations/typescript.d.ts.map +1 -0
  50. package/dist/integrations/typescript.js +92 -0
  51. package/dist/integrations/typescript.js.map +1 -0
  52. package/dist/rules/registry.d.ts +83 -0
  53. package/dist/rules/registry.d.ts.map +1 -0
  54. package/dist/rules/registry.js +205 -0
  55. package/dist/rules/registry.js.map +1 -0
  56. package/dist/scanners/a11y-scanner.d.ts +14 -0
  57. package/dist/scanners/a11y-scanner.d.ts.map +1 -0
  58. package/dist/scanners/a11y-scanner.js +781 -0
  59. package/dist/scanners/a11y-scanner.js.map +1 -0
  60. package/dist/scanners/component-scanner.d.ts +12 -0
  61. package/dist/scanners/component-scanner.d.ts.map +1 -0
  62. package/dist/scanners/component-scanner.js +304 -0
  63. package/dist/scanners/component-scanner.js.map +1 -0
  64. package/dist/scanners/cross-file-scanner.d.ts +18 -0
  65. package/dist/scanners/cross-file-scanner.d.ts.map +1 -0
  66. package/dist/scanners/cross-file-scanner.js +684 -0
  67. package/dist/scanners/cross-file-scanner.js.map +1 -0
  68. package/dist/scanners/hooks-scanner.d.ts +15 -0
  69. package/dist/scanners/hooks-scanner.d.ts.map +1 -0
  70. package/dist/scanners/hooks-scanner.js +670 -0
  71. package/dist/scanners/hooks-scanner.js.map +1 -0
  72. package/dist/scanners/i18n-scanner.d.ts +13 -0
  73. package/dist/scanners/i18n-scanner.d.ts.map +1 -0
  74. package/dist/scanners/i18n-scanner.js +535 -0
  75. package/dist/scanners/i18n-scanner.js.map +1 -0
  76. package/dist/scanners/naming-scanner.d.ts +19 -0
  77. package/dist/scanners/naming-scanner.d.ts.map +1 -0
  78. package/dist/scanners/naming-scanner.js +746 -0
  79. package/dist/scanners/naming-scanner.js.map +1 -0
  80. package/dist/scanners/performance-scanner.d.ts +7 -0
  81. package/dist/scanners/performance-scanner.d.ts.map +1 -0
  82. package/dist/scanners/performance-scanner.js +402 -0
  83. package/dist/scanners/performance-scanner.js.map +1 -0
  84. package/dist/scanners/platform-scanner.d.ts +15 -0
  85. package/dist/scanners/platform-scanner.d.ts.map +1 -0
  86. package/dist/scanners/platform-scanner.js +320 -0
  87. package/dist/scanners/platform-scanner.js.map +1 -0
  88. package/dist/scanners/security-scanner.d.ts +7 -0
  89. package/dist/scanners/security-scanner.d.ts.map +1 -0
  90. package/dist/scanners/security-scanner.js +349 -0
  91. package/dist/scanners/security-scanner.js.map +1 -0
  92. package/dist/scanners/svelte-scanner.d.ts +14 -0
  93. package/dist/scanners/svelte-scanner.d.ts.map +1 -0
  94. package/dist/scanners/svelte-scanner.js +228 -0
  95. package/dist/scanners/svelte-scanner.js.map +1 -0
  96. package/dist/types.d.ts +343 -0
  97. package/dist/types.d.ts.map +1 -0
  98. package/dist/types.js +6 -0
  99. package/dist/types.js.map +1 -0
  100. package/dist/utils/ast-parser.d.ts +21 -0
  101. package/dist/utils/ast-parser.d.ts.map +1 -0
  102. package/dist/utils/ast-parser.js +119 -0
  103. package/dist/utils/ast-parser.js.map +1 -0
  104. package/dist/utils/baseline.d.ts +89 -0
  105. package/dist/utils/baseline.d.ts.map +1 -0
  106. package/dist/utils/baseline.js +156 -0
  107. package/dist/utils/baseline.js.map +1 -0
  108. package/dist/utils/ci-generator.d.ts +34 -0
  109. package/dist/utils/ci-generator.d.ts.map +1 -0
  110. package/dist/utils/ci-generator.js +194 -0
  111. package/dist/utils/ci-generator.js.map +1 -0
  112. package/dist/utils/common.d.ts +8 -0
  113. package/dist/utils/common.d.ts.map +1 -0
  114. package/dist/utils/common.js +38 -0
  115. package/dist/utils/common.js.map +1 -0
  116. package/dist/utils/concurrent.d.ts +16 -0
  117. package/dist/utils/concurrent.d.ts.map +1 -0
  118. package/dist/utils/concurrent.js +49 -0
  119. package/dist/utils/concurrent.js.map +1 -0
  120. package/dist/utils/config-loader.d.ts +8 -0
  121. package/dist/utils/config-loader.d.ts.map +1 -0
  122. package/dist/utils/config-loader.js +154 -0
  123. package/dist/utils/config-loader.js.map +1 -0
  124. package/dist/utils/fix-bot.d.ts +36 -0
  125. package/dist/utils/fix-bot.d.ts.map +1 -0
  126. package/dist/utils/fix-bot.js +274 -0
  127. package/dist/utils/fix-bot.js.map +1 -0
  128. package/dist/utils/git-hooks.d.ts +55 -0
  129. package/dist/utils/git-hooks.d.ts.map +1 -0
  130. package/dist/utils/git-hooks.js +318 -0
  131. package/dist/utils/git-hooks.js.map +1 -0
  132. package/dist/utils/history-report.d.ts +72 -0
  133. package/dist/utils/history-report.d.ts.map +1 -0
  134. package/dist/utils/history-report.js +144 -0
  135. package/dist/utils/history-report.js.map +1 -0
  136. package/dist/utils/init-config.d.ts +23 -0
  137. package/dist/utils/init-config.d.ts.map +1 -0
  138. package/dist/utils/init-config.js +146 -0
  139. package/dist/utils/init-config.js.map +1 -0
  140. package/dist/utils/pr-publisher.d.ts +64 -0
  141. package/dist/utils/pr-publisher.d.ts.map +1 -0
  142. package/dist/utils/pr-publisher.js +265 -0
  143. package/dist/utils/pr-publisher.js.map +1 -0
  144. package/dist/utils/project-detector.d.ts +20 -0
  145. package/dist/utils/project-detector.d.ts.map +1 -0
  146. package/dist/utils/project-detector.js +342 -0
  147. package/dist/utils/project-detector.js.map +1 -0
  148. package/dist/utils/report-uploader.d.ts +35 -0
  149. package/dist/utils/report-uploader.d.ts.map +1 -0
  150. package/dist/utils/report-uploader.js +106 -0
  151. package/dist/utils/report-uploader.js.map +1 -0
  152. package/package.json +78 -0
@@ -0,0 +1,349 @@
1
+ "use strict";
2
+ /**
3
+ * 安全规则 Scanner
4
+ * 参考 OWASP Top 10、SonarQube 安全规则和 CWE 漏洞分类
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.securityRules = void 0;
11
+ const traverse_1 = __importDefault(require("@babel/traverse"));
12
+ const common_js_1 = require("../utils/common.js");
13
+ /** 密钥检测模式 */
14
+ const SECRET_PATTERNS = [
15
+ { regex: /AK[A-Za-z0-9]{16,}/, name: "阿里云 AccessKey", example: "AKLT..." },
16
+ { regex: /sk-[a-zA-Z0-9]{48}/, name: "OpenAI API Key", example: "sk-..." },
17
+ { regex: /wx[a-f0-9]{32}/, name: "微信密钥", example: "wx..." },
18
+ { regex: /password\s*=\s*['"][^'"]{4,}['"]/i, name: "硬编码密码", example: 'password = "xxx"' },
19
+ {
20
+ regex: /api[_-]?key\s*[:=]\s*['"][a-zA-Z0-9-_]{8,}['"]/i,
21
+ name: "API Key",
22
+ example: 'api_key = "xxx"',
23
+ },
24
+ {
25
+ regex: /secret[_-]?key\s*[:=]\s*['"][a-zA-Z0-9-_]{8,}['"]/i,
26
+ name: "Secret Key",
27
+ example: 'secret_key = "xxx"',
28
+ },
29
+ { regex: /token\s*[:=]\s*['"][a-zA-Z0-9-_]{20,}['"]/i, name: "Token", example: 'token = "xxx"' },
30
+ ];
31
+ exports.securityRules = [
32
+ {
33
+ id: "sec-xss-innerhtml",
34
+ name: "危险的 innerHTML 使用",
35
+ description: "避免直接使用 innerHTML,应使用 textContent 或安全库",
36
+ severity: "critical",
37
+ category: "security",
38
+ defaultEnabled: true,
39
+ docsUrl: "https://github.com/wzm111/frontend-guardian/blob/main/docs/rules/sec-xss-innerhtml.md",
40
+ execute(context) {
41
+ const issues = [];
42
+ const ast = context.utils.parseAST(context.source, {
43
+ ext: (0, common_js_1.getFileExt)(context.filePath),
44
+ });
45
+ if (!ast)
46
+ return issues;
47
+ (0, traverse_1.default)(ast, {
48
+ // 1. 赋值: element.innerHTML = ...
49
+ AssignmentExpression(path) {
50
+ const left = path.node.left;
51
+ if (left.type !== "MemberExpression")
52
+ return;
53
+ const prop = left.property;
54
+ if (prop.type === "Identifier" && prop.name === "innerHTML") {
55
+ const { line, column } = path.node.loc?.start || { line: 0, column: 0 };
56
+ issues.push({
57
+ ruleId: "sec-xss-innerhtml",
58
+ title: "危险的 innerHTML 赋值",
59
+ description: "innerHTML 赋值存在 XSS 风险,应使用 textContent 或 DOMPurify",
60
+ severity: "critical",
61
+ file: context.filePath,
62
+ line,
63
+ column,
64
+ source: "innerHTML = ...",
65
+ fix: {
66
+ text: "textContent",
67
+ start: { line, column },
68
+ end: { line, column: column + "innerHTML".length },
69
+ confidence: "high",
70
+ description: "将 innerHTML 替换为 textContent,彻底消除 XSS 风险",
71
+ },
72
+ });
73
+ }
74
+ },
75
+ // 2. 方法调用: element.innerHTML.replace(...)
76
+ MemberExpression(path) {
77
+ const prop = path.node.property;
78
+ if (prop.type === "Identifier" && prop.name === "innerHTML") {
79
+ const parent = path.parentPath?.node;
80
+ // 如果不是赋值左侧,可能是读取(可以放宽)
81
+ if (parent?.type === "AssignmentExpression" && parent.left === path.node) {
82
+ return; // 已在上面处理
83
+ }
84
+ }
85
+ },
86
+ // 3. Vue 的 v-html 指令
87
+ JSXAttribute(path) {
88
+ const attrName = path.node.name;
89
+ if (attrName.type === "JSXIdentifier" && attrName.name === "dangerouslySetInnerHTML") {
90
+ const { line, column } = path.node.loc?.start || { line: 0, column: 0 };
91
+ issues.push({
92
+ ruleId: "sec-xss-innerhtml",
93
+ title: "危险的 dangerouslySetInnerHTML 使用",
94
+ description: "React dangerouslySetInnerHTML 存在 XSS 风险,确保数据来源可信",
95
+ severity: "critical",
96
+ file: context.filePath,
97
+ line,
98
+ column,
99
+ source: "dangerouslySetInnerHTML={...}",
100
+ });
101
+ }
102
+ },
103
+ });
104
+ return issues;
105
+ },
106
+ },
107
+ {
108
+ id: "sec-eval-dangerous",
109
+ name: "禁止 eval / new Function",
110
+ description: "eval, new Function, setTimeout 字符串 存在代码注入风险",
111
+ severity: "critical",
112
+ category: "security",
113
+ defaultEnabled: true,
114
+ docsUrl: "https://github.com/wzm111/frontend-guardian/blob/main/docs/rules/sec-eval-dangerous.md",
115
+ execute(context) {
116
+ const issues = [];
117
+ const ast = context.utils.parseAST(context.source, {
118
+ ext: (0, common_js_1.getFileExt)(context.filePath),
119
+ });
120
+ if (!ast)
121
+ return issues;
122
+ (0, traverse_1.default)(ast, {
123
+ CallExpression(path) {
124
+ const callee = path.node.callee;
125
+ const { line, column } = path.node.loc?.start || { line: 0, column: 0 };
126
+ // 1. eval(...)
127
+ if (callee.type === "Identifier" && callee.name === "eval") {
128
+ issues.push({
129
+ ruleId: "sec-eval-dangerous",
130
+ title: "禁止使用 eval()",
131
+ description: "eval() 存在代码注入风险,应使用 JSON.parse 或安全的序列化方案",
132
+ severity: "critical",
133
+ file: context.filePath,
134
+ line,
135
+ column,
136
+ source: "eval(...)",
137
+ });
138
+ }
139
+ // 2. new Function(...)
140
+ if (callee.type === "NewExpression" ||
141
+ (callee.type === "Identifier" && callee.name === "Function")) {
142
+ // 检查是否是 new Function()
143
+ const parent = path.parentPath?.node;
144
+ if (parent?.type === "NewExpression") {
145
+ issues.push({
146
+ ruleId: "sec-eval-dangerous",
147
+ title: "禁止使用 new Function()",
148
+ description: "new Function() 等同于 eval,存在代码注入风险",
149
+ severity: "critical",
150
+ file: context.filePath,
151
+ line,
152
+ column,
153
+ source: "new Function(...)",
154
+ });
155
+ }
156
+ }
157
+ // 3. setTimeout(string, delay)
158
+ if (callee.type === "Identifier" && callee.name === "setTimeout") {
159
+ const firstArg = path.node.arguments[0];
160
+ if (firstArg?.type === "StringLiteral") {
161
+ issues.push({
162
+ ruleId: "sec-eval-dangerous",
163
+ title: "禁止 setTimeout 传入字符串",
164
+ description: 'setTimeout("string", delay) 会被 eval 执行,存在注入风险',
165
+ severity: "critical",
166
+ file: context.filePath,
167
+ line,
168
+ column,
169
+ source: `setTimeout("${firstArg.value}", ...)`,
170
+ });
171
+ }
172
+ }
173
+ // 4. setInterval(string, delay)
174
+ if (callee.type === "Identifier" && callee.name === "setInterval") {
175
+ const firstArg = path.node.arguments[0];
176
+ if (firstArg?.type === "StringLiteral") {
177
+ issues.push({
178
+ ruleId: "sec-eval-dangerous",
179
+ title: "禁止 setInterval 传入字符串",
180
+ description: 'setInterval("string", delay) 会被 eval 执行,存在注入风险',
181
+ severity: "critical",
182
+ file: context.filePath,
183
+ line,
184
+ column,
185
+ source: `setInterval("${firstArg.value}", ...)`,
186
+ });
187
+ }
188
+ }
189
+ },
190
+ });
191
+ return issues;
192
+ },
193
+ },
194
+ {
195
+ id: "sec-url-validation",
196
+ name: "URL 参数验证",
197
+ description: "使用 window.open, location.href 时应对 URL 做白名单校验",
198
+ severity: "warning",
199
+ category: "security",
200
+ defaultEnabled: true,
201
+ docsUrl: "https://github.com/wzm111/frontend-guardian/blob/main/docs/rules/sec-url-validation.md",
202
+ execute(context) {
203
+ const issues = [];
204
+ const ast = context.utils.parseAST(context.source, {
205
+ ext: (0, common_js_1.getFileExt)(context.filePath),
206
+ });
207
+ if (!ast)
208
+ return issues;
209
+ (0, traverse_1.default)(ast, {
210
+ // window.open(variable, ...)
211
+ CallExpression(path) {
212
+ const callee = path.node.callee;
213
+ if (callee.type === "MemberExpression") {
214
+ const obj = callee.object;
215
+ const prop = callee.property;
216
+ if (obj.type === "Identifier" &&
217
+ obj.name === "window" &&
218
+ prop.type === "Identifier" &&
219
+ prop.name === "open") {
220
+ const firstArg = path.node.arguments[0];
221
+ if (firstArg?.type === "Identifier") {
222
+ const { line, column } = path.node.loc?.start || { line: 0, column: 0 };
223
+ issues.push({
224
+ ruleId: "sec-url-validation",
225
+ title: "window.open 应校验 URL",
226
+ description: `window.open("${firstArg.name}") 应校验目标 URL 是否在白名单中,防止跳转攻击`,
227
+ severity: "warning",
228
+ file: context.filePath,
229
+ line,
230
+ column,
231
+ source: `window.open(${firstArg.name}, ...)`,
232
+ });
233
+ }
234
+ }
235
+ }
236
+ },
237
+ // location.href = variable
238
+ AssignmentExpression(path) {
239
+ const left = path.node.left;
240
+ if (left.type === "MemberExpression") {
241
+ const obj = left.object;
242
+ const prop = left.property;
243
+ if (obj.type === "Identifier" &&
244
+ (obj.name === "location" || obj.name === "window") &&
245
+ prop.type === "Identifier" &&
246
+ prop.name === "href") {
247
+ const right = path.node.right;
248
+ if (right?.type === "Identifier") {
249
+ const { line, column } = path.node.loc?.start || { line: 0, column: 0 };
250
+ issues.push({
251
+ ruleId: "sec-url-validation",
252
+ title: "location.href 应校验 URL",
253
+ description: `location.href = ${right.name} 应校验目标 URL 是否在白名单中,防止跳转攻击`,
254
+ severity: "warning",
255
+ file: context.filePath,
256
+ line,
257
+ column,
258
+ source: `location.href = ${right.name}`,
259
+ });
260
+ }
261
+ }
262
+ }
263
+ },
264
+ });
265
+ return issues;
266
+ },
267
+ },
268
+ {
269
+ id: "sec-no-secrets",
270
+ name: "代码中不得包含密钥",
271
+ description: "禁止在源码中硬编码 API Key、Token、密码等敏感信息",
272
+ severity: "critical",
273
+ category: "security",
274
+ defaultEnabled: true,
275
+ docsUrl: "https://github.com/wzm111/frontend-guardian/blob/main/docs/rules/sec-no-secrets.md",
276
+ execute(context) {
277
+ const issues = [];
278
+ const lines = context.source.split("\n");
279
+ for (let i = 0; i < lines.length; i++) {
280
+ const line = lines[i];
281
+ const lineNum = i + 1;
282
+ for (const pattern of SECRET_PATTERNS) {
283
+ const match = pattern.regex.exec(line);
284
+ if (match) {
285
+ // 跳过注释行
286
+ const trimmed = line.trim();
287
+ if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*"))
288
+ continue;
289
+ // 跳过测试数据中的假密钥
290
+ if (line.includes("test") ||
291
+ line.includes("mock") ||
292
+ line.includes("example") ||
293
+ line.includes("placeholder"))
294
+ continue;
295
+ issues.push({
296
+ ruleId: "sec-no-secrets",
297
+ title: `发现硬编码 ${pattern.name}`,
298
+ description: `第 ${lineNum} 行疑似包含硬编码 ${pattern.name},应使用环境变量存储`,
299
+ severity: "critical",
300
+ file: context.filePath,
301
+ line: lineNum,
302
+ column: match.index + 1,
303
+ source: match[0],
304
+ });
305
+ // 每行只报一次
306
+ break;
307
+ }
308
+ }
309
+ }
310
+ return issues;
311
+ },
312
+ },
313
+ {
314
+ id: "sec-cors-misconfig",
315
+ name: "CORS 配置检查",
316
+ description: "CORS 不应配置为 * 在生产环境",
317
+ severity: "warning",
318
+ category: "security",
319
+ defaultEnabled: true,
320
+ docsUrl: "https://github.com/wzm111/frontend-guardian/blob/main/docs/rules/sec-cors-misconfig.md",
321
+ execute(context) {
322
+ const issues = [];
323
+ const lines = context.source.split("\n");
324
+ for (let i = 0; i < lines.length; i++) {
325
+ const line = lines[i];
326
+ const lineNum = i + 1;
327
+ // 检测 Access-Control-Allow-Origin: * 或 setHeader('Access-Control-Allow-Origin', '*')
328
+ if (/Access-Control-Allow-Origin['"]?\s*[:=,]\s*['"]\*['"]/.test(line) ||
329
+ /allowOrigin\s*[:=]\s*['"]\*['"]/.test(line)) {
330
+ const match = line.match(/\*/);
331
+ if (match) {
332
+ issues.push({
333
+ ruleId: "sec-cors-misconfig",
334
+ title: "CORS 配置为通配符 *",
335
+ description: "生产环境不应允许所有来源(*),应指定具体域名",
336
+ severity: "warning",
337
+ file: context.filePath,
338
+ line: lineNum,
339
+ column: (match.index || 0) + 1,
340
+ source: line.trim(),
341
+ });
342
+ }
343
+ }
344
+ }
345
+ return issues;
346
+ },
347
+ },
348
+ ];
349
+ //# sourceMappingURL=security-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-scanner.js","sourceRoot":"","sources":["../../src/scanners/security-scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Svelte Scanner
3
+ * Phase 4: 覆盖全面化 — 现代框架支持
4
+ *
5
+ * 规则列表:
6
+ * 1. svelte-reactive-statement — $: 响应式语句中使用未声明变量
7
+ * 2. svelte-store-unsubscribe — 订阅 store 但未取消订阅(内存泄漏)
8
+ * 3. svelte-transition-directive — transition 指令在条件渲染中的问题
9
+ * 4. svelte-props-mutate — 直接修改 props(Svelte 中 props 是只读的)
10
+ * 5. svelte-event-modifier — 使用过时的 event modifier(Svelte 5 已弃用)
11
+ */
12
+ import type { Rule } from "../types.js";
13
+ export declare const svelteRules: Rule[];
14
+ //# sourceMappingURL=svelte-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svelte-scanner.d.ts","sourceRoot":"","sources":["../../src/scanners/svelte-scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAsB,MAAM,YAAY,CAAC;AAE3D,eAAO,MAAM,WAAW,EAAE,IAAI,EAqM7B,CAAC"}
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ /**
3
+ * Svelte Scanner
4
+ * Phase 4: 覆盖全面化 — 现代框架支持
5
+ *
6
+ * 规则列表:
7
+ * 1. svelte-reactive-statement — $: 响应式语句中使用未声明变量
8
+ * 2. svelte-store-unsubscribe — 订阅 store 但未取消订阅(内存泄漏)
9
+ * 3. svelte-transition-directive — transition 指令在条件渲染中的问题
10
+ * 4. svelte-props-mutate — 直接修改 props(Svelte 中 props 是只读的)
11
+ * 5. svelte-event-modifier — 使用过时的 event modifier(Svelte 5 已弃用)
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.svelteRules = void 0;
15
+ exports.svelteRules = [
16
+ {
17
+ id: "svelte-reactive-statement",
18
+ name: "响应式语句使用未声明变量",
19
+ description: "$: 响应式语句中引用了未声明的变量",
20
+ severity: "critical",
21
+ category: "component",
22
+ defaultEnabled: true,
23
+ docsUrl: "https://github.com/wzm111/frontend-guardian/blob/main/docs/rules/svelte-reactive-statement.md",
24
+ frameworks: ["svelte"],
25
+ execute(context) {
26
+ const issues = [];
27
+ const source = context.source;
28
+ // Svelte 响应式语句:$: expr
29
+ // 简单正则匹配 $: 语句中引用的变量是否在 script 中声明
30
+ const reactiveRegex = /^\s*\$:\s*(.+)$/gm;
31
+ let match;
32
+ while ((match = reactiveRegex.exec(source)) !== null) {
33
+ const expr = match[1];
34
+ const line = source.slice(0, match.index).split("\n").length;
35
+ // 简单检查:如果表达式中引用了一个看起来像变量的标识符
36
+ // 但 script 中没有 let/const/var 声明它
37
+ const varMatches = expr.match(/\b[a-zA-Z_]\w*\b/g);
38
+ if (varMatches) {
39
+ for (const varName of varMatches) {
40
+ // 跳过常见关键字和内置变量
41
+ if (isSvelteBuiltin(varName))
42
+ continue;
43
+ // 检查是否在 script 中声明
44
+ const declarationPattern = new RegExp(`(?:let|const|var|function)\\s+${varName}\\b`, "m");
45
+ if (!declarationPattern.test(source)) {
46
+ issues.push({
47
+ ruleId: "svelte-reactive-statement",
48
+ title: `响应式语句引用了未声明变量 '${varName}'`,
49
+ description: `\$:${expr.trim()} 中使用了未在 script 中声明的变量 '${varName}'。响应式语句只能引用已声明的变量或 store。`,
50
+ severity: "critical",
51
+ file: context.filePath,
52
+ line,
53
+ column: 1,
54
+ source: match[0].trim(),
55
+ });
56
+ break; // 每条响应式语句只报一次
57
+ }
58
+ }
59
+ }
60
+ }
61
+ return issues;
62
+ },
63
+ },
64
+ {
65
+ id: "svelte-store-unsubscribe",
66
+ name: "Store 订阅未取消",
67
+ description: "subscribe() 返回的 unsubscribe 函数未被调用,导致内存泄漏",
68
+ severity: "warning",
69
+ category: "hooks",
70
+ defaultEnabled: true,
71
+ docsUrl: "https://github.com/wzm111/frontend-guardian/blob/main/docs/rules/svelte-store-unsubscribe.md",
72
+ frameworks: ["svelte"],
73
+ execute(context) {
74
+ const issues = [];
75
+ const source = context.source;
76
+ // 检测 subscribe() 调用
77
+ const subscribeRegex = /\b(\w+)\.subscribe\s*\(/g;
78
+ let match;
79
+ while ((match = subscribeRegex.exec(source)) !== null) {
80
+ const storeName = match[1];
81
+ const subscribePos = match.index;
82
+ const line = source.slice(0, subscribePos).split("\n").length;
83
+ // 检查后面是否有 unsubscribe 调用
84
+ const afterSource = source.slice(subscribePos);
85
+ const unsubscribePattern = new RegExp(`\\b${storeName}\\.unsubscribe\\b|\\bunsubscribe\\s*\\(\\s*${storeName}\\s*\\)`);
86
+ // 检查是否在 onDestroy 中取消订阅
87
+ const hasOnDestroy = /onDestroy\s*\(/.test(source);
88
+ const hasUnsubscribe = unsubscribePattern.test(afterSource);
89
+ if (!hasUnsubscribe && !hasOnDestroy) {
90
+ issues.push({
91
+ ruleId: "svelte-store-unsubscribe",
92
+ title: `Store '${storeName}' 订阅未取消`,
93
+ description: `${storeName}.subscribe() 返回的 unsubscribe 函数未被调用。组件卸载后订阅仍会持续,导致内存泄漏。建议在 onDestroy 中取消订阅或使用 $store 自动订阅语法。`,
94
+ severity: "warning",
95
+ file: context.filePath,
96
+ line,
97
+ column: 1,
98
+ source: `${storeName}.subscribe(...)`,
99
+ fix: {
100
+ text: `const unsubscribe = ${storeName}.subscribe(...);\n\nonDestroy(() => {\n unsubscribe();\n});`,
101
+ start: { line, column: 1 },
102
+ end: { line, column: 1 },
103
+ },
104
+ });
105
+ }
106
+ }
107
+ return issues;
108
+ },
109
+ },
110
+ {
111
+ id: "svelte-props-mutate",
112
+ name: "直接修改 props",
113
+ description: "Svelte 中 props 是只读的,直接赋值会产生编译错误",
114
+ severity: "critical",
115
+ category: "component",
116
+ defaultEnabled: true,
117
+ docsUrl: "https://github.com/wzm111/frontend-guardian/blob/main/docs/rules/svelte-props-mutate.md",
118
+ frameworks: ["svelte"],
119
+ execute(context) {
120
+ const issues = [];
121
+ const source = context.source;
122
+ // Svelte 中 props 通过 export let propName 声明
123
+ // 检测在组件内部对 props 的直接赋值(非响应式语句中)
124
+ const propRegex = /export\s+let\s+(\w+)/g;
125
+ const props = [];
126
+ let match;
127
+ while ((match = propRegex.exec(source)) !== null) {
128
+ props.push(match[1]);
129
+ }
130
+ for (const prop of props) {
131
+ // 检测直接赋值(非 $: 响应式语句中的赋值)
132
+ const assignPattern = new RegExp(`(?<!\\$:)\\s*\\b${prop}\\s*=[^=]`, "g");
133
+ while ((match = assignPattern.exec(source)) !== null) {
134
+ const line = source.slice(0, match.index).split("\n").length;
135
+ const lineText = source.split("\n")[line - 1] || "";
136
+ // 排除 export let 声明本身和响应式语句
137
+ if (lineText.includes("export let"))
138
+ continue;
139
+ if (lineText.trim().startsWith("$:"))
140
+ continue;
141
+ issues.push({
142
+ ruleId: "svelte-props-mutate",
143
+ title: `直接修改了 props '${prop}'`,
144
+ description: `Svelte 中 props 是只读的。\'${prop}\' 通过 export let 声明为 prop,不能直接在组件内部赋值。如需双向绑定请使用 bind: 语法,或使用本地状态副本。`,
145
+ severity: "critical",
146
+ file: context.filePath,
147
+ line,
148
+ column: lineText.indexOf(prop) + 1,
149
+ source: lineText.trim(),
150
+ });
151
+ }
152
+ }
153
+ return issues;
154
+ },
155
+ },
156
+ {
157
+ id: "svelte-event-modifier",
158
+ name: "使用过时的 event modifier",
159
+ description: "Svelte 5 已弃用 event modifier(如 |preventDefault),建议使用事件处理函数",
160
+ severity: "warning",
161
+ category: "component",
162
+ defaultEnabled: true,
163
+ docsUrl: "https://github.com/wzm111/frontend-guardian/blob/main/docs/rules/svelte-event-modifier.md",
164
+ frameworks: ["svelte"],
165
+ execute(context) {
166
+ const issues = [];
167
+ const source = context.source;
168
+ // Svelte 事件修饰符:on:click|preventDefault, on:submit|stopPropagation 等
169
+ const modifierRegex = /on:\w+\|(\w+)/g;
170
+ let match;
171
+ while ((match = modifierRegex.exec(source)) !== null) {
172
+ const modifier = match[1];
173
+ const line = source.slice(0, match.index).split("\n").length;
174
+ issues.push({
175
+ ruleId: "svelte-event-modifier",
176
+ title: `使用了过时的事件修饰符 '|${modifier}'`,
177
+ description: `Svelte 5 已弃用事件修饰符语法(on:event|modifier)。建议使用原生事件处理函数调用 event.${modifier}(),或使用动作(action)实现。`,
178
+ severity: "warning",
179
+ file: context.filePath,
180
+ line,
181
+ column: 1,
182
+ source: match[0],
183
+ });
184
+ }
185
+ return issues;
186
+ },
187
+ },
188
+ ];
189
+ /** Svelte 内置变量和关键字 */
190
+ function isSvelteBuiltin(name) {
191
+ const builtins = new Set([
192
+ "console",
193
+ "window",
194
+ "document",
195
+ "Math",
196
+ "JSON",
197
+ "Date",
198
+ "Array",
199
+ "Object",
200
+ "String",
201
+ "Number",
202
+ "Boolean",
203
+ "Promise",
204
+ "setTimeout",
205
+ "setInterval",
206
+ "clearTimeout",
207
+ "clearInterval",
208
+ "fetch",
209
+ "true",
210
+ "false",
211
+ "null",
212
+ "undefined",
213
+ "if",
214
+ "else",
215
+ "for",
216
+ "while",
217
+ "return",
218
+ "function",
219
+ "const",
220
+ "let",
221
+ "var",
222
+ "new",
223
+ "typeof",
224
+ "instanceof",
225
+ ]);
226
+ return builtins.has(name);
227
+ }
228
+ //# sourceMappingURL=svelte-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svelte-scanner.js","sourceRoot":"","sources":["../../src/scanners/svelte-scanner.ts"],"names":[],"mappings":"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"}