code-abyss 1.7.0 → 1.7.2
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.
- package/README.md +8 -6
- package/bin/install.js +59 -163
- package/bin/lib/ccline.js +82 -0
- package/bin/lib/utils.js +61 -0
- package/package.json +5 -2
- package/skills/domains/ai/SKILL.md +2 -0
- package/skills/domains/architecture/SKILL.md +2 -0
- package/skills/domains/data-engineering/SKILL.md +2 -0
- package/skills/domains/development/SKILL.md +2 -0
- package/skills/domains/devops/SKILL.md +2 -0
- package/skills/domains/frontend-design/SKILL.md +1 -0
- package/skills/domains/frontend-design/claymorphism/SKILL.md +2 -0
- package/skills/domains/frontend-design/glassmorphism/SKILL.md +2 -0
- package/skills/domains/frontend-design/liquid-glass/SKILL.md +2 -0
- package/skills/domains/frontend-design/neubrutalism/SKILL.md +2 -0
- package/skills/domains/infrastructure/SKILL.md +2 -0
- package/skills/domains/mobile/SKILL.md +2 -0
- package/skills/domains/orchestration/SKILL.md +2 -1
- package/skills/domains/security/SKILL.md +2 -0
- package/skills/orchestration/multi-agent/SKILL.md +2 -0
- package/skills/run_skill.js +14 -9
- package/skills/tools/gen-docs/scripts/doc_generator.js +21 -7
- package/skills/tools/lib/shared.js +98 -0
- package/skills/tools/verify-change/scripts/change_analyzer.js +73 -54
- package/skills/tools/verify-module/scripts/module_scanner.js +63 -37
- package/skills/tools/verify-quality/scripts/quality_checker.js +134 -73
- package/skills/tools/verify-security/scripts/security_scanner.js +212 -62
|
@@ -6,28 +6,161 @@ const path = require('path');
|
|
|
6
6
|
|
|
7
7
|
const SEVERITY_ORDER = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
|
|
8
8
|
|
|
9
|
+
// prettier-ignore
|
|
9
10
|
const SECURITY_RULES = [
|
|
10
|
-
{
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
{
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
11
|
+
{
|
|
12
|
+
id: 'SQL_INJECTION_DYNAMIC', category: '注入',
|
|
13
|
+
severity: 'critical',
|
|
14
|
+
pattern: new RegExp(
|
|
15
|
+
'\\b(execute|query|raw)\\s*\\(\\s*' +
|
|
16
|
+
'(f["\']|["\'][^"\'\\n]*["\']\\s*\\+\\s*|["\'][^"\'\\n]*["\']\\s*%\\s*[^,)]|["\'][^"\'\\n]*["\']' +
|
|
17
|
+
'\\.format\\s*\\()', 'i'),
|
|
18
|
+
extensions: ['.py', '.js', '.ts', '.go', '.java', '.php'],
|
|
19
|
+
message: '可能存在 SQL 注入风险',
|
|
20
|
+
recommendation: '使用参数化查询或 ORM',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'SQL_INJECTION_FSTRING', category: '注入',
|
|
24
|
+
severity: 'critical',
|
|
25
|
+
pattern: /cursor\.(execute|executemany)\s*\(\s*f["']/i,
|
|
26
|
+
extensions: ['.py'],
|
|
27
|
+
message: '使用 f-string 构造 SQL 语句',
|
|
28
|
+
recommendation: '使用参数化查询',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 'COMMAND_INJECTION', category: '注入',
|
|
32
|
+
severity: 'critical',
|
|
33
|
+
pattern: /(os\.system|os\.popen|subprocess\.call|subprocess\.run|subprocess\.Popen)\s*\([^)]*shell\s*=\s*True/i,
|
|
34
|
+
extensions: ['.py'],
|
|
35
|
+
message: '使用 shell=True 可能导致命令注入',
|
|
36
|
+
recommendation: '避免 shell=True,使用列表参数',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'COMMAND_INJECTION_EVAL', category: '注入',
|
|
40
|
+
severity: 'critical',
|
|
41
|
+
pattern: /\b(eval|exec)\s*\([^)]*\b(input|request|argv|args)/i,
|
|
42
|
+
extensions: ['.py'],
|
|
43
|
+
message: 'eval/exec 执行用户输入',
|
|
44
|
+
recommendation: '避免对用户输入使用 eval/exec',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'HARDCODED_SECRET', category: '敏感信息',
|
|
48
|
+
severity: 'high',
|
|
49
|
+
pattern: /(?<!\w)(password|passwd|pwd|secret|api_key|apikey|token|auth_token)\s*=\s*["'][^"']{8,}["']/i,
|
|
50
|
+
excludePattern: /(example|placeholder|changeme|xxx|your[_-]|TODO|FIXME|<.*>|\*{3,})/i,
|
|
51
|
+
extensions: [
|
|
52
|
+
'.py', '.js', '.ts', '.go', '.java', '.php',
|
|
53
|
+
'.rb', '.yaml', '.yml', '.json', '.env',
|
|
54
|
+
],
|
|
55
|
+
message: '可能存在硬编码密钥/密码',
|
|
56
|
+
recommendation: '使用环境变量或密钥管理服务',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: 'HARDCODED_AWS_KEY', category: '敏感信息',
|
|
60
|
+
severity: 'critical',
|
|
61
|
+
pattern: /AKIA[0-9A-Z]{16}/,
|
|
62
|
+
extensions: ['*'],
|
|
63
|
+
message: '发现 AWS Access Key',
|
|
64
|
+
recommendation: '立即轮换密钥,使用 IAM 角色或环境变量',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: 'HARDCODED_PRIVATE_KEY', category: '敏感信息',
|
|
68
|
+
severity: 'critical',
|
|
69
|
+
pattern: /-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,
|
|
70
|
+
extensions: ['*'],
|
|
71
|
+
message: '发现私钥',
|
|
72
|
+
recommendation: '私钥不应提交到代码库',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: 'XSS_INNERHTML', category: 'XSS', severity: 'high',
|
|
76
|
+
pattern: /\.innerHTML\s*=|\.outerHTML\s*=|document\.write\s*\(/i,
|
|
77
|
+
extensions: ['.js', '.ts', '.jsx', '.tsx', '.html'],
|
|
78
|
+
message: '直接操作 innerHTML 可能导致 XSS',
|
|
79
|
+
recommendation: '使用 textContent 或框架的安全绑定',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: 'XSS_DANGEROUSLY', category: 'XSS',
|
|
83
|
+
severity: 'medium',
|
|
84
|
+
pattern: /dangerouslySetInnerHTML/i,
|
|
85
|
+
extensions: ['.js', '.ts', '.jsx', '.tsx'],
|
|
86
|
+
message: '使用 dangerouslySetInnerHTML',
|
|
87
|
+
recommendation: '确保内容已经过净化处理',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: 'UNSAFE_PICKLE', category: '反序列化',
|
|
91
|
+
severity: 'high',
|
|
92
|
+
pattern: /pickle\.loads?\s*\(|yaml\.load\s*\([^)]*Loader\s*=\s*yaml\.Loader/i,
|
|
93
|
+
extensions: ['.py'],
|
|
94
|
+
message: '不安全的反序列化',
|
|
95
|
+
recommendation: '使用 yaml.safe_load() 或验证数据来源',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: 'WEAK_CRYPTO_MD5', category: '加密',
|
|
99
|
+
severity: 'medium',
|
|
100
|
+
pattern: /\b(md5|MD5)\s*\(|hashlib\.md5\s*\(/i,
|
|
101
|
+
extensions: ['.py', '.js', '.ts', '.go', '.java', '.php'],
|
|
102
|
+
message: '使用弱哈希算法 MD5',
|
|
103
|
+
recommendation: '使用 bcrypt/argon2 或 SHA-256+',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: 'WEAK_CRYPTO_SHA1', category: '加密',
|
|
107
|
+
severity: 'low',
|
|
108
|
+
pattern: /\b(sha1|SHA1)\s*\(|hashlib\.sha1\s*\(/i,
|
|
109
|
+
extensions: ['.py', '.js', '.ts', '.go', '.java', '.php'],
|
|
110
|
+
message: '使用弱哈希算法 SHA1',
|
|
111
|
+
recommendation: '使用 SHA-256 或更强的算法',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: 'PATH_TRAVERSAL', category: '路径遍历',
|
|
115
|
+
severity: 'high',
|
|
116
|
+
pattern: new RegExp(
|
|
117
|
+
'(open|read|write|Path|os\\.path\\.join)\\s*\\([^\\n]*' +
|
|
118
|
+
'(request|input|argv|args|params|query|form|path_param)\\b', 'i'),
|
|
119
|
+
extensions: ['.py'],
|
|
120
|
+
message: '可能存在路径遍历风险',
|
|
121
|
+
recommendation: '验证并规范化用户输入的路径',
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: 'SSRF', category: 'SSRF', severity: 'high',
|
|
125
|
+
pattern: new RegExp(
|
|
126
|
+
'(requests\\.(get|post|put|delete|head)|urllib\\.request\\.urlopen)' +
|
|
127
|
+
'\\s*\\([^\\n]*(request|input|argv|args|params|query|url)\\b', 'i'),
|
|
128
|
+
extensions: ['.py'],
|
|
129
|
+
message: '可能存在 SSRF 风险',
|
|
130
|
+
recommendation: '验证并限制目标 URL',
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: 'DEBUG_CODE', category: '调试', severity: 'low',
|
|
134
|
+
pattern: /\b(console\.log|debugger|pdb\.set_trace|breakpoint)\s*\(/i,
|
|
135
|
+
extensions: ['.py', '.js', '.ts'],
|
|
136
|
+
message: '发现调试代码',
|
|
137
|
+
recommendation: '生产环境移除调试代码',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: 'INSECURE_RANDOM', category: '加密',
|
|
141
|
+
severity: 'medium',
|
|
142
|
+
pattern: /\brandom\.(random|randint|choice|shuffle)\s*\(/i,
|
|
143
|
+
extensions: ['.py'],
|
|
144
|
+
message: '使用不安全的随机数生成器',
|
|
145
|
+
recommendation: '安全场景使用 secrets 模块',
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: 'XXE', category: 'XXE', severity: 'high',
|
|
149
|
+
pattern: /etree\.(parse|fromstring)\s*\([^)]*\)|xml\.dom\.minidom\.parse/i,
|
|
150
|
+
extensions: ['.py'],
|
|
151
|
+
message: 'XML 解析可能存在 XXE 风险',
|
|
152
|
+
recommendation: '禁用外部实体: XMLParser(resolve_entities=False)',
|
|
153
|
+
},
|
|
27
154
|
];
|
|
28
155
|
|
|
29
|
-
const CODE_EXTENSIONS = new Set([
|
|
30
|
-
|
|
156
|
+
const CODE_EXTENSIONS = new Set([
|
|
157
|
+
'.py', '.js', '.ts', '.jsx', '.tsx', '.go',
|
|
158
|
+
'.java', '.php', '.rb', '.yaml', '.yml', '.json',
|
|
159
|
+
]);
|
|
160
|
+
const DEFAULT_EXCLUDES = [
|
|
161
|
+
'.git', 'node_modules', '__pycache__', '.venv', 'venv',
|
|
162
|
+
'dist', 'build', '.tox', 'tests', 'test', '__tests__', 'spec',
|
|
163
|
+
];
|
|
31
164
|
|
|
32
165
|
function scanFile(filePath, rules) {
|
|
33
166
|
const findings = [];
|
|
@@ -43,12 +176,25 @@ function scanFile(filePath, rules) {
|
|
|
43
176
|
for (let i = 0; i < lines.length; i++) {
|
|
44
177
|
const line = lines[i];
|
|
45
178
|
const stripped = line.trim();
|
|
46
|
-
|
|
179
|
+
const isComment = stripped.startsWith('#') ||
|
|
180
|
+
stripped.startsWith('//') || stripped.startsWith('*') ||
|
|
181
|
+
stripped.startsWith('/*');
|
|
182
|
+
if (isComment) continue;
|
|
183
|
+
const ruleDefRe = /^\s*(id|pattern|severity|message|recommendation|extensions|excludePattern|category)\s*:/;
|
|
184
|
+
if (ruleDefRe.test(stripped)) continue;
|
|
47
185
|
|
|
48
186
|
if (rule.pattern.test(line)) {
|
|
49
187
|
rule.pattern.lastIndex = 0;
|
|
50
|
-
if (rule.excludePattern && rule.excludePattern.test(line)) {
|
|
51
|
-
|
|
188
|
+
if (rule.excludePattern && rule.excludePattern.test(line)) {
|
|
189
|
+
rule.excludePattern.lastIndex = 0; continue;
|
|
190
|
+
}
|
|
191
|
+
findings.push({
|
|
192
|
+
severity: rule.severity, category: rule.category,
|
|
193
|
+
message: rule.message, file_path: filePath,
|
|
194
|
+
line_number: i + 1,
|
|
195
|
+
line_content: stripped.slice(0, 100),
|
|
196
|
+
recommendation: rule.recommendation,
|
|
197
|
+
});
|
|
52
198
|
}
|
|
53
199
|
}
|
|
54
200
|
}
|
|
@@ -63,7 +209,11 @@ function walkDir(dir, excludeDirs) {
|
|
|
63
209
|
if (excludeDirs.includes(entry.name)) continue;
|
|
64
210
|
const full = path.join(dir, entry.name);
|
|
65
211
|
if (entry.isDirectory()) { results.push(...walkDir(full, excludeDirs)); }
|
|
66
|
-
else if (entry.isFile()
|
|
212
|
+
else if (entry.isFile()) {
|
|
213
|
+
if (CODE_EXTENSIONS.has(path.extname(entry.name).toLowerCase())) {
|
|
214
|
+
results.push(full);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
67
217
|
}
|
|
68
218
|
return results;
|
|
69
219
|
}
|
|
@@ -73,61 +223,61 @@ function scanDirectory(scanPath, excludeDirs) {
|
|
|
73
223
|
const findings = [];
|
|
74
224
|
const files = walkDir(resolved, excludeDirs);
|
|
75
225
|
for (const f of files) findings.push(...scanFile(f, SECURITY_RULES));
|
|
76
|
-
findings.sort((a, b) =>
|
|
77
|
-
|
|
226
|
+
findings.sort((a, b) =>
|
|
227
|
+
(SEVERITY_ORDER[a.severity] ?? 9) - (SEVERITY_ORDER[b.severity] ?? 9));
|
|
228
|
+
const passed = !findings.some(
|
|
229
|
+
f => f.severity === 'critical' || f.severity === 'high'
|
|
230
|
+
);
|
|
78
231
|
return { scan_path: resolved, files_scanned: files.length, passed, findings };
|
|
79
232
|
}
|
|
80
233
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return counts;
|
|
85
|
-
}
|
|
234
|
+
const { buildReport, countBySeverity, parseCliArgs } = require(
|
|
235
|
+
path.join(__dirname, '..', '..', 'lib', 'shared.js')
|
|
236
|
+
);
|
|
86
237
|
|
|
87
238
|
function formatReport(result, verbose) {
|
|
88
239
|
const counts = countBySeverity(result.findings);
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
'
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
lines.push(`\n${icons[f.severity] || ''} [${f.severity.toUpperCase()}] ${f.category}`);
|
|
100
|
-
lines.push(` 文件: ${f.file_path}:${f.line_number}`);
|
|
101
|
-
lines.push(` 问题: ${f.message}`);
|
|
102
|
-
if (verbose) lines.push(` 代码: ${f.line_content}`);
|
|
103
|
-
lines.push(` 建议: ${f.recommendation}`);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
lines.push('\n' + '='.repeat(60));
|
|
107
|
-
return lines.join('\n');
|
|
240
|
+
const fields = {
|
|
241
|
+
'扫描路径': result.scan_path,
|
|
242
|
+
'扫描文件': result.files_scanned,
|
|
243
|
+
'扫描结果': result.passed ? '\u2713 通过' : '\u2717 发现高危问题',
|
|
244
|
+
'统计': `严重: ${counts.critical || 0} | 高危: ${counts.high || 0}` +
|
|
245
|
+
` | 中危: ${counts.medium || 0} | 低危: ${counts.low || 0}`,
|
|
246
|
+
};
|
|
247
|
+
return buildReport(
|
|
248
|
+
'代码安全扫描报告', fields, result.findings, verbose, 'category'
|
|
249
|
+
);
|
|
108
250
|
}
|
|
109
251
|
|
|
252
|
+
|
|
110
253
|
function main() {
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (args[i] === '-v' || args[i] === '--verbose') verbose = true;
|
|
116
|
-
else if (args[i] === '--json') jsonOut = true;
|
|
117
|
-
else if (args[i] === '--exclude') { while (i + 1 < args.length && !args[i + 1].startsWith('-')) extraExcludes.push(args[++i]); }
|
|
118
|
-
else if (args[i] === '--help' || args[i] === '-h') { console.log('Usage: security_scanner.js [path] [-v] [--json] [--exclude dir1 dir2]'); process.exit(0); }
|
|
119
|
-
else if (!args[i].startsWith('-')) scanPath = args[i];
|
|
254
|
+
const opts = parseCliArgs(process.argv, { exclude: [] });
|
|
255
|
+
if (opts.help) {
|
|
256
|
+
console.log('Usage: security_scanner.js [path] [-v] [--json] [--exclude dir1 dir2]');
|
|
257
|
+
process.exit(0);
|
|
120
258
|
}
|
|
121
|
-
|
|
122
|
-
const
|
|
259
|
+
const scanPath = opts.target;
|
|
260
|
+
const verbose = opts.verbose;
|
|
261
|
+
const jsonOut = opts.json;
|
|
262
|
+
const excludeDirs = [...DEFAULT_EXCLUDES, ...opts.exclude];
|
|
123
263
|
const result = scanDirectory(scanPath, excludeDirs);
|
|
124
264
|
|
|
125
265
|
if (jsonOut) {
|
|
126
|
-
console.log(JSON.stringify({
|
|
266
|
+
console.log(JSON.stringify({
|
|
267
|
+
scan_path: result.scan_path,
|
|
268
|
+
files_scanned: result.files_scanned,
|
|
269
|
+
passed: result.passed,
|
|
270
|
+
counts: countBySeverity(result.findings),
|
|
271
|
+
findings: result.findings,
|
|
272
|
+
}, null, 2));
|
|
127
273
|
} else {
|
|
128
274
|
console.log(formatReport(result, verbose));
|
|
129
275
|
}
|
|
130
276
|
process.exit(result.passed ? 0 : 1);
|
|
131
277
|
}
|
|
132
278
|
|
|
133
|
-
main
|
|
279
|
+
if (require.main === module) {
|
|
280
|
+
main();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
module.exports = { scanFile, SECURITY_RULES };
|