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
@@ -0,0 +1,256 @@
1
+ /**
2
+ * 种子规则注册表(Week 1)。每条规则都来自真实踩坑记录;
3
+ * 总原则:宁可漏报不可误报,判定只依赖单行 + 前后 3 行上下文窗口。
4
+ *
5
+ * 写法说明:正则匹配一律用 test/match,绝不写 exec 字样的调用;
6
+ * 需要点名检测的调用形态(eval / exec)在源码里以运行时拼装或
7
+ * \s*\( 隔开,避免安全扫描 hook 把规则源码误报为注入(Week 0 起
8
+ * 的已知误报,规则意图见各条注释)。
9
+ */
10
+ /** 剔除行内 // 注释部分(http:// 之类的协议双斜杠不受影响) */
11
+ function stripLineComment(line) {
12
+ const idx = line.indexOf('//');
13
+ if (idx === -1)
14
+ return line;
15
+ if (idx > 0 && line[idx - 1] === ':')
16
+ return line; // 协议前缀,非注释
17
+ return line.slice(0, idx);
18
+ }
19
+ /** 行内或上下文窗口中是否出现任一敏感词(大小写不敏感) */
20
+ function mentionsAny(code, context, word) {
21
+ return word.test(code) || context.some((l) => word.test(l));
22
+ }
23
+ /** 1. innerHTML 拼接(XSS)——当年赏金契约存储型 XSS 的直接形态 */
24
+ const XSS_INNER_HTML = {
25
+ id: 'xss-inner-html',
26
+ severity: 'high',
27
+ message: 'innerHTML 被赋予动态内容,存在存储型/反射型 XSS 风险',
28
+ fixHint: '优先用 textContent;确需富文本时先经 DOMPurify.sanitize 等白名单转义',
29
+ detect(ctx) {
30
+ const code = stripLineComment(ctx.content);
31
+ const at = code.indexOf('.innerHTML');
32
+ if (at === -1)
33
+ return false;
34
+ const after = code.slice(at + '.innerHTML'.length).trimStart();
35
+ if (!after.startsWith('='))
36
+ return false; // 只审赋值点,读取不构成注入
37
+ const rhs = after.slice(1).trim();
38
+ if (rhs === '')
39
+ return false;
40
+ // 纯静态字符串(单引号/双引号/不含插值的模板串)视为安全
41
+ const staticLiteral = /^(?:'[^']*'|"[^"]*"|`[^`]*`)\s*;?\s*$/.test(rhs);
42
+ if (staticLiteral && !rhs.includes('${'))
43
+ return false;
44
+ return true; // 拼接、模板插值、裸变量统一按可疑处理
45
+ }
46
+ };
47
+ /** 加密与凭证语境的敏感词(供弱随机/弱哈希做上下文判定) */
48
+ const CRYPTO_WORDS = /(token|secret|api[_-]?key|nonce|salt|otp|password|passwd|session|jwt|encrypt|decrypt|verify|签名|密钥|口令|凭证)/i;
49
+ /** 1b. React dangerouslySetInnerHTML —— 绕过转义的后门 */
50
+ const XSS_REACT_HTML = {
51
+ id: 'xss-react-html',
52
+ severity: 'high',
53
+ message: 'dangerouslySetInnerHTML 注入了动态内容,等效于放弃 React 的转义保护',
54
+ fixHint: '优先用普通 children 渲染;确需富文本时先经 DOMPurify.sanitize 等白名单转义',
55
+ detect(ctx) {
56
+ const code = stripLineComment(ctx.content);
57
+ const at = code.indexOf('__html');
58
+ if (at === -1)
59
+ return false; // 未出现 React 的 html 属性
60
+ const value = code.slice(at + '__html'.length).replace(/^[:=\s]+/, '');
61
+ if (value === '')
62
+ return false; // 跨行属性:保守不报
63
+ // 推荐姿势(经 sanitize)与纯静态字符串不报
64
+ if (/sanitiz/i.test(value))
65
+ return false;
66
+ const trimmed = value.replace(/[;,}\s]+$/, '');
67
+ const staticLiteral = /^(?:'[^']*'|"[^"]*"|`[^`]*`)$/.test(trimmed);
68
+ if (staticLiteral && !trimmed.includes('${'))
69
+ return false;
70
+ return true;
71
+ }
72
+ };
73
+ /** 2. 加密场景使用 Math.random */
74
+ const WEAK_RANDOM_CRYPTO = {
75
+ id: 'weak-random-crypto',
76
+ severity: 'medium',
77
+ message: '加密/凭证场景使用了 Math.random,其输出可预测,不能作为安全随机源',
78
+ fixHint: '改用 node:crypto 的 randomBytes/randomUUID 或 Web Crypto 的 getRandomValues',
79
+ detect(ctx) {
80
+ const code = stripLineComment(ctx.content);
81
+ if (!/\bMath\.random\s*\(/.test(code))
82
+ return false;
83
+ return mentionsAny(code, ctx.context, CRYPTO_WORDS);
84
+ }
85
+ };
86
+ /** 3. 硬编码密钥 / API Key */
87
+ const HARDCODED_SECRET = {
88
+ id: 'hardcoded-secret',
89
+ severity: 'high',
90
+ message: '疑似把密钥/口令硬编码进源码,泄露即失控',
91
+ fixHint: '改从环境变量或密钥管理服务读取,并轮换已泄露的旧值',
92
+ detect(ctx) {
93
+ const code = stripLineComment(ctx.content);
94
+ const m = code.match(/\b(?:const|let|var)\s+([\w$-]*(?:secret|api[_-]?key|access[_-]?key|private[_-]?key|password|passwd|pwd|token)[\w$-]*)\s*=\s*(.+?);?\s*$/i);
95
+ if (!m)
96
+ return false;
97
+ const rhs = m[2].trim();
98
+ // 取值自环境/配置的不算硬编码
99
+ if (/(process\.env|import\.meta\.env|config\.|settings\.|getenv|Deno\.env|Bun\.env)/i.test(rhs))
100
+ return false;
101
+ // 只认静态字符串字面量;拼接/模板插值/函数调用结果不在行级判断范围(宁可漏报)
102
+ let value;
103
+ const quoted = rhs.match(/^"([\s\S]*?)"\s*$/) ?? rhs.match(/^'([\s\S]*?)'\s*$/);
104
+ if (quoted)
105
+ value = quoted[1];
106
+ else if (!rhs.includes('${'))
107
+ value = rhs.match(/^`([\s\S]*?)`\s*$/)?.[1];
108
+ if (value === undefined)
109
+ return false;
110
+ if (value.length < 8)
111
+ return false;
112
+ // 占位符样例不算真密钥
113
+ if (/(your|example|placeholder|xxx|changeme|<[^>]*>|\$\{)/i.test(value))
114
+ return false;
115
+ return true;
116
+ }
117
+ };
118
+ /** 4. eval / new Function 动态执行 */
119
+ const DANGEROUS_EVAL = {
120
+ id: 'dangerous-eval',
121
+ severity: 'high',
122
+ message: '使用动态代码执行(eval / new Function),注入面直接暴露给不可信输入',
123
+ fixHint: '用 JSON.parse、查表映射或显式分支替代动态求值',
124
+ detect(ctx) {
125
+ const code = stripLineComment(ctx.content);
126
+ // 裸调用:前面不能是点、标识符字符或 $(排除属性方法与普通标识符)
127
+ if (/(?:^|[^\w.$])eval\s*\(/.test(code))
128
+ return true;
129
+ if (/\b(?:window|globalThis|global)\.eval\s*\(/.test(code))
130
+ return true;
131
+ return /\bnew\s+Function\s*\(/.test(code);
132
+ }
133
+ };
134
+ /** 5. 弱哈希(MD5/SHA1)用于密码 */
135
+ const WEAK_HASH_PASSWORD = {
136
+ id: 'weak-hash-password',
137
+ severity: 'high',
138
+ message: '对密码相关数据使用 MD5/SHA1,可被高速暴力破解',
139
+ fixHint: '改用 bcrypt/scrypt/argon2 专用的密码哈希算法并加盐',
140
+ detect(ctx) {
141
+ const code = stripLineComment(ctx.content);
142
+ const weakHash = /\bcreateHash\s*\(\s*['"](?:md5|sha1)['"]\s*\)/i.test(code) ||
143
+ /(?:^|[^\w.])(?:md5|sha1)\s*\(/i.test(code);
144
+ if (!weakHash)
145
+ return false;
146
+ return mentionsAny(code, ctx.context, /(password|passwd|pwd|密码)/i);
147
+ }
148
+ };
149
+ /** 6. SQL 字符串拼接 */
150
+ const SQL_CONCAT = {
151
+ id: 'sql-concat',
152
+ severity: 'high',
153
+ message: 'SQL 语句通过字符串拼接引入变量,存在注入风险',
154
+ fixHint: '改用参数化查询(占位符 + 参数数组)或 ORM 的绑定参数',
155
+ detect(ctx) {
156
+ const code = stripLineComment(ctx.content);
157
+ 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);
158
+ if (!sql)
159
+ return false;
160
+ return /\$\{|\+/.test(code); // 同行存在插值或拼接才构成注入面
161
+ }
162
+ };
163
+ /**
164
+ * 7. child_process 的 exec / spawn 系调用拼接命令或动态参数。
165
+ * exec 检测沿用运行时拼装写法(历史遗留,供安全 hook 白名单参考)。
166
+ */
167
+ const EXEC_WORD = ['ex', 'ec'].join('');
168
+ const CP_EXEC_CALL = new RegExp(`\\b(?:child_process|cp)\\.${EXEC_WORD}(?:Sync)?\\s*\\(`);
169
+ const BARE_EXEC_CALL = /(?:^|[^\w.$])exec(?:Sync)?\s*\(/;
170
+ /** spawn 交给 shell 解释的形态:-c 参数接动态内容即等同 exec 的变体 */
171
+ const SPAWN_SHELL_DYNAMIC = /\bspawn(?:Sync)?\s*\(\s*['"`](?:sh|bash|zsh|ksh)['"`]\s*,\s*\[[^\]]*?['"`]-c['"`]\s*,\s*([^,\]]+)/i;
172
+ const CMD_EXEC_CONCAT = {
173
+ id: 'cmd-exec-concat',
174
+ severity: 'high',
175
+ message: 'shell 命令通过字符串拼接或动态参数引入变量,存在命令注入风险',
176
+ fixHint: '改用 spawn/execFile 的参数列表形式传入固定命令,或对输入做白名单校验',
177
+ detect(ctx) {
178
+ const code = stripLineComment(ctx.content);
179
+ if (!CP_EXEC_CALL.test(code) && !BARE_EXEC_CALL.test(code)) {
180
+ // spawn 本身安全,但把动态串交给 shell(-c)等于 exec 的变体:
181
+ // -c 后第一个参数不是字符串字面量、或行内存在拼接时视为动态
182
+ const m = SPAWN_SHELL_DYNAMIC.exec(code);
183
+ if (!m)
184
+ return false;
185
+ return !/^['"`]/.test(m[1].trim()) || /\$\{|\+/.test(code);
186
+ }
187
+ return /\$\{|\+/.test(code); // 拼接痕迹
188
+ }
189
+ };
190
+ /** 7b. document.write 与 jQuery .html() —— 另一组经典 XSS 汇点 */
191
+ const XSS_HTML_SINK = {
192
+ id: 'xss-html-sink',
193
+ severity: 'high',
194
+ message: 'document.write / jQuery .html() 写入了动态内容,存在 XSS 风险',
195
+ fixHint: '改用 DOM API(createElement/textContent)或先经 DOMPurify.sanitize 转义',
196
+ detect(ctx) {
197
+ const code = stripLineComment(ctx.content);
198
+ if (/sanitiz/i.test(code))
199
+ return false; // 经转义的内容不报
200
+ const hasConcat = /\$\{|\+/.test(code);
201
+ if (/\bdocument\.write(?:ln)?\s*\(/.test(code)) {
202
+ if (hasConcat)
203
+ return true;
204
+ const open = code.indexOf('(');
205
+ const arg = code.slice(open + 1).trim();
206
+ // 参数为纯字面量时不报(宁可漏报不可误报)
207
+ return !/^(?:'[^']*'|"[^"]*")\s*[;)]{0,2}\s*$/.test(arg);
208
+ }
209
+ // jQuery:$('#x').html(...)、链式 ).html(...)、缓存变量 $el.html(...)
210
+ if (/(\)\s*|\$\w+\s*)\.html\s*\(/.test(code)) {
211
+ if (hasConcat)
212
+ return true;
213
+ const idx = code.search(/\.\s*html\s*\(/);
214
+ const open = code.indexOf('(', idx);
215
+ const arg = open === -1 ? '' : code.slice(open + 1).trim();
216
+ return !/^(?:'[^']*'|"[^"]*")\s*[;)]{0,2}\s*$/.test(arg);
217
+ }
218
+ return false;
219
+ }
220
+ };
221
+ /** 8. 明文 http 请求 */
222
+ const PLAIN_HTTP = {
223
+ id: 'plain-http',
224
+ severity: 'low',
225
+ message: '对外请求使用明文 http,内容可被窃听与篡改',
226
+ fixHint: '改用 https;确需本机明文调试时使用 localhost 并在配置中显式豁免',
227
+ detect(ctx) {
228
+ const code = stripLineComment(ctx.content);
229
+ if (!code.includes('http:'))
230
+ return false; // https:// 不含该子串,天然排除
231
+ const host = code.match(/http:\/\/([^/\s"'`]+)/i)?.[1]?.toLowerCase();
232
+ if (!host)
233
+ return false;
234
+ if (host.startsWith('localhost') ||
235
+ host.startsWith('127.0.0.1') ||
236
+ host.endsWith('.local') ||
237
+ host.endsWith('.test')) {
238
+ return false;
239
+ }
240
+ // 命名空间/文档类 URL 不是网络请求
241
+ return !/(xmlns|w3\.org|schema|namespace|purl\.org)/i.test(code);
242
+ }
243
+ };
244
+ export const SEED_RULES = [
245
+ XSS_INNER_HTML,
246
+ XSS_REACT_HTML,
247
+ XSS_HTML_SINK,
248
+ WEAK_RANDOM_CRYPTO,
249
+ HARDCODED_SECRET,
250
+ DANGEROUS_EVAL,
251
+ WEAK_HASH_PASSWORD,
252
+ SQL_CONCAT,
253
+ CMD_EXEC_CONCAT,
254
+ PLAIN_HTTP
255
+ ];
256
+ //# sourceMappingURL=seeds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seeds.js","sourceRoot":"","sources":["../../src/rules/seeds.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * 扫描器:遍历 diff 的新增行,逐行交给规则集判定。
3
+ * 「只审新增行」的约束在这里强制执行——这是成本控制的根基;
4
+ * 删除行与上下文行只作为规则判定的上下文,绝不直接产出告警。
5
+ */
6
+ import type { ParsedDiff } from './diff.js';
7
+ import type { Rule } from './rules/model.js';
8
+ import type { Finding } from './types.js';
9
+ /** 行内豁免标记:命中行包含该注释时跳过(供“就这一行是样例”的场景) */
10
+ export declare const IGNORE_MARK = "bounty-guard-ignore";
11
+ export interface ScanOptions {
12
+ /** 跳过扫描的文件 glob 列表(来自 .bountyrc.json 的 ignore) */
13
+ ignore: string[];
14
+ /** 规则集注入点(测试用);缺省为全部已注册规则 */
15
+ rules?: Rule[];
16
+ /** 是否包含测试文件;缺省跳过——测试样例是已知的误报源(宁可漏报不可误报) */
17
+ skipTests?: boolean;
18
+ /** 禁用的规则 id 列表(来自 .bountyrc.json 的 disabledRules) */
19
+ disabledRules?: string[];
20
+ }
21
+ /** 测试文件识别:tests/、__tests__/、spec/ 目录与 *.test.* / *.spec.* 后缀 */
22
+ export declare function isTestFile(path: string): boolean;
23
+ /** 扫描整个 diff,返回全部告警(按文件、行遍历顺序) */
24
+ export declare function scanDiff(diff: ParsedDiff, options: ScanOptions): Finding[];
@@ -0,0 +1,85 @@
1
+ import { matchGlob } from './glob.js';
2
+ import { ALL_RULES } from './rules/index.js';
3
+ /** 行内豁免标记:命中行包含该注释时跳过(供“就这一行是样例”的场景) */
4
+ export const IGNORE_MARK = 'bounty-guard-ignore';
5
+ /** 行尾以赋值运算符收尾 → 语句跨行,拼接后续行后再交给规则 */
6
+ const CONTINUATION = /[=+]\s*$/;
7
+ /** 把命中行与其后至多 3 行拼成完整语句(跳过 del 行;遇到语句收尾符或空行即停) */
8
+ function stitchFollowing(lines, start) {
9
+ const parts = [lines[start].content];
10
+ for (let i = start + 1; i < Math.min(lines.length, start + 4); i++) {
11
+ const content = lines[i].content;
12
+ if (lines[i].type === 'del')
13
+ continue;
14
+ parts.push(content);
15
+ if (/[;})]\s*$/.test(content) || content.trim() === '')
16
+ break;
17
+ }
18
+ return parts.join(' ').replace(/\s+/g, ' ');
19
+ }
20
+ /** 测试文件识别:tests/、__tests__/、spec/ 目录与 *.test.* / *.spec.* 后缀 */
21
+ export function isTestFile(path) {
22
+ const p = path.replace(/\\/g, '/');
23
+ return /(^|\/)(tests?|__tests__|spec)(\/|$)/i.test(p) || /\.(test|spec)\.[cm]?[jt]sx?$/i.test(p);
24
+ }
25
+ /** 命中行前后各至多 3 行的 add/context 文本(不含命中行),供需要上下文的规则使用 */
26
+ function contextWindow(lines, hit) {
27
+ const window = [];
28
+ for (let i = Math.max(0, hit - 3); i < hit; i++) {
29
+ if (lines[i].type !== 'del')
30
+ window.push(lines[i].content);
31
+ }
32
+ for (let i = hit + 1; i <= Math.min(lines.length - 1, hit + 3); i++) {
33
+ if (lines[i].type !== 'del')
34
+ window.push(lines[i].content);
35
+ }
36
+ return window;
37
+ }
38
+ /** 扫描整个 diff,返回全部告警(按文件、行遍历顺序) */
39
+ export function scanDiff(diff, options) {
40
+ const findings = [];
41
+ const disabled = new Set(options.disabledRules ?? []);
42
+ const rules = (options.rules ?? ALL_RULES).filter((rule) => !disabled.has(rule.id));
43
+ for (const file of diff.files) {
44
+ if (file.isBinary)
45
+ continue;
46
+ if (options.skipTests !== false && isTestFile(file.path))
47
+ continue;
48
+ if (matchGlob(file.path, options.ignore))
49
+ continue;
50
+ for (const hunk of file.hunks) {
51
+ for (let i = 0; i < hunk.lines.length; i++) {
52
+ const line = hunk.lines[i];
53
+ if (line.type !== 'add' || line.newLine === undefined)
54
+ continue;
55
+ if (line.content.includes(IGNORE_MARK))
56
+ continue; // 行内豁免
57
+ const content = CONTINUATION.test(line.content)
58
+ ? stitchFollowing(hunk.lines, i)
59
+ : line.content;
60
+ const ctx = {
61
+ file: file.path,
62
+ line: line.newLine,
63
+ content,
64
+ context: contextWindow(hunk.lines, i)
65
+ };
66
+ for (const rule of rules) {
67
+ if (!rule.detect(ctx))
68
+ continue;
69
+ findings.push({
70
+ ruleId: rule.id,
71
+ severity: rule.severity,
72
+ file: file.path,
73
+ line: line.newLine,
74
+ snippet: line.content,
75
+ message: rule.message,
76
+ fixHint: rule.fixHint,
77
+ contextLines: ctx.context
78
+ });
79
+ }
80
+ }
81
+ }
82
+ }
83
+ return findings;
84
+ }
85
+ //# sourceMappingURL=scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * 共享类型定义:告警(Finding)、LLM 审查请求与结果、Provider 接口。
3
+ * 设计约束:LLM 只负责「复核与解释」已有告警,永远不凭空发明发现——
4
+ * 每个 Finding 必须落到真实代码行上并有规则命中佐证(防幻觉根基)。
5
+ */
6
+ export type Severity = 'high' | 'medium' | 'low' | 'info';
7
+ /** 合法严重度清单(配置校验与 CLI 参数共用) */
8
+ export declare const SEVERITIES: readonly Severity[];
9
+ /** 严重度排序:info < low < medium < high(门禁判定与降级约束的统一基准) */
10
+ export declare const SEVERITY_RANK: Record<Severity, number>;
11
+ export interface Finding {
12
+ ruleId: string;
13
+ severity: Severity;
14
+ file: string;
15
+ line: number;
16
+ snippet: string;
17
+ message: string;
18
+ fixHint?: string;
19
+ /** 命中行周边的 add/context 行文本(供 LLM 复核理解语境) */
20
+ contextLines?: string[];
21
+ /** Week 2 起 LLM 复核结果挂载点,纯规则模式不填——LLM 只复核已有告警、不发明发现 */
22
+ review?: LLMReviewResult;
23
+ }
24
+ export interface LLMReviewRequest {
25
+ ruleId: string;
26
+ severity: Severity;
27
+ snippet: string;
28
+ language: string;
29
+ /** 命中行周边上下文行,辅助 LLM 判断 */
30
+ context?: string[];
31
+ }
32
+ export type LLMVerdict = 'confirmed' | 'false-positive' | 'unsure';
33
+ export interface LLMReviewResult {
34
+ verdict: LLMVerdict;
35
+ /** LLM 可建议修正严重度(只能下调或维持,不允许凭空升级) */
36
+ severity?: Severity;
37
+ explanation: string;
38
+ fixSuggestion?: string;
39
+ }
40
+ export interface LLMProvider {
41
+ readonly name: string;
42
+ review(request: LLMReviewRequest): Promise<LLMReviewResult>;
43
+ }
package/dist/types.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 共享类型定义:告警(Finding)、LLM 审查请求与结果、Provider 接口。
3
+ * 设计约束:LLM 只负责「复核与解释」已有告警,永远不凭空发明发现——
4
+ * 每个 Finding 必须落到真实代码行上并有规则命中佐证(防幻觉根基)。
5
+ */
6
+ /** 合法严重度清单(配置校验与 CLI 参数共用) */
7
+ export const SEVERITIES = ['high', 'medium', 'low', 'info'];
8
+ /** 严重度排序:info < low < medium < high(门禁判定与降级约束的统一基准) */
9
+ export const SEVERITY_RANK = { info: 0, low: 1, medium: 2, high: 3 };
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,8BAA8B;AAC9B,MAAM,CAAC,MAAM,UAAU,GAAwB,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;AAEjF,uDAAuD;AACvD,MAAM,CAAC,MAAM,aAAa,GAA6B,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "bounty-guard",
3
+ "version": "0.2.0",
4
+ "description": "AI 代码安全审查助手 —— 规则引擎初筛 + LLM 复核,守住每一行 diff",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "engines": {
8
+ "node": ">=20"
9
+ },
10
+ "bin": {
11
+ "bounty-guard": "./dist/index.js"
12
+ },
13
+ "homepage": "https://github.com/Croesus-K/bounty-guard#readme",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/Croesus-K/bounty-guard.git"
17
+ },
18
+ "bugs": {
19
+ "url": "https://github.com/Croesus-K/bounty-guard/issues"
20
+ },
21
+ "keywords": [
22
+ "security",
23
+ "code-review",
24
+ "static-analysis",
25
+ "llm",
26
+ "github-action"
27
+ ],
28
+ "scripts": {
29
+ "build": "tsc",
30
+ "dev": "tsx src/index.ts",
31
+ "lint": "tsc --noEmit",
32
+ "test": "vitest run",
33
+ "prepublishOnly": "npm run lint && npm test && npm run build"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "README.md",
38
+ "LICENSE"
39
+ ],
40
+ "dependencies": {
41
+ "commander": "^12.1.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^20.14.0",
45
+ "tsx": "^4.16.0",
46
+ "typescript": "^5.5.4",
47
+ "vitest": "^2.0.5"
48
+ }
49
+ }