bounty-guard 0.2.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 (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +204 -0
  3. package/dist/app/server.d.ts +23 -0
  4. package/dist/app/server.js +130 -0
  5. package/dist/app/server.js.map +1 -0
  6. package/dist/config.d.ts +24 -0
  7. package/dist/config.js +38 -0
  8. package/dist/config.js.map +1 -0
  9. package/dist/diff.d.ts +41 -0
  10. package/dist/diff.js +191 -0
  11. package/dist/diff.js.map +1 -0
  12. package/dist/doctor.d.ts +25 -0
  13. package/dist/doctor.js +55 -0
  14. package/dist/doctor.js.map +1 -0
  15. package/dist/git-scan.d.ts +7 -0
  16. package/dist/git-scan.js +76 -0
  17. package/dist/git-scan.js.map +1 -0
  18. package/dist/github.d.ts +36 -0
  19. package/dist/github.js +145 -0
  20. package/dist/github.js.map +1 -0
  21. package/dist/glob.d.ts +8 -0
  22. package/dist/glob.js +53 -0
  23. package/dist/glob.js.map +1 -0
  24. package/dist/hooks.d.ts +14 -0
  25. package/dist/hooks.js +45 -0
  26. package/dist/hooks.js.map +1 -0
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.js +345 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/llm/mock.d.ts +9 -0
  31. package/dist/llm/mock.js +14 -0
  32. package/dist/llm/mock.js.map +1 -0
  33. package/dist/llm/openai.d.ts +50 -0
  34. package/dist/llm/openai.js +193 -0
  35. package/dist/llm/openai.js.map +1 -0
  36. package/dist/llm/provider.d.ts +20 -0
  37. package/dist/llm/provider.js +36 -0
  38. package/dist/llm/provider.js.map +1 -0
  39. package/dist/mcp/server.d.ts +8 -0
  40. package/dist/mcp/server.js +181 -0
  41. package/dist/mcp/server.js.map +1 -0
  42. package/dist/report.d.ts +34 -0
  43. package/dist/report.js +181 -0
  44. package/dist/report.js.map +1 -0
  45. package/dist/review.d.ts +22 -0
  46. package/dist/review.js +98 -0
  47. package/dist/review.js.map +1 -0
  48. package/dist/rules/index.d.ts +6 -0
  49. package/dist/rules/index.js +9 -0
  50. package/dist/rules/index.js.map +1 -0
  51. package/dist/rules/model.d.ts +24 -0
  52. package/dist/rules/model.js +2 -0
  53. package/dist/rules/model.js.map +1 -0
  54. package/dist/rules/python.d.ts +2 -0
  55. package/dist/rules/python.js +88 -0
  56. package/dist/rules/python.js.map +1 -0
  57. package/dist/rules/seeds.d.ts +2 -0
  58. package/dist/rules/seeds.js +256 -0
  59. package/dist/rules/seeds.js.map +1 -0
  60. package/dist/scanner.d.ts +24 -0
  61. package/dist/scanner.js +85 -0
  62. package/dist/scanner.js.map +1 -0
  63. package/dist/types.d.ts +43 -0
  64. package/dist/types.js +10 -0
  65. package/dist/types.js.map +1 -0
  66. package/package.json +49 -0
package/dist/report.js ADDED
@@ -0,0 +1,181 @@
1
+ /**
2
+ * 终端报告渲染与 CI 门禁判定。
3
+ */
4
+ import { languageOf } from './review.js';
5
+ import { SEVERITY_RANK } from './types.js';
6
+ /** 粘性评论的识别标记:更新而非新建全靠它 */
7
+ export const COMMENT_MARKER = '<!-- bounty-guard-report -->';
8
+ /** 各严重度的中文标签(终端、Markdown 与告警标注共用) */
9
+ export const LABELS = {
10
+ high: '高危',
11
+ medium: '中危',
12
+ low: '低危',
13
+ info: '提示'
14
+ };
15
+ /** CI 门禁:任一告警达到 failOn 档即应非零退出 */
16
+ export function shouldFail(findings, failOn) {
17
+ const threshold = SEVERITY_RANK[failOn];
18
+ return findings.some((f) => SEVERITY_RANK[f.severity] >= threshold);
19
+ }
20
+ /** 渲染中文终端报告,按文件分组 */
21
+ export function renderReport(findings, meta) {
22
+ const out = [];
23
+ out.push('bounty-guard 扫描报告');
24
+ out.push(`来源:${meta.source} | 扫描文件 ${meta.scannedFiles} 个 | 新增行 ${meta.addedLines} 行`);
25
+ if (meta.review) {
26
+ const r = meta.review;
27
+ out.push(`LLM 复核(${r.provider}):确认 ${r.confirmed} · 误报过滤 ${r.filtered} · 严重度下调 ${r.downgraded}${r.unreviewed ? ` · 未复核 ${r.unreviewed}(保留规则原判)` : ''}`);
28
+ }
29
+ out.push('');
30
+ if (findings.length === 0) {
31
+ out.push('✅ 未发现问题');
32
+ }
33
+ else {
34
+ const byFile = new Map();
35
+ for (const f of findings) {
36
+ const list = byFile.get(f.file);
37
+ if (list)
38
+ list.push(f);
39
+ else
40
+ byFile.set(f.file, [f]);
41
+ }
42
+ for (const [file, list] of byFile) {
43
+ out.push(file);
44
+ for (const f of list) {
45
+ const suffix = f.review
46
+ ? f.review.verdict === 'unsure'
47
+ ? ' · LLM 未能确证,保留原判'
48
+ : f.review.severity
49
+ ? ' · 严重度经复核下调'
50
+ : ''
51
+ : '';
52
+ out.push(` [${LABELS[f.severity]}] ${f.ruleId} · 第 ${f.line} 行${suffix}`);
53
+ out.push(` ${f.snippet.trim()}`);
54
+ out.push(` ⚠ ${f.message}`);
55
+ if (f.review?.fixSuggestion)
56
+ out.push(` 💡 修复建议(复核):${f.review.fixSuggestion}`);
57
+ else if (f.fixHint)
58
+ out.push(` 💡 ${f.fixHint}`);
59
+ }
60
+ out.push('');
61
+ }
62
+ }
63
+ const count = (s) => findings.filter((f) => f.severity === s).length;
64
+ out.push(`汇总:高危 ${count('high')} · 中危 ${count('medium')} · 低危 ${count('low')} · 提示 ${count('info')}`);
65
+ return out.join('\n');
66
+ }
67
+ const MD_ICONS = { high: '🔴', medium: '🟠', low: '🟡', info: '🔵' };
68
+ /** Markdown 版报告:PR 粘性评论与 Job Summary 共用 */
69
+ export function renderMarkdownReport(findings, meta) {
70
+ const out = [];
71
+ out.push('## 🛡 bounty-guard 扫描报告');
72
+ out.push('');
73
+ out.push(`来源:${meta.source} | 扫描文件 ${meta.scannedFiles} 个 | 新增行 ${meta.addedLines} 行`);
74
+ if (meta.review) {
75
+ const r = meta.review;
76
+ out.push('');
77
+ out.push(`**LLM 复核(${r.provider})**:确认 ${r.confirmed} · 误报过滤 ${r.filtered} · 严重度下调 ${r.downgraded}${r.unreviewed ? ` · 未复核 ${r.unreviewed}(保留规则原判)` : ''}`);
78
+ }
79
+ out.push('');
80
+ if (findings.length === 0) {
81
+ out.push('✅ **未发现安全问题**');
82
+ }
83
+ else {
84
+ const count = (s) => findings.filter((f) => f.severity === s).length;
85
+ out.push(`发现 **${findings.length}** 个问题:${LABELS.high} ${count('high')} · ${LABELS.medium} ${count('medium')} · ${LABELS.low} ${count('low')} · ${LABELS.info} ${count('info')}`);
86
+ out.push('');
87
+ for (const f of findings) {
88
+ out.push(`### ${MD_ICONS[f.severity]} \`${f.file}:${f.line}\` — ${LABELS[f.severity]} · \`${f.ruleId}\``);
89
+ out.push('');
90
+ // 代码块围栏升级:片段本身含三反引号时用四反引号包裹
91
+ const fence = f.snippet.includes('```') ? '````' : '```';
92
+ const lang = languageOf(f.file);
93
+ out.push(fence + (lang === 'unknown' ? '' : lang));
94
+ out.push(f.snippet);
95
+ out.push(fence);
96
+ out.push('');
97
+ out.push(`- ⚠️ ${f.message}`);
98
+ if (f.review?.fixSuggestion)
99
+ out.push(`- 💡 复核建议:${f.review.fixSuggestion}`);
100
+ else if (f.fixHint)
101
+ out.push(`- 💡 ${f.fixHint}`);
102
+ if (f.review?.verdict === 'unsure')
103
+ out.push('- ❓ LLM 未能确证,保留原判');
104
+ out.push('');
105
+ }
106
+ }
107
+ out.push('---');
108
+ out.push(`<sub>🤖 由 <a href="https://github.com/Croesus-K/bounty-guard">bounty-guard</a> 自动生成 · 规则初筛${meta.review ? ' + LLM 复核' : ''} · 粘性评论,重复扫描只更新本条</sub>`);
109
+ out.push('');
110
+ out.push(COMMENT_MARKER);
111
+ return out.join('\n');
112
+ }
113
+ /** SARIF 级别映射:high → error,medium → warning,low/info → note */ function sarifLevel(severity) {
114
+ return severity === 'high' ? 'error' : severity === 'medium' ? 'warning' : 'note';
115
+ }
116
+ /** SARIF 2.1.0 输出:可直接对接 GitHub code-scanning(actions/upload-sarif) */
117
+ export function renderSarif(findings, meta) {
118
+ const rules = new Map();
119
+ const results = findings.map((f) => {
120
+ if (!rules.has(f.ruleId)) {
121
+ rules.set(f.ruleId, {
122
+ id: f.ruleId,
123
+ shortDescription: { text: f.message },
124
+ defaultConfiguration: { level: sarifLevel(f.severity) }
125
+ });
126
+ }
127
+ return {
128
+ ruleId: f.ruleId,
129
+ level: sarifLevel(f.severity),
130
+ message: { text: f.review?.fixSuggestion ? `${f.message}。${f.review.fixSuggestion}` : f.message },
131
+ locations: [
132
+ {
133
+ physicalLocation: {
134
+ artifactLocation: { uri: f.file.replace(/\\/g, '/') },
135
+ region: { startLine: f.line }
136
+ }
137
+ }
138
+ ]
139
+ };
140
+ });
141
+ return {
142
+ $schema: 'https://json.schemastore.org/sarif-2.1.0.json',
143
+ version: '2.1.0',
144
+ runs: [
145
+ {
146
+ tool: {
147
+ driver: {
148
+ name: 'bounty-guard',
149
+ informationUri: 'https://github.com/Croesus-K/bounty-guard',
150
+ rules: [...rules.values()]
151
+ }
152
+ },
153
+ results,
154
+ properties: { source: meta.source, scannedFiles: meta.scannedFiles, addedLines: meta.addedLines }
155
+ }
156
+ ]
157
+ };
158
+ }
159
+ /** 周报表格:配合 scripts/weekly-report.ts 定时生成 docs/metrics.md */
160
+ export function renderMetricsTable(rows, date) {
161
+ const totalAdded = rows.reduce((n, r) => n + r.addedLines, 0);
162
+ const totalFindings = rows.reduce((n, r) => n + r.findings.length, 0);
163
+ return [
164
+ '# 误报率周报',
165
+ '',
166
+ `> 自动生成于 ${date} · 来源清单 \`scripts/metrics-prs.txt\` · 样本 ${rows.length} 个真实 PR`,
167
+ '',
168
+ '| 指标 | 数值 |',
169
+ '|---|---|',
170
+ `| 新增行 | ${totalAdded} |`,
171
+ `| 命中 | ${totalFindings} |`,
172
+ '',
173
+ '| PR | 新增行 | 命中 |',
174
+ '|---|---|---|',
175
+ ...rows.map((r) => `| ${r.pr} | ${r.addedLines} | ${r.findings.length === 0 ? '0' : r.findings.join('<br>')} |`),
176
+ '',
177
+ '<sub>由 bounty-guard 自动生成,每周五更新</sub>',
178
+ ''
179
+ ].join('\n');
180
+ }
181
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { Finding, LLMProvider } from './types.js';
2
+ /** 从文件扩展名推断语言(供 LLM 判断语境) */
3
+ export declare function languageOf(file: string): string;
4
+ export interface ReviewOptions {
5
+ /** 并发复核数(默认 4) */
6
+ concurrency?: number;
7
+ /** 最多送审的告警组数(默认 20);按严重度优先,超出部分保留规则原判 */
8
+ maxReviews?: number;
9
+ }
10
+ export interface ReviewOutcome {
11
+ /** 过滤误报后的最终告警(已复核项挂载 review 结果) */
12
+ findings: Finding[];
13
+ /** 被判误报剔除的数量 */
14
+ filtered: number;
15
+ /** 严重度被下调的数量 */
16
+ downgraded: number;
17
+ /** 实际送入 LLM 的告警数(按条计) */
18
+ reviewed: number;
19
+ /** 因超出上限未复核、保留规则原判的告警数 */
20
+ unreviewed: number;
21
+ }
22
+ export declare function reviewFindings(findings: Finding[], provider: LLMProvider, options?: ReviewOptions): Promise<ReviewOutcome>;
package/dist/review.js ADDED
@@ -0,0 +1,98 @@
1
+ /**
2
+ * 复核流水线:规则候选告警 → LLM 逐条核实 → 过滤误报 / 应用降级。
3
+ * 约束:LLM 只复核、不发明——本模块只会增改已有 Finding 的复核字段
4
+ * 或剔除误报,绝不发明新告警;被剔除的误报计数上报(降噪可量化)。
5
+ *
6
+ * 成本护栏:
7
+ * - 相同「规则 + 归一化片段」的告警共享一次复核结论(去重);
8
+ * - 按严重度优先送审,超出 maxReviews 的保留规则原判并计入 unreviewed;
9
+ * - 并发度可调(默认 4),避免大 diff 时逐条串行等待。
10
+ */
11
+ import { SEVERITY_RANK } from './types.js';
12
+ const LANGUAGE_BY_EXT = {
13
+ js: 'javascript',
14
+ mjs: 'javascript',
15
+ cjs: 'javascript',
16
+ jsx: 'javascript',
17
+ ts: 'typescript',
18
+ tsx: 'typescript',
19
+ vue: 'vue',
20
+ html: 'html',
21
+ css: 'css',
22
+ py: 'python',
23
+ go: 'go',
24
+ java: 'java',
25
+ php: 'php',
26
+ rb: 'ruby',
27
+ sql: 'sql'
28
+ };
29
+ /** 从文件扩展名推断语言(供 LLM 判断语境) */
30
+ export function languageOf(file) {
31
+ const dot = file.lastIndexOf('.');
32
+ if (dot === -1 || dot === file.length - 1)
33
+ return 'unknown';
34
+ return LANGUAGE_BY_EXT[file.slice(dot + 1).toLowerCase()] ?? 'unknown';
35
+ }
36
+ export async function reviewFindings(findings, provider, options = {}) {
37
+ const concurrency = Math.max(1, Math.floor(options.concurrency ?? 4));
38
+ const maxReviews = Math.max(1, Math.floor(options.maxReviews ?? 20));
39
+ // 按「规则 + 归一化片段」分组去重:同组告警共享一次复核结论
40
+ const groups = new Map();
41
+ for (const finding of findings) {
42
+ const key = `${finding.ruleId}|${finding.snippet.trim()}`;
43
+ const group = groups.get(key);
44
+ if (group)
45
+ group.push(finding);
46
+ else
47
+ groups.set(key, [finding]);
48
+ }
49
+ // 复核优先级:严重度高的组先送审
50
+ const ordered = [...groups.entries()].sort((a, b) => SEVERITY_RANK[b[1][0].severity] - SEVERITY_RANK[a[1][0].severity]);
51
+ const toReview = ordered.slice(0, maxReviews);
52
+ const verdicts = new Map();
53
+ let cursor = 0;
54
+ const worker = async () => {
55
+ for (;;) {
56
+ const index = cursor++;
57
+ if (index >= toReview.length)
58
+ return;
59
+ const [key, group] = toReview[index];
60
+ const sample = group[0];
61
+ const request = {
62
+ ruleId: sample.ruleId,
63
+ severity: sample.severity,
64
+ snippet: sample.snippet,
65
+ language: languageOf(sample.file),
66
+ context: sample.contextLines
67
+ };
68
+ verdicts.set(key, await provider.review(request));
69
+ }
70
+ };
71
+ await Promise.all(Array.from({ length: Math.min(concurrency, toReview.length) }, worker));
72
+ const kept = [];
73
+ let filtered = 0;
74
+ let downgraded = 0;
75
+ let reviewed = 0;
76
+ for (const [key, group] of groups.entries()) {
77
+ const review = verdicts.get(key);
78
+ if (!review) {
79
+ kept.push(...group); // 超出上限:保留规则原判
80
+ continue;
81
+ }
82
+ for (const finding of group) {
83
+ reviewed++;
84
+ if (review.verdict === 'false-positive') {
85
+ filtered++;
86
+ continue; // 误报剔除——降噪主通道
87
+ }
88
+ const result = { ...finding, review };
89
+ if (review.severity && SEVERITY_RANK[review.severity] < SEVERITY_RANK[result.severity]) {
90
+ result.severity = review.severity; // 只可能下调(适配器已丢弃上调建议,这里双保险)
91
+ downgraded++;
92
+ }
93
+ kept.push(result);
94
+ }
95
+ }
96
+ return { findings: kept, filtered, downgraded, reviewed, unreviewed: findings.length - reviewed };
97
+ }
98
+ //# sourceMappingURL=review.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../src/review.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { Rule, RuleContext } from './model.js';
2
+ export type { Rule, RuleContext } from './model.js';
3
+ /** 当前注册的全部规则(JS 种子规则 + Python 子集,数组顺序即运行顺序) */
4
+ export declare const ALL_RULES: Rule[];
5
+ /** 对单个新增行运行全部规则,返回命中的规则列表 */
6
+ export declare function runRules(ctx: RuleContext): Rule[];
@@ -0,0 +1,9 @@
1
+ import { PYTHON_RULES } from './python.js';
2
+ import { SEED_RULES } from './seeds.js';
3
+ /** 当前注册的全部规则(JS 种子规则 + Python 子集,数组顺序即运行顺序) */
4
+ export const ALL_RULES = [...SEED_RULES, ...PYTHON_RULES];
5
+ /** 对单个新增行运行全部规则,返回命中的规则列表 */
6
+ export function runRules(ctx) {
7
+ return ALL_RULES.filter((rule) => rule.detect(ctx));
8
+ }
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAIxC,+CAA+C;AAC/C,MAAM,CAAC,MAAM,SAAS,GAAW,CAAC,GAAG,UAAU,EAAE,GAAG,YAAY,CAAC,CAAC;AAElE,6BAA6B;AAC7B,MAAM,UAAU,QAAQ,CAAC,GAAgB;IACvC,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AACtD,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { Severity } from '../types.js';
2
+ /**
3
+ * 规则判定上下文:只暴露「单行 + 周边窗口」,规则不做跨文件分析——
4
+ * 这既控制成本,也逼着规则把误报率压在行级判断里。
5
+ */
6
+ export interface RuleContext {
7
+ /** 新文件路径(已按 ignore 过滤) */
8
+ file: string;
9
+ /** 新文件中的行号 */
10
+ line: number;
11
+ /** 命中行原文(不含换行符) */
12
+ content: string;
13
+ /** hunk 内命中行前后各至多 3 行的 add/context 行文本(不含命中行自身) */
14
+ context: string[];
15
+ }
16
+ /** 规则模型:每条规则必须自带中文描述与修复建议 */
17
+ export interface Rule {
18
+ id: string;
19
+ severity: Severity;
20
+ message: string;
21
+ fixHint: string;
22
+ /** 返回 true 表示命中;只在新增行上被调用 */
23
+ detect(ctx: RuleContext): boolean;
24
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/rules/model.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ import type { Rule } from './model.js';
2
+ export declare const PYTHON_RULES: Rule[];
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Python 规则子集(多语言第一步)。
3
+ * 只在 .py 文件上运行;判定倾向与 JS 规则一致:宁可漏报不可误报。
4
+ * Python 注释以 # 开头,匹配前简易剔除(字符串含 # 时宁可漏报)。
5
+ */
6
+ function isPython(file) {
7
+ return file.toLowerCase().endsWith('.py');
8
+ }
9
+ function stripPyComment(line) {
10
+ return line.split('#')[0];
11
+ }
12
+ function mentionsAny(code, context, word) {
13
+ return word.test(code) || context.some((l) => word.test(l));
14
+ }
15
+ /** P1. Python 动态执行:eval / exec / compile */
16
+ const PY_DANGEROUS_EVAL = {
17
+ id: 'py-dangerous-eval',
18
+ severity: 'high',
19
+ message: '使用动态代码执行(eval / exec / compile),注入面直接暴露给不可信输入',
20
+ fixHint: '改用 ast.literal_eval、json.loads 或查表映射等显式解析',
21
+ detect(ctx) {
22
+ if (!isPython(ctx.file))
23
+ return false;
24
+ const code = stripPyComment(ctx.content);
25
+ return (/(?:^|[^\w.])eval\s*\(/.test(code) ||
26
+ /(?:^|[^\w.])exec\s*\(/.test(code) ||
27
+ /\bcompile\s*\(/.test(code));
28
+ }
29
+ };
30
+ /** P2. Python 弱哈希(MD5/SHA1)用于密码 */
31
+ const PY_WEAK_HASH_PASSWORD = {
32
+ id: 'py-weak-hash-password',
33
+ severity: 'high',
34
+ message: '对密码相关数据使用 MD5/SHA1,可被高速暴力破解',
35
+ fixHint: '改用 bcrypt / passlib 的 CryptContext(argon2/bcrypt)并加盐',
36
+ detect(ctx) {
37
+ if (!isPython(ctx.file))
38
+ return false;
39
+ const code = stripPyComment(ctx.content);
40
+ if (!/\b(?:md5|sha1)\s*\(/i.test(code))
41
+ return false;
42
+ return mentionsAny(code, ctx.context, /(password|passwd|pwd|密码)/i);
43
+ }
44
+ };
45
+ /** P3. Python SQL 字符串拼接(+ / % 元组 / .format / f-string 插值) */
46
+ const PY_SQL_CONCAT = {
47
+ id: 'py-sql-concat',
48
+ severity: 'high',
49
+ message: 'SQL 语句通过字符串拼接引入变量,存在注入风险',
50
+ fixHint: '改用参数化查询(占位符 + 参数元组)或 ORM 的绑定参数',
51
+ detect(ctx) {
52
+ if (!isPython(ctx.file))
53
+ return false;
54
+ const code = stripPyComment(ctx.content);
55
+ const sql = /(?:\bselect\b.{0,120}?\bfrom\b|\binsert\s+into\b|\bupdate\s+\S+.{0,60}?\bset\b|\bdelete\s+from\b)/i.test(code);
56
+ if (!sql)
57
+ return false;
58
+ const fString = /\bf['"]/.test(code) && /\{/.test(code);
59
+ return /\+/.test(code) || /\bformat\s*\(/.test(code) || /%\s*\(/.test(code) || fString;
60
+ }
61
+ };
62
+ /** P4. Python 硬编码密钥(模块级常量形态) */
63
+ const PY_HARDCODED_SECRET = {
64
+ id: 'py-hardcoded-secret',
65
+ severity: 'high',
66
+ message: '疑似把密钥/口令硬编码进源码,泄露即失控',
67
+ fixHint: '改从环境变量(os.environ)或密钥管理服务读取,并轮换已泄露的旧值',
68
+ detect(ctx) {
69
+ if (!isPython(ctx.file))
70
+ return false;
71
+ const code = ctx.content.split('#')[0];
72
+ const m = code.match(/^\s*([A-Za-z0-9_]*(?:secret|api_?key|access_?key|private_?key|password|passwd|token)[A-Za-z0-9_]*)\s*=\s*(['"])(.{8,}?)\2\s*$/i);
73
+ if (!m)
74
+ return false;
75
+ if (/(os\.environ|getenv|config\[|config\.)/i.test(code))
76
+ return false;
77
+ if (/(your|example|placeholder|xxx|changeme|<[^>]*>)/i.test(m[3]))
78
+ return false;
79
+ return true;
80
+ }
81
+ };
82
+ export const PYTHON_RULES = [
83
+ PY_DANGEROUS_EVAL,
84
+ PY_WEAK_HASH_PASSWORD,
85
+ PY_SQL_CONCAT,
86
+ PY_HARDCODED_SECRET
87
+ ];
88
+ //# sourceMappingURL=python.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"python.js","sourceRoot":"","sources":["../../src/rules/python.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Rule } from './model.js';
2
+ export declare const SEED_RULES: Rule[];