ccbot-cli 2.0.0 → 2.1.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.
- package/LICENSE +21 -0
- package/bin/adapters/claude.js +150 -0
- package/bin/adapters/codex.js +439 -0
- package/bin/install.js +509 -349
- package/bin/lib/ccline.js +82 -0
- package/bin/lib/utils.js +87 -34
- package/bin/uninstall.js +48 -0
- package/config/AGENTS.md +630 -0
- package/config/CLAUDE.md +229 -20
- package/config/ccline/config.toml +161 -0
- package/config/codex-config.example.toml +22 -0
- package/config/settings.example.json +32 -0
- package/output-styles/abyss-cultivator.md +399 -0
- package/package.json +14 -5
- package/skills/SKILL.md +159 -0
- package/skills/domains/ai/SKILL.md +34 -0
- package/skills/domains/ai/agent-dev.md +242 -0
- package/skills/domains/ai/llm-security.md +288 -0
- package/skills/domains/ai/prompt-and-eval.md +279 -0
- package/skills/domains/ai/rag-system.md +542 -0
- package/skills/domains/architecture/SKILL.md +42 -0
- package/skills/domains/architecture/api-design.md +225 -0
- package/skills/domains/architecture/caching.md +299 -0
- package/skills/domains/architecture/cloud-native.md +285 -0
- package/skills/domains/architecture/message-queue.md +329 -0
- package/skills/domains/architecture/security-arch.md +297 -0
- package/skills/domains/data-engineering/SKILL.md +207 -0
- package/skills/domains/development/SKILL.md +46 -0
- package/skills/domains/development/cpp.md +246 -0
- package/skills/domains/development/go.md +323 -0
- package/skills/domains/development/java.md +277 -0
- package/skills/domains/development/python.md +288 -0
- package/skills/domains/development/rust.md +313 -0
- package/skills/domains/development/shell.md +313 -0
- package/skills/domains/development/typescript.md +277 -0
- package/skills/domains/devops/SKILL.md +39 -0
- package/skills/domains/devops/cost-optimization.md +272 -0
- package/skills/domains/devops/database.md +217 -0
- package/skills/domains/devops/devsecops.md +198 -0
- package/skills/domains/devops/git-workflow.md +181 -0
- package/skills/domains/devops/observability.md +280 -0
- package/skills/domains/devops/performance.md +336 -0
- package/skills/domains/devops/testing.md +283 -0
- package/skills/domains/frontend-design/SKILL.md +38 -0
- package/skills/domains/frontend-design/claymorphism/SKILL.md +119 -0
- package/skills/domains/frontend-design/claymorphism/references/tokens.css +52 -0
- package/skills/domains/frontend-design/component-patterns.md +202 -0
- package/skills/domains/frontend-design/engineering.md +287 -0
- package/skills/domains/frontend-design/glassmorphism/SKILL.md +140 -0
- package/skills/domains/frontend-design/glassmorphism/references/tokens.css +32 -0
- package/skills/domains/frontend-design/liquid-glass/SKILL.md +137 -0
- package/skills/domains/frontend-design/liquid-glass/references/tokens.css +81 -0
- package/skills/domains/frontend-design/neubrutalism/SKILL.md +143 -0
- package/skills/domains/frontend-design/neubrutalism/references/tokens.css +44 -0
- package/skills/domains/frontend-design/state-management.md +680 -0
- package/skills/domains/frontend-design/ui-aesthetics.md +110 -0
- package/skills/domains/frontend-design/ux-principles.md +156 -0
- package/skills/domains/infrastructure/SKILL.md +200 -0
- package/skills/domains/mobile/SKILL.md +224 -0
- package/skills/domains/orchestration/SKILL.md +29 -0
- package/skills/domains/orchestration/multi-agent.md +263 -0
- package/skills/domains/security/SKILL.md +54 -0
- package/skills/domains/security/blue-team.md +436 -0
- package/skills/domains/security/code-audit.md +265 -0
- package/skills/domains/security/pentest.md +226 -0
- package/skills/domains/security/red-team.md +375 -0
- package/skills/domains/security/threat-intel.md +372 -0
- package/skills/domains/security/vuln-research.md +369 -0
- package/skills/orchestration/multi-agent/SKILL.md +493 -0
- package/skills/run_skill.js +129 -0
- package/skills/tools/gen-docs/SKILL.md +116 -0
- package/skills/tools/gen-docs/scripts/doc_generator.js +435 -0
- package/skills/tools/lib/shared.js +98 -0
- package/skills/tools/verify-change/SKILL.md +140 -0
- package/skills/tools/verify-change/scripts/change_analyzer.js +289 -0
- package/skills/tools/verify-module/SKILL.md +127 -0
- package/skills/tools/verify-module/scripts/module_scanner.js +171 -0
- package/skills/tools/verify-quality/SKILL.md +160 -0
- package/skills/tools/verify-quality/scripts/quality_checker.js +337 -0
- package/skills/tools/verify-security/SKILL.md +143 -0
- package/skills/tools/verify-security/scripts/security_scanner.js +283 -0
- package/bin/lib/registry.js +0 -61
- package/config/.claudeignore +0 -11
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: verify-security
|
|
3
|
+
description: 安全校验关卡。自动扫描代码安全漏洞,检测危险模式,确保安全决策有文档记录。当魔尊提到安全扫描、漏洞检测、安全审计、代码安全、OWASP、注入检测、敏感信息泄露时使用。在新建模块、安全相关变更、攻防任务、重构完成时自动触发。
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: node>=18
|
|
6
|
+
user-invocable: true
|
|
7
|
+
disable-model-invocation: false
|
|
8
|
+
allowed-tools: Bash, Read, Grep
|
|
9
|
+
argument-hint: <扫描路径>
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# ⚖ 校验关卡 · 安全校验
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
## 核心原则
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
安全即道基,破则劫败
|
|
19
|
+
安全决策必须可追溯
|
|
20
|
+
Critical/High 问题必须修复后才能交付
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 自动扫描
|
|
24
|
+
|
|
25
|
+
运行安全扫描脚本(跨平台):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# 在 skill 目录下运行
|
|
29
|
+
node scripts/security_scanner.js <扫描路径>
|
|
30
|
+
node scripts/security_scanner.js <扫描路径> -v # 详细模式
|
|
31
|
+
node scripts/security_scanner.js <扫描路径> --json # JSON 输出
|
|
32
|
+
node scripts/security_scanner.js <扫描路径> --exclude vendor # 排除目录
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 检测范围
|
|
36
|
+
|
|
37
|
+
### 自动检测的漏洞类型
|
|
38
|
+
|
|
39
|
+
| 类别 | 检测项 | 严重度 |
|
|
40
|
+
|------|--------|--------|
|
|
41
|
+
| **注入** | SQL 注入、命令注入、代码注入 | 🔴 Critical |
|
|
42
|
+
| **敏感信息** | 硬编码密钥、AWS Key、私钥 | 🔴 Critical |
|
|
43
|
+
| **XSS** | innerHTML、dangerouslySetInnerHTML | 🟠 High |
|
|
44
|
+
| **反序列化** | pickle.loads、yaml.load | 🟠 High |
|
|
45
|
+
| **路径遍历** | 未验证的文件路径操作 | 🟠 High |
|
|
46
|
+
| **SSRF** | 未验证的 URL 请求 | 🟠 High |
|
|
47
|
+
| **XXE** | 不安全的 XML 解析 | 🟠 High |
|
|
48
|
+
| **弱加密** | MD5、SHA1 用于安全场景 | 🟡 Medium |
|
|
49
|
+
| **不安全随机** | random 模块用于安全场景 | 🟡 Medium |
|
|
50
|
+
| **调试代码** | console.log、print、debugger | 🔵 Low |
|
|
51
|
+
|
|
52
|
+
### 文档层面检查
|
|
53
|
+
|
|
54
|
+
安全相关代码必须在 DESIGN.md 中记录:
|
|
55
|
+
|
|
56
|
+
- [ ] **威胁模型** — 防御哪些攻击
|
|
57
|
+
- [ ] **安全决策** — 为何选择此方案
|
|
58
|
+
- [ ] **安全边界** — 信任边界在哪里
|
|
59
|
+
- [ ] **已知风险** — 接受了哪些风险
|
|
60
|
+
|
|
61
|
+
## 危险模式速查
|
|
62
|
+
|
|
63
|
+
### Python
|
|
64
|
+
```python
|
|
65
|
+
# 🔴 危险 - 触犯道基
|
|
66
|
+
eval(), exec(), os.system()
|
|
67
|
+
subprocess(..., shell=True)
|
|
68
|
+
pickle.loads(), yaml.load()
|
|
69
|
+
cursor.execute(f"SELECT * FROM t WHERE id = {id}")
|
|
70
|
+
|
|
71
|
+
# ✅ 安全替代 - 道基稳固
|
|
72
|
+
ast.literal_eval()
|
|
73
|
+
subprocess([...], shell=False)
|
|
74
|
+
yaml.safe_load()
|
|
75
|
+
cursor.execute("SELECT * FROM t WHERE id = %s", (id,))
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### JavaScript
|
|
79
|
+
```javascript
|
|
80
|
+
// 🔴 危险 - 触犯道基
|
|
81
|
+
eval(), innerHTML, document.write()
|
|
82
|
+
new Function(userInput)
|
|
83
|
+
|
|
84
|
+
// ✅ 安全替代 - 道基稳固
|
|
85
|
+
JSON.parse(), textContent
|
|
86
|
+
模板引擎自动转义
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Go
|
|
90
|
+
```go
|
|
91
|
+
// 🔴 危险 - 触犯道基
|
|
92
|
+
exec.Command("sh", "-c", userInput)
|
|
93
|
+
template.HTML(userInput)
|
|
94
|
+
|
|
95
|
+
// ✅ 安全替代 - 道基稳固
|
|
96
|
+
exec.Command("cmd", args...)
|
|
97
|
+
html/template 自动转义
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## 校验流程
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
1. 运行 security_scanner.js 自动扫描
|
|
104
|
+
2. 分析扫描结果,按严重度排序
|
|
105
|
+
3. 检查安全决策是否有文档记录
|
|
106
|
+
4. 输出安全校验报告
|
|
107
|
+
5. Critical/High 问题必须修复后才能交付
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## 自动触发时机
|
|
111
|
+
|
|
112
|
+
| 场景 | 触发条件 |
|
|
113
|
+
|------|----------|
|
|
114
|
+
| 新建模块 | 模块创建完成时 |
|
|
115
|
+
| 安全相关变更 | 涉及认证、授权、加密、输入处理 |
|
|
116
|
+
| 攻防任务 | 红队/蓝队任务完成时 |
|
|
117
|
+
| 重构完成 | 重构任务完成时 |
|
|
118
|
+
| 提交前 | 代码提交前检查 |
|
|
119
|
+
|
|
120
|
+
## 校验报告格式
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
## 安全校验报告
|
|
124
|
+
|
|
125
|
+
✓ 通过 | ✗ 未通过
|
|
126
|
+
|
|
127
|
+
- 🔴 Critical: N
|
|
128
|
+
- 🟠 High: N
|
|
129
|
+
- 🟡 Medium: N
|
|
130
|
+
- 🔵 Low: N
|
|
131
|
+
|
|
132
|
+
### 发现问题
|
|
133
|
+
|
|
134
|
+
| 文件 | 行号 | 类型 | 严重度 | 描述 |
|
|
135
|
+
|------|------|------|--------|------|
|
|
136
|
+
| ... | ... | ... | ... | ... |
|
|
137
|
+
|
|
138
|
+
### 结论
|
|
139
|
+
|
|
140
|
+
可交付 / 需修复后交付
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const SEVERITY_ORDER = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
|
|
8
|
+
|
|
9
|
+
// prettier-ignore
|
|
10
|
+
const SECURITY_RULES = [
|
|
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
|
+
},
|
|
154
|
+
];
|
|
155
|
+
|
|
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
|
+
];
|
|
164
|
+
|
|
165
|
+
function scanFile(filePath, rules) {
|
|
166
|
+
const findings = [];
|
|
167
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
168
|
+
let content;
|
|
169
|
+
try { content = fs.readFileSync(filePath, 'utf-8'); } catch { return findings; }
|
|
170
|
+
const lines = content.split('\n');
|
|
171
|
+
|
|
172
|
+
for (const rule of rules) {
|
|
173
|
+
const exts = rule.extensions;
|
|
174
|
+
if (!exts.includes('*') && !exts.includes(ext)) continue;
|
|
175
|
+
|
|
176
|
+
for (let i = 0; i < lines.length; i++) {
|
|
177
|
+
const line = lines[i];
|
|
178
|
+
const stripped = line.trim();
|
|
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;
|
|
185
|
+
|
|
186
|
+
if (rule.pattern.test(line)) {
|
|
187
|
+
rule.pattern.lastIndex = 0;
|
|
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
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return findings;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function walkDir(dir, excludeDirs) {
|
|
205
|
+
const results = [];
|
|
206
|
+
let entries;
|
|
207
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return results; }
|
|
208
|
+
for (const entry of entries) {
|
|
209
|
+
if (excludeDirs.includes(entry.name)) continue;
|
|
210
|
+
const full = path.join(dir, entry.name);
|
|
211
|
+
if (entry.isDirectory()) { results.push(...walkDir(full, excludeDirs)); }
|
|
212
|
+
else if (entry.isFile()) {
|
|
213
|
+
if (CODE_EXTENSIONS.has(path.extname(entry.name).toLowerCase())) {
|
|
214
|
+
results.push(full);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return results;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function scanDirectory(scanPath, excludeDirs) {
|
|
222
|
+
const resolved = path.resolve(scanPath);
|
|
223
|
+
const findings = [];
|
|
224
|
+
const files = walkDir(resolved, excludeDirs);
|
|
225
|
+
for (const f of files) findings.push(...scanFile(f, SECURITY_RULES));
|
|
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
|
+
);
|
|
231
|
+
return { scan_path: resolved, files_scanned: files.length, passed, findings };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const { buildReport, countBySeverity, parseCliArgs } = require(
|
|
235
|
+
path.join(__dirname, '..', '..', 'lib', 'shared.js')
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
function formatReport(result, verbose) {
|
|
239
|
+
const counts = countBySeverity(result.findings);
|
|
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
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
function main() {
|
|
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);
|
|
258
|
+
}
|
|
259
|
+
const scanPath = opts.target;
|
|
260
|
+
const verbose = opts.verbose;
|
|
261
|
+
const jsonOut = opts.json;
|
|
262
|
+
const excludeDirs = [...DEFAULT_EXCLUDES, ...opts.exclude];
|
|
263
|
+
const result = scanDirectory(scanPath, excludeDirs);
|
|
264
|
+
|
|
265
|
+
if (jsonOut) {
|
|
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));
|
|
273
|
+
} else {
|
|
274
|
+
console.log(formatReport(result, verbose));
|
|
275
|
+
}
|
|
276
|
+
process.exit(result.passed ? 0 : 1);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (require.main === module) {
|
|
280
|
+
main();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
module.exports = { scanFile, SECURITY_RULES };
|
package/bin/lib/registry.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* MCP Servers 预设注册表
|
|
5
|
-
* 每项包含: name, pkg (npm包名), desc, args (启动参数生成函数)
|
|
6
|
-
*/
|
|
7
|
-
const MCP_SERVERS = [
|
|
8
|
-
{
|
|
9
|
-
name: 'playwright',
|
|
10
|
-
desc: '浏览器自动化 (Playwright)',
|
|
11
|
-
args: () => ['npx', ['-y', '@playwright/mcp']],
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
name: 'filesystem',
|
|
15
|
-
desc: '文件系统访问',
|
|
16
|
-
args: (home) => ['npx', ['-y', '@modelcontextprotocol/server-filesystem', home]],
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
name: 'context7',
|
|
20
|
-
desc: '文档查询 (Context7)',
|
|
21
|
-
args: () => ['npx', ['-y', '@upstash/context7-mcp@latest']],
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
name: 'deepwiki',
|
|
25
|
-
desc: '深度Wiki知识库',
|
|
26
|
-
args: () => ['npx', ['-y', 'mcp-deepwiki']],
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
name: 'memory',
|
|
30
|
-
desc: '持久化记忆',
|
|
31
|
-
args: () => ['npx', ['-y', '@modelcontextprotocol/server-memory']],
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
name: 'sequential-thinking',
|
|
35
|
-
desc: '顺序推理',
|
|
36
|
-
args: () => ['npx', ['-y', '@modelcontextprotocol/server-sequential-thinking']],
|
|
37
|
-
},
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Skills / Plugins 预设注册表
|
|
42
|
-
*/
|
|
43
|
-
const SKILLS = [
|
|
44
|
-
{
|
|
45
|
-
name: 'superpowers',
|
|
46
|
-
desc: '超能力插件集 (brainstorming, TDD, debugging...)',
|
|
47
|
-
cmd: 'claude plugins install claude-plugins-official/superpowers',
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
name: 'spec-workflow',
|
|
51
|
-
desc: 'Spec 工作流 MCP',
|
|
52
|
-
cmd: 'claude plugins install nicobailon/spec-workflow-mcp-server',
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
name: 'skill-creator',
|
|
56
|
-
desc: 'Skill 创建器',
|
|
57
|
-
cmd: 'claude plugins install anthropic/skill-creator',
|
|
58
|
-
},
|
|
59
|
-
];
|
|
60
|
-
|
|
61
|
-
module.exports = { MCP_SERVERS, SKILLS };
|