@blueking/bkui-knowledge 0.0.1-beta.1 → 0.0.1-beta.11
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 +166 -58
- package/bin/bkui-knowledge.js +229 -86
- package/knowledge/manifest.json +38 -1
- package/knowledge/skills/.template/README.md +1 -1
- package/knowledge/skills/bk-security-redlines/SKILL.md +47 -0
- package/knowledge/skills/bk-security-redlines/references/auth-check.md +73 -0
- package/knowledge/skills/bk-security-redlines/references/data-encryption.md +78 -0
- package/knowledge/skills/bk-security-redlines/references/input-validation.md +96 -0
- package/knowledge/skills/bk-skill-creator/SKILL.md +37 -0
- package/knowledge/skills/bk-skill-creator/references/common-mistakes.md +43 -0
- package/knowledge/skills/bk-skill-creator/references/quick-start.md +42 -0
- package/knowledge/skills/bk-skill-creator/references/skill-checklist.md +93 -0
- package/knowledge/skills/bk-skill-creator/references/structure-guide.md +88 -0
- package/knowledge/skills/bk-skill-creator/references/writing-tips.md +153 -0
- package/knowledge/skills/bkui-quick-start/SKILL.md +52 -0
- package/knowledge/skills/bkui-quick-start/references/components-list.md +17 -0
- package/knowledge/skills/bkui-quick-start/references/skills-index.md +26 -0
- package/knowledge/skills/external/vue-skills/LICENSE +21 -0
- package/knowledge/skills/external/vue-skills/README.md +69 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/SKILL.md +42 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/codeactions-save-performance.md +79 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/data-attributes-config.md +74 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/deep-watch-numeric.md +102 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/define-model-update-event.md +79 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/duplicate-plugin-detection.md +102 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/fallthrough-attributes.md +63 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/hmr-vue-ssr.md +124 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/module-resolution-bundler.md +81 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/pinia-store-mocking.md +159 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/script-setup-jsdoc.md +85 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/strict-css-modules.md +68 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/unplugin-auto-import-conflicts.md +97 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/volar-3-breaking-changes.md +66 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/vue-directive-comments.md +73 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/vue-router-typed-params.md +81 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/vue-tsc-strict-templates.md +69 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/with-defaults-union-types.md +102 -0
- package/knowledge/skills/web-security-guide/SKILL.md +48 -0
- package/knowledge/skills/web-security-guide/references/access-control.md +123 -0
- package/knowledge/skills/web-security-guide/references/auth-session.md +99 -0
- package/knowledge/skills/web-security-guide/references/csrf.md +59 -0
- package/knowledge/skills/web-security-guide/references/data-exposure.md +108 -0
- package/knowledge/skills/web-security-guide/references/deserialization.md +59 -0
- package/knowledge/skills/web-security-guide/references/injection.md +357 -0
- package/knowledge/skills/web-security-guide/references/logging-monitoring.md +47 -0
- package/knowledge/skills/web-security-guide/references/security-config.md +73 -0
- package/knowledge/skills/web-security-guide/references/ssrf.md +55 -0
- package/knowledge/skills/web-security-guide/references/xss.md +134 -0
- package/package.json +3 -3
- package/server/mcp-core.js +48 -33
package/bin/bkui-knowledge.js
CHANGED
|
@@ -7,15 +7,43 @@
|
|
|
7
7
|
* 2. 启动 MCP stdio 服务
|
|
8
8
|
*
|
|
9
9
|
* 用法(用户只需配置一次):
|
|
10
|
+
*
|
|
11
|
+
* Cursor(默认):
|
|
10
12
|
* {
|
|
11
13
|
* "mcpServers": {
|
|
12
14
|
* "bkui-knowledge": {
|
|
13
15
|
* "command": "npx",
|
|
14
|
-
* "args": ["-y", "@blueking/bkui-knowledge"]
|
|
16
|
+
* "args": ["-y", "@blueking/bkui-knowledge", "${workspaceFolder}"]
|
|
15
17
|
* }
|
|
16
18
|
* }
|
|
17
19
|
* }
|
|
18
20
|
*
|
|
21
|
+
* CodeBuddy:
|
|
22
|
+
* {
|
|
23
|
+
* "bkui-knowledge": {
|
|
24
|
+
* "command": "npx",
|
|
25
|
+
* "args": ["-y", "@blueking/bkui-knowledge", "--ide=codebuddy"]
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* Claude Code(无需额外配置,自动从 MCP initialize 请求获取项目路径):
|
|
30
|
+
* {
|
|
31
|
+
* "bkui-knowledge": {
|
|
32
|
+
* "command": "npx",
|
|
33
|
+
* "args": ["-y", "@blueking/bkui-knowledge", "--ide=claude-code"]
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
*
|
|
37
|
+
* 参数说明:
|
|
38
|
+
* - 第一个非 -- 开头的参数: 项目路径
|
|
39
|
+
* - --ide=cursor|codebuddy|claude-code: 指定目标 IDE(默认 cursor)
|
|
40
|
+
*
|
|
41
|
+
* 项目路径传递方式(优先级从高到低):
|
|
42
|
+
* 1. 命令行参数: npx @blueking/bkui-knowledge /path/to/project
|
|
43
|
+
* 2. 环境变量: BKUI_PROJECT_ROOT=/path/to/project
|
|
44
|
+
* 3. MCP initialize 请求中的 rootUri(Claude Code 等支持此协议的客户端)
|
|
45
|
+
* 4. 当前工作目录 (cwd)
|
|
46
|
+
*
|
|
19
47
|
* 更新机制:
|
|
20
48
|
* - skills 内置在 npm 包中
|
|
21
49
|
* - 更新 skills 时:修改代码 → npm publish
|
|
@@ -25,14 +53,33 @@
|
|
|
25
53
|
const fs = require('fs');
|
|
26
54
|
const path = require('path');
|
|
27
55
|
|
|
28
|
-
//
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
56
|
+
// IDE 配置映射
|
|
57
|
+
const IDE_CONFIGS = {
|
|
58
|
+
cursor: {
|
|
59
|
+
name: 'Cursor',
|
|
60
|
+
configDir: '.cursor',
|
|
61
|
+
skillsDir: '.cursor/skills',
|
|
62
|
+
versionFile: '.cursor/.bkui-knowledge-version'
|
|
63
|
+
},
|
|
64
|
+
codebuddy: {
|
|
65
|
+
name: 'CodeBuddy',
|
|
66
|
+
configDir: '.codebuddy',
|
|
67
|
+
skillsDir: '.codebuddy/skills',
|
|
68
|
+
versionFile: '.codebuddy/.bkui-knowledge-version'
|
|
69
|
+
},
|
|
70
|
+
'claude-code': {
|
|
71
|
+
name: 'Claude Code',
|
|
72
|
+
configDir: '.claude',
|
|
73
|
+
skillsDir: '.claude/skills',
|
|
74
|
+
versionFile: '.claude/.bkui-knowledge-version'
|
|
75
|
+
}
|
|
34
76
|
};
|
|
35
77
|
|
|
78
|
+
// 用于存储从 MCP initialize 请求获取的项目路径
|
|
79
|
+
let mcpRootUri = null;
|
|
80
|
+
// 标记是否已经同步过 skills
|
|
81
|
+
let skillsSynced = false;
|
|
82
|
+
|
|
36
83
|
// npm 包根目录
|
|
37
84
|
const PKG_ROOT = path.join(__dirname, '..');
|
|
38
85
|
|
|
@@ -42,12 +89,72 @@ function log(msg) {
|
|
|
42
89
|
}
|
|
43
90
|
|
|
44
91
|
/**
|
|
45
|
-
*
|
|
92
|
+
* 解析 --ide 参数,获取目标 IDE
|
|
93
|
+
* @returns {string} IDE 标识符 (cursor | codebuddy)
|
|
94
|
+
*/
|
|
95
|
+
function getTargetIDE() {
|
|
96
|
+
const ideArg = process.argv.find(arg => arg.startsWith('--ide='));
|
|
97
|
+
if (ideArg) {
|
|
98
|
+
const ide = ideArg.split('=')[1].toLowerCase();
|
|
99
|
+
if (IDE_CONFIGS[ide]) {
|
|
100
|
+
return ide;
|
|
101
|
+
}
|
|
102
|
+
log(`警告: 未知 IDE "${ide}",使用默认值 cursor`);
|
|
103
|
+
}
|
|
104
|
+
return 'cursor'; // 默认使用 Cursor
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 获取当前 IDE 配置
|
|
109
|
+
* @returns {object} IDE 配置对象
|
|
110
|
+
*/
|
|
111
|
+
function getIDEConfig() {
|
|
112
|
+
const ide = getTargetIDE();
|
|
113
|
+
return IDE_CONFIGS[ide];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 获取用户项目根目录
|
|
118
|
+
* 优先级:命令行参数 > 环境变量 > MCP rootUri > cwd
|
|
46
119
|
*/
|
|
47
120
|
function getProjectRoot() {
|
|
121
|
+
// 1. 命令行参数: node bkui-knowledge.js /path/to/project (过滤掉 -- 开头的参数)
|
|
122
|
+
const pathArg = process.argv.slice(2).find(arg => !arg.startsWith('-'));
|
|
123
|
+
if (pathArg) {
|
|
124
|
+
return pathArg;
|
|
125
|
+
}
|
|
126
|
+
// 2. 环境变量: BKUI_PROJECT_ROOT=/path/to/project
|
|
127
|
+
if (process.env.BKUI_PROJECT_ROOT) {
|
|
128
|
+
return process.env.BKUI_PROJECT_ROOT;
|
|
129
|
+
}
|
|
130
|
+
// 3. MCP initialize 请求中的 rootUri
|
|
131
|
+
if (mcpRootUri) {
|
|
132
|
+
return mcpRootUri;
|
|
133
|
+
}
|
|
134
|
+
// 4. 默认使用 cwd
|
|
48
135
|
return process.cwd();
|
|
49
136
|
}
|
|
50
137
|
|
|
138
|
+
/**
|
|
139
|
+
* 从 file:// URI 提取路径
|
|
140
|
+
* @param {string} uri - file:// URI
|
|
141
|
+
* @returns {string} 文件系统路径
|
|
142
|
+
*/
|
|
143
|
+
function uriToPath(uri) {
|
|
144
|
+
if (!uri) return null;
|
|
145
|
+
if (uri.startsWith('file://')) {
|
|
146
|
+
// file:///path/to/dir -> /path/to/dir
|
|
147
|
+
let filePath = uri.slice(7);
|
|
148
|
+
// Windows: file:///C:/path -> C:/path
|
|
149
|
+
if (filePath.match(/^\/[A-Za-z]:\//)) {
|
|
150
|
+
filePath = filePath.slice(1);
|
|
151
|
+
}
|
|
152
|
+
return decodeURIComponent(filePath);
|
|
153
|
+
}
|
|
154
|
+
// 如果不是 file:// URI,直接返回(可能已经是路径)
|
|
155
|
+
return uri;
|
|
156
|
+
}
|
|
157
|
+
|
|
51
158
|
/**
|
|
52
159
|
* 获取当前包版本
|
|
53
160
|
*/
|
|
@@ -62,9 +169,12 @@ function getPackageVersion() {
|
|
|
62
169
|
|
|
63
170
|
/**
|
|
64
171
|
* 检查是否需要同步
|
|
172
|
+
* @param {string} projectRoot - 项目根目录
|
|
173
|
+
* @param {string} currentVersion - 当前版本
|
|
174
|
+
* @param {object} ideConfig - IDE 配置
|
|
65
175
|
*/
|
|
66
|
-
function needsSync(projectRoot, currentVersion) {
|
|
67
|
-
const versionFile = path.join(projectRoot,
|
|
176
|
+
function needsSync(projectRoot, currentVersion, ideConfig) {
|
|
177
|
+
const versionFile = path.join(projectRoot, ideConfig.versionFile);
|
|
68
178
|
if (!fs.existsSync(versionFile)) {
|
|
69
179
|
return true;
|
|
70
180
|
}
|
|
@@ -73,70 +183,63 @@ function needsSync(projectRoot, currentVersion) {
|
|
|
73
183
|
}
|
|
74
184
|
|
|
75
185
|
/**
|
|
76
|
-
*
|
|
186
|
+
* 递归扫描目录,返回相对路径列表
|
|
187
|
+
* @param {string} dirPath - 目录路径
|
|
188
|
+
* @param {string} relativePath - 相对路径前缀
|
|
189
|
+
* @returns {string[]} 文件相对路径列表
|
|
77
190
|
*/
|
|
78
|
-
function
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
推荐的 skills 已自动同步到 \`.cursor/skills/\` 目录:
|
|
99
|
-
${recommendedSkills.map(id => `- ${id}`).join('\n')}
|
|
100
|
-
|
|
101
|
-
## 可用资源索引
|
|
102
|
-
|
|
103
|
-
### Skills (调用 get_skill 获取详情)
|
|
104
|
-
| ID | 名称 |
|
|
105
|
-
|----|------|
|
|
106
|
-
${skillList}
|
|
107
|
-
|
|
108
|
-
> 💡 bkui-builder 包含布局模版和页面模版 (assets/)
|
|
109
|
-
|
|
110
|
-
### Component APIs (调用 get_component_api 获取文档)
|
|
111
|
-
⚠️ **高优先级 (必须先查询):** ${apiPriority}
|
|
112
|
-
|
|
113
|
-
全部组件: ${allApis}
|
|
114
|
-
|
|
115
|
-
## 工作流程
|
|
116
|
-
1. 分析需求 → 确定需要哪些资源
|
|
117
|
-
2. 使用布局组件? → get_component_api({ componentName: 'navigation' })
|
|
118
|
-
3. 需要模板代码? → get_skill({ skillId: 'bkui-builder' })
|
|
119
|
-
4. 批量获取? → batch_load({ skillIds: [...], componentNames: [...] })
|
|
120
|
-
|
|
121
|
-
## 强制规范
|
|
122
|
-
- 组件库: bkui-vue (前缀 bk-)
|
|
123
|
-
- 语法: Vue 3 <script setup lang="ts">
|
|
124
|
-
- 布局: 必须使用 bk-navigation
|
|
191
|
+
function scanSkillDir(dirPath, relativePath) {
|
|
192
|
+
const results = [];
|
|
193
|
+
try {
|
|
194
|
+
const items = fs.readdirSync(dirPath);
|
|
195
|
+
for (const item of items) {
|
|
196
|
+
const fullPath = path.join(dirPath, item);
|
|
197
|
+
const relPath = relativePath ? `${relativePath}/${item}` : item;
|
|
198
|
+
const stat = fs.statSync(fullPath);
|
|
199
|
+
if (stat.isDirectory()) {
|
|
200
|
+
results.push(...scanSkillDir(fullPath, relPath));
|
|
201
|
+
} else {
|
|
202
|
+
results.push(relPath);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
} catch (e) {
|
|
206
|
+
// 忽略错误
|
|
207
|
+
}
|
|
208
|
+
return results;
|
|
209
|
+
}
|
|
125
210
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
211
|
+
/**
|
|
212
|
+
* 生成 skill 的可用资源列表
|
|
213
|
+
* @param {string} skillDir - skill 目录路径
|
|
214
|
+
* @param {string} skillId - skill ID
|
|
215
|
+
* @returns {string} 资源列表 Markdown
|
|
216
|
+
*/
|
|
217
|
+
function generateResourcesList(skillDir, skillId) {
|
|
218
|
+
const subDirs = ['scripts', 'references', 'assets', 'rules'];
|
|
219
|
+
const resources = [];
|
|
220
|
+
|
|
221
|
+
subDirs.forEach(dir => {
|
|
222
|
+
const dirPath = path.join(skillDir, dir);
|
|
223
|
+
if (fs.existsSync(dirPath)) {
|
|
224
|
+
const files = scanSkillDir(dirPath, '');
|
|
225
|
+
files.forEach(file => {
|
|
226
|
+
resources.push(`- \`skill://${skillId}/${dir}/${file}\``);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
});
|
|
131
230
|
|
|
132
|
-
|
|
133
|
-
|
|
231
|
+
if (resources.length > 0) {
|
|
232
|
+
return `\n\n---\n## 📦 可用资源\n\n${resources.join('\n')}\n\n> 根据 SKILL.md 中的 IF-THEN 规则判断是否需要加载\n`;
|
|
233
|
+
}
|
|
234
|
+
return '';
|
|
134
235
|
}
|
|
135
236
|
|
|
136
237
|
/**
|
|
137
238
|
* 同步 skills 到用户项目(从 npm 包内置的 knowledge 目录)
|
|
239
|
+
* @param {string} projectRoot - 项目根目录
|
|
240
|
+
* @param {object} ideConfig - IDE 配置
|
|
138
241
|
*/
|
|
139
|
-
function syncSkills(projectRoot) {
|
|
242
|
+
function syncSkills(projectRoot, ideConfig) {
|
|
140
243
|
try {
|
|
141
244
|
const manifestPath = path.join(PKG_ROOT, 'knowledge/manifest.json');
|
|
142
245
|
|
|
@@ -151,18 +254,16 @@ function syncSkills(projectRoot) {
|
|
|
151
254
|
const version = `${pkgVersion}-${manifest.updated_at || 'unknown'}`;
|
|
152
255
|
|
|
153
256
|
// 检查是否需要同步
|
|
154
|
-
if (!needsSync(projectRoot, version)) {
|
|
155
|
-
log(`skills 已是最新版本 (v${pkgVersion})`);
|
|
257
|
+
if (!needsSync(projectRoot, version, ideConfig)) {
|
|
258
|
+
log(`[${ideConfig.name}] skills 已是最新版本 (v${pkgVersion})`);
|
|
156
259
|
return;
|
|
157
260
|
}
|
|
158
261
|
|
|
159
|
-
log(
|
|
262
|
+
log(`[${ideConfig.name}] 同步 skills (v${pkgVersion})...`);
|
|
160
263
|
|
|
161
264
|
// 创建目录
|
|
162
|
-
const skillsDir = path.join(projectRoot,
|
|
163
|
-
const commandsDir = path.join(projectRoot, CONFIG.commandsDir);
|
|
265
|
+
const skillsDir = path.join(projectRoot, ideConfig.skillsDir);
|
|
164
266
|
fs.mkdirSync(skillsDir, { recursive: true });
|
|
165
|
-
fs.mkdirSync(commandsDir, { recursive: true });
|
|
166
267
|
|
|
167
268
|
// 同步推荐的 skills
|
|
168
269
|
let count = 0;
|
|
@@ -173,25 +274,28 @@ function syncSkills(projectRoot) {
|
|
|
173
274
|
const skillFilePath = path.join(PKG_ROOT, 'knowledge', manifest.skills.base_path, skill.path);
|
|
174
275
|
if (!fs.existsSync(skillFilePath)) continue;
|
|
175
276
|
|
|
176
|
-
|
|
277
|
+
// 读取原始内容
|
|
278
|
+
let content = fs.readFileSync(skillFilePath, 'utf-8');
|
|
279
|
+
|
|
280
|
+
// 生成并附加资源列表(与 mcp-core.js 的 get_skill 逻辑一致)
|
|
281
|
+
const skillDir = path.dirname(skillFilePath);
|
|
282
|
+
const resourcesList = generateResourcesList(skillDir, skillId);
|
|
283
|
+
content += resourcesList;
|
|
284
|
+
|
|
177
285
|
fs.writeFileSync(path.join(skillsDir, `${skillId}.md`), content, 'utf-8');
|
|
178
|
-
fs.writeFileSync(path.join(commandsDir, `${skillId}.md`), `请加载使用 ${skillId} skill`, 'utf-8');
|
|
179
286
|
|
|
180
287
|
log(` ✓ ${skillId}`);
|
|
181
288
|
count++;
|
|
182
289
|
}
|
|
183
290
|
|
|
184
|
-
// 生成 Cursor Rules 文件
|
|
185
|
-
generateRules(projectRoot, manifest);
|
|
186
|
-
|
|
187
291
|
// 保存版本信息
|
|
188
|
-
fs.mkdirSync(path.dirname(path.join(projectRoot,
|
|
189
|
-
fs.writeFileSync(path.join(projectRoot,
|
|
292
|
+
fs.mkdirSync(path.dirname(path.join(projectRoot, ideConfig.versionFile)), { recursive: true });
|
|
293
|
+
fs.writeFileSync(path.join(projectRoot, ideConfig.versionFile), version, 'utf-8');
|
|
190
294
|
|
|
191
|
-
log(
|
|
295
|
+
log(`[${ideConfig.name}] 同步完成!共 ${count} 个 skills`);
|
|
192
296
|
|
|
193
297
|
} catch (error) {
|
|
194
|
-
log(
|
|
298
|
+
log(`[${ideConfig.name}] 同步失败: ${error.message}(不影响 MCP 服务)`);
|
|
195
299
|
}
|
|
196
300
|
}
|
|
197
301
|
|
|
@@ -208,6 +312,27 @@ async function startMcpServer() {
|
|
|
208
312
|
|
|
209
313
|
async function handleMessage(message) {
|
|
210
314
|
if (message.method === "initialize") {
|
|
315
|
+
// 从 initialize 请求中提取项目路径
|
|
316
|
+
const params = message.params || {};
|
|
317
|
+
|
|
318
|
+
// 尝试从 rootUri 或 workspaceFolders 获取项目路径
|
|
319
|
+
if (params.rootUri) {
|
|
320
|
+
mcpRootUri = uriToPath(params.rootUri);
|
|
321
|
+
log(`从 MCP initialize 获取项目路径: ${mcpRootUri}`);
|
|
322
|
+
} else if (params.workspaceFolders && params.workspaceFolders.length > 0) {
|
|
323
|
+
mcpRootUri = uriToPath(params.workspaceFolders[0].uri);
|
|
324
|
+
log(`从 MCP workspaceFolders 获取项目路径: ${mcpRootUri}`);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// 延迟同步 skills(在获取到项目路径后)
|
|
328
|
+
if (!skillsSynced && mcpRootUri) {
|
|
329
|
+
const projectRoot = getProjectRoot();
|
|
330
|
+
const ideConfig = getIDEConfig();
|
|
331
|
+
log(`[延迟同步] 工作目录: ${projectRoot}, IDE: ${ideConfig.name}`);
|
|
332
|
+
syncSkills(projectRoot, ideConfig);
|
|
333
|
+
skillsSynced = true;
|
|
334
|
+
}
|
|
335
|
+
|
|
211
336
|
return {
|
|
212
337
|
protocolVersion: "2024-11-05",
|
|
213
338
|
serverInfo: { name: "bkui-knowledge-server", version: "1.0.0" },
|
|
@@ -302,16 +427,34 @@ async function startMcpServer() {
|
|
|
302
427
|
log('MCP stdio 服务已启动');
|
|
303
428
|
}
|
|
304
429
|
|
|
430
|
+
/**
|
|
431
|
+
* 检查是否有明确的项目路径(命令行参数或环境变量)
|
|
432
|
+
*/
|
|
433
|
+
function hasExplicitProjectPath() {
|
|
434
|
+
const pathArg = process.argv.slice(2).find(arg => !arg.startsWith('-'));
|
|
435
|
+
return !!(pathArg || process.env.BKUI_PROJECT_ROOT);
|
|
436
|
+
}
|
|
437
|
+
|
|
305
438
|
/**
|
|
306
439
|
* 主入口
|
|
307
440
|
*/
|
|
308
441
|
async function main() {
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
//
|
|
312
|
-
|
|
442
|
+
const ideConfig = getIDEConfig();
|
|
443
|
+
|
|
444
|
+
// 如果有明确的项目路径(命令行参数或环境变量),立即同步
|
|
445
|
+
// 否则等待 MCP initialize 请求获取项目路径
|
|
446
|
+
if (hasExplicitProjectPath()) {
|
|
447
|
+
const projectRoot = getProjectRoot();
|
|
448
|
+
log(`工作目录: ${projectRoot}`);
|
|
449
|
+
log(`目标 IDE: ${ideConfig.name}`);
|
|
450
|
+
syncSkills(projectRoot, ideConfig);
|
|
451
|
+
skillsSynced = true;
|
|
452
|
+
} else {
|
|
453
|
+
log(`等待 MCP initialize 请求获取项目路径...`);
|
|
454
|
+
log(`目标 IDE: ${ideConfig.name}`);
|
|
455
|
+
}
|
|
313
456
|
|
|
314
|
-
//
|
|
457
|
+
// 启动 MCP 服务
|
|
315
458
|
await startMcpServer();
|
|
316
459
|
}
|
|
317
460
|
|
package/knowledge/manifest.json
CHANGED
|
@@ -107,6 +107,43 @@
|
|
|
107
107
|
"category": "security",
|
|
108
108
|
"path": "nodejs-security-check/SKILL.md",
|
|
109
109
|
"tags": ["security", "nodejs", "backend", "ssrf", "sql-injection"]
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"id": "bkui-quick-start",
|
|
113
|
+
"name": "BKUI 快速入门",
|
|
114
|
+
"category": "engineering",
|
|
115
|
+
"path": "bkui-quick-start/SKILL.md",
|
|
116
|
+
"tags": ["bkui", "规范", "索引", "入门"]
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"id": "vue-best-practices",
|
|
120
|
+
"name": "Vue 3 开发最佳实践",
|
|
121
|
+
"category": "engineering",
|
|
122
|
+
"path": "external/vue-skills/skills/vue-best-practices/SKILL.md",
|
|
123
|
+
"tags": ["vue3", "typescript", "vue-tsc", "volar", "vite", "pinia"],
|
|
124
|
+
"external": true,
|
|
125
|
+
"source": "https://github.com/hyf0/vue-skills"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"id": "bk-skill-creator",
|
|
129
|
+
"name": "Skill 创建指南",
|
|
130
|
+
"category": "engineering",
|
|
131
|
+
"path": "bk-skill-creator/SKILL.md",
|
|
132
|
+
"tags": ["skill", "knowledge", "template", "guide"]
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"id": "bk-security-redlines",
|
|
136
|
+
"name": "蓝鲸代码安全三大红线",
|
|
137
|
+
"category": "security",
|
|
138
|
+
"path": "bk-security-redlines/SKILL.md",
|
|
139
|
+
"tags": ["security", "input-validation", "auth", "encryption", "redlines"]
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"id": "web-security-guide",
|
|
143
|
+
"name": "Web 安全漏洞学习指南",
|
|
144
|
+
"category": "security",
|
|
145
|
+
"path": "web-security-guide/SKILL.md",
|
|
146
|
+
"tags": ["security", "owasp", "vulnerability", "injection", "xss", "csrf", "ssrf"]
|
|
110
147
|
}
|
|
111
148
|
]
|
|
112
149
|
},
|
|
@@ -663,5 +700,5 @@
|
|
|
663
700
|
]
|
|
664
701
|
},
|
|
665
702
|
|
|
666
|
-
"recommendedSkills": ["bkui-builder", "bkui-cheatsheet", "code-review", "js-security-check"]
|
|
703
|
+
"recommendedSkills": ["bkui-quick-start", "bkui-builder", "bkui-cheatsheet", "vue-best-practices", "code-review", "js-security-check"]
|
|
667
704
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: security/bk-security-redlines
|
|
3
|
+
name: 蓝鲸代码安全三大红线
|
|
4
|
+
category: security
|
|
5
|
+
description: 基于 IEG 安全规范,覆盖输入校验、鉴权、数据加密三大高危领域
|
|
6
|
+
tags: [security, input-validation, auth, encryption, redlines]
|
|
7
|
+
updated_at: 2026-01-23
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# 蓝鲸代码安全三大红线
|
|
11
|
+
|
|
12
|
+
## ⚠️ 核心规则
|
|
13
|
+
|
|
14
|
+
### 红线 1:外部输入未校验
|
|
15
|
+
|
|
16
|
+
外部输入进入**高危操作**前,必须完成**服务端强约束校验**(类型/长度/格式/白名单),不可绕过。
|
|
17
|
+
|
|
18
|
+
**高危操作**:命令执行、模板解释/eval、文件路径、请求目标、SQL/NoSQL 构造、渲染解析、协议输出
|
|
19
|
+
|
|
20
|
+
### 红线 2:敏感接口未鉴权
|
|
21
|
+
|
|
22
|
+
高危能力或敏感资源接口必须同时实施**身份认证 + 权限校验**,缺一即违规。
|
|
23
|
+
|
|
24
|
+
**禁止**:接口无认证、仅登录不校验权限、依赖前端字段/URL/IP 判权、服务间调用无鉴权
|
|
25
|
+
|
|
26
|
+
### 红线 3:敏感数据未加密
|
|
27
|
+
|
|
28
|
+
敏感数据(密码/Token/AKSK/私钥/PII)在存储、传输、日志、导出任一环节保护不当即违规。
|
|
29
|
+
|
|
30
|
+
**禁止**:硬编码凭证、明文存储、异常回显细节、日志记录敏感字段、URL 携带 token
|
|
31
|
+
|
|
32
|
+
## 常见错误
|
|
33
|
+
|
|
34
|
+
| 错误做法 | 正确做法 |
|
|
35
|
+
|---------|---------|
|
|
36
|
+
| ❌ 仅前端/网关校验 | ✅ 服务端强约束校验 |
|
|
37
|
+
| ❌ 仅判空/黑名单 | ✅ 白名单 + 类型/长度/格式 |
|
|
38
|
+
| ❌ 内网可达即放通 | ✅ 服务间调用也需鉴权 |
|
|
39
|
+
| ❌ 日志记录完整请求 | ✅ 脱敏后记录 |
|
|
40
|
+
|
|
41
|
+
## 📦 按需加载资源
|
|
42
|
+
|
|
43
|
+
| 资源 | URI | 说明 |
|
|
44
|
+
|-----|-----|------|
|
|
45
|
+
| 输入校验详解 | `skill://bk-security-redlines/references/input-validation.md` | 7 类高危操作场景 |
|
|
46
|
+
| 鉴权检查详解 | `skill://bk-security-redlines/references/auth-check.md` | 8 类鉴权缺失场景 |
|
|
47
|
+
| 数据加密详解 | `skill://bk-security-redlines/references/data-encryption.md` | 8 类加密缺失场景 |
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# 红线 2:敏感接口未鉴权 - 详解
|
|
2
|
+
|
|
3
|
+
## 定义
|
|
4
|
+
|
|
5
|
+
凡提供高危能力或访问敏感资源的接口,必须在服务端实施完整鉴权:
|
|
6
|
+
|
|
7
|
+
- **身份认证**:确认"你是谁"
|
|
8
|
+
- **权限校验**:确认"你能做什么"
|
|
9
|
+
|
|
10
|
+
任一缺失即违规。
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 8 类鉴权缺失场景
|
|
15
|
+
|
|
16
|
+
### 2.1 接口缺失身份认证
|
|
17
|
+
|
|
18
|
+
**业务场景**:
|
|
19
|
+
- 新增 API
|
|
20
|
+
- 后台管理接口
|
|
21
|
+
- 内部运维接口
|
|
22
|
+
- RPC/SDK 入口
|
|
23
|
+
- 仅凭"内网可达"放通
|
|
24
|
+
|
|
25
|
+
### 2.2 接口缺失权限校验
|
|
26
|
+
|
|
27
|
+
**业务场景**:
|
|
28
|
+
- 仅校验登录不校验角色/租户/项目/资源范围
|
|
29
|
+
- 后台管理操作未做 RBAC/ABAC
|
|
30
|
+
|
|
31
|
+
### 2.3 鉴权依赖不可信要素
|
|
32
|
+
|
|
33
|
+
**业务场景**:
|
|
34
|
+
- `is_admin` 等前端字段判权
|
|
35
|
+
- URL 前缀隔离
|
|
36
|
+
- 仅网关策略
|
|
37
|
+
- 仅 IP 白名单
|
|
38
|
+
|
|
39
|
+
### 2.4 服务间调用缺失鉴权
|
|
40
|
+
|
|
41
|
+
**业务场景**:
|
|
42
|
+
- 服务间调用
|
|
43
|
+
- 内部 SDK
|
|
44
|
+
- 代调用
|
|
45
|
+
- 组件联动
|
|
46
|
+
|
|
47
|
+
### 2.5 资源访问缺失对象鉴权
|
|
48
|
+
|
|
49
|
+
**业务场景**:
|
|
50
|
+
- 仅凭 id/task_id/file_id/cluster_id/app_id 直接读写资源
|
|
51
|
+
- 未校验当前用户是否有权访问该资源
|
|
52
|
+
|
|
53
|
+
### 2.6 回调消息缺失验源
|
|
54
|
+
|
|
55
|
+
**业务场景**:
|
|
56
|
+
- 回调接口
|
|
57
|
+
- Webhook
|
|
58
|
+
- 消息消费
|
|
59
|
+
- 事件触发入口
|
|
60
|
+
|
|
61
|
+
### 2.7 会话操作缺失 CSRF 防护
|
|
62
|
+
|
|
63
|
+
**业务场景**:
|
|
64
|
+
- Cookie 会话的敏感操作
|
|
65
|
+
- 未使用 CSRF Token
|
|
66
|
+
- 未校验 Origin/Referer
|
|
67
|
+
|
|
68
|
+
### 2.8 调试运维缺失鉴权
|
|
69
|
+
|
|
70
|
+
**业务场景**:
|
|
71
|
+
- debug/admin/metrics/health 端点
|
|
72
|
+
- 运维开关
|
|
73
|
+
- 临时接口
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# 红线 3:敏感数据未加密 - 详解
|
|
2
|
+
|
|
3
|
+
## 定义
|
|
4
|
+
|
|
5
|
+
敏感数据在代码、存储、传输、日志、异常输出、导出/备份等任一环节保护不当即违规。
|
|
6
|
+
|
|
7
|
+
## 敏感数据范围
|
|
8
|
+
|
|
9
|
+
- 密码/Token/AKSK/连接串/私钥/加密密钥
|
|
10
|
+
- 权限票据
|
|
11
|
+
- 个人敏感信息(PII)
|
|
12
|
+
- 内部系统地址与关键配置
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 8 类加密缺失场景
|
|
17
|
+
|
|
18
|
+
### 3.1 敏感信息硬编码暴露
|
|
19
|
+
|
|
20
|
+
**业务场景**:
|
|
21
|
+
- 代码常量/默认配置/示例文件/CI 脚本中写入:
|
|
22
|
+
- 密码
|
|
23
|
+
- Token
|
|
24
|
+
- AK/SK
|
|
25
|
+
- 连接串
|
|
26
|
+
- 私钥
|
|
27
|
+
- 加密密钥
|
|
28
|
+
|
|
29
|
+
### 3.2 敏感信息明文存储
|
|
30
|
+
|
|
31
|
+
**业务场景**:
|
|
32
|
+
- 数据库字段明文存 token/密码/私钥
|
|
33
|
+
- 配置文件明文凭证
|
|
34
|
+
- 缓存/搜索系统写入明文敏感字段
|
|
35
|
+
|
|
36
|
+
### 3.3 异常回显泄露细节
|
|
37
|
+
|
|
38
|
+
**业务场景**:
|
|
39
|
+
- API 返回堆栈、SQL、文件路径
|
|
40
|
+
- API 返回内部域名、配置内容、密钥片段
|
|
41
|
+
- 将异常信息原样透传给调用方
|
|
42
|
+
|
|
43
|
+
### 3.4 日志链路泄露敏感
|
|
44
|
+
|
|
45
|
+
**业务场景**:
|
|
46
|
+
- 全量记录 request/response
|
|
47
|
+
- 记录 Authorization/Cookie/Token
|
|
48
|
+
- trace/span/metrics 写入 PII/凭证
|
|
49
|
+
|
|
50
|
+
### 3.5 票据出现在 URL/前端
|
|
51
|
+
|
|
52
|
+
**业务场景**:
|
|
53
|
+
- token 放 query 参数
|
|
54
|
+
- 跳转携带票据
|
|
55
|
+
- localStorage/sessionStorage 存长效 token/敏感字段
|
|
56
|
+
|
|
57
|
+
### 3.6 敏感传输缺少加密
|
|
58
|
+
|
|
59
|
+
**业务场景**:
|
|
60
|
+
- HTTP 明文传 token/凭证/PII
|
|
61
|
+
- 跳过证书校验
|
|
62
|
+
- RPC/MQ 链路未加密传输敏感字段
|
|
63
|
+
|
|
64
|
+
### 3.7 导出备份暴露敏感
|
|
65
|
+
|
|
66
|
+
**业务场景**:
|
|
67
|
+
- 导出 zip/csv 含凭证/配置/PII
|
|
68
|
+
- dump/日志包上传对象存储
|
|
69
|
+
- 权限过宽、无过期控制或可公开访问
|
|
70
|
+
|
|
71
|
+
### 3.8 密码学使用不规范
|
|
72
|
+
|
|
73
|
+
**业务场景**:
|
|
74
|
+
- 自研加密算法
|
|
75
|
+
- 弱算法(MD5/SHA1 用于密码、DES/RC4 等)
|
|
76
|
+
- 固定 IV/盐
|
|
77
|
+
- 同密钥多用途
|
|
78
|
+
- 密钥落配置/日志或在接口返回中泄漏
|