transskill 0.2.1 → 0.2.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 +162 -32
- package/README.zh.md +134 -4
- package/dist/audit/auditor-registry.d.ts +6 -0
- package/dist/audit/auditor-registry.d.ts.map +1 -0
- package/dist/audit/auditor-registry.js +17 -0
- package/dist/audit/auditor-registry.js.map +1 -0
- package/dist/audit/auditor.interface.d.ts +59 -0
- package/dist/audit/auditor.interface.d.ts.map +1 -0
- package/dist/audit/auditor.interface.js +36 -0
- package/dist/audit/auditor.interface.js.map +1 -0
- package/dist/audit/index.d.ts +39 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +155 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/audit/reporter/console-reporter.d.ts +4 -0
- package/dist/audit/reporter/console-reporter.d.ts.map +1 -0
- package/dist/audit/reporter/console-reporter.js +107 -0
- package/dist/audit/reporter/console-reporter.js.map +1 -0
- package/dist/audit/rules/dangerous-commands.d.ts +15 -0
- package/dist/audit/rules/dangerous-commands.d.ts.map +1 -0
- package/dist/audit/rules/dangerous-commands.js +189 -0
- package/dist/audit/rules/dangerous-commands.js.map +1 -0
- package/dist/audit/rules/prompt-injection.d.ts +15 -0
- package/dist/audit/rules/prompt-injection.d.ts.map +1 -0
- package/dist/audit/rules/prompt-injection.js +65 -0
- package/dist/audit/rules/prompt-injection.js.map +1 -0
- package/dist/audit/rules/suspicious-urls.d.ts +14 -0
- package/dist/audit/rules/suspicious-urls.d.ts.map +1 -0
- package/dist/audit/rules/suspicious-urls.js +42 -0
- package/dist/audit/rules/suspicious-urls.js.map +1 -0
- package/dist/audit/scanner/directory-scanner.d.ts +40 -0
- package/dist/audit/scanner/directory-scanner.d.ts.map +1 -0
- package/dist/audit/scanner/directory-scanner.js +288 -0
- package/dist/audit/scanner/directory-scanner.js.map +1 -0
- package/dist/audit/scanner/instruction-scanner.d.ts +20 -0
- package/dist/audit/scanner/instruction-scanner.d.ts.map +1 -0
- package/dist/audit/scanner/instruction-scanner.js +147 -0
- package/dist/audit/scanner/instruction-scanner.js.map +1 -0
- package/dist/audit/scanner/mcp-scanner.d.ts +17 -0
- package/dist/audit/scanner/mcp-scanner.d.ts.map +1 -0
- package/dist/audit/scanner/mcp-scanner.js +181 -0
- package/dist/audit/scanner/mcp-scanner.js.map +1 -0
- package/dist/audit/scanner/permission-scanner.d.ts +19 -0
- package/dist/audit/scanner/permission-scanner.d.ts.map +1 -0
- package/dist/audit/scanner/permission-scanner.js +143 -0
- package/dist/audit/scanner/permission-scanner.js.map +1 -0
- package/dist/index.js +86 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rules for detecting suspicious URLs and network references
|
|
3
|
+
* in agent skill instructions.
|
|
4
|
+
*/
|
|
5
|
+
export const URL_PATTERNS = [
|
|
6
|
+
{
|
|
7
|
+
id: 'L1-011a',
|
|
8
|
+
pattern: /https?:\/\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?::\d+)?\//,
|
|
9
|
+
severity: 'medium',
|
|
10
|
+
description: '指令中包含直接 IP 地址的 HTTP 请求,可能指向恶意服务器',
|
|
11
|
+
recommendation: '审查该 IP 地址是否可信,尽可能使用域名替代 IP',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: 'L1-011b',
|
|
15
|
+
pattern: /https?:\/\/[^\s'"]*\.(?:onion|i2p)\b/i,
|
|
16
|
+
severity: 'high',
|
|
17
|
+
description: '引用暗网地址(.onion/.i2p),可能访问非法内容',
|
|
18
|
+
recommendation: '审查该地址的用途,避免从暗网下载不可信内容',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: 'L1-011c',
|
|
22
|
+
pattern: /https?:\/\/[^\s'"]*(?:pastebin|hastebin|rentry|ghostbin|dpaste)\.[^\s'"]+/i,
|
|
23
|
+
severity: 'medium',
|
|
24
|
+
description: '引用文本分享网站,内容不可追踪且可能隐藏恶意载荷',
|
|
25
|
+
recommendation: '审查引用的实际内容是否安全',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'L1-011d',
|
|
29
|
+
pattern: /https?:\/\/[^\s'"]*(?:raw\.githubusercontent|gist\.github)[^\s'"]+\.(?:sh|py|js|ps1|bat)\b/i,
|
|
30
|
+
severity: 'low',
|
|
31
|
+
description: '引用 GitHub 上的脚本文件,需确认来源是否可信',
|
|
32
|
+
recommendation: '确认 GitHub 仓库和作者是否可信,建议锁定版本 commit hash',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 'L1-011e',
|
|
36
|
+
pattern: /data:\s*text\/[a-z]+;base64,[A-Za-z0-9+/=]{50,}/i,
|
|
37
|
+
severity: 'medium',
|
|
38
|
+
description: '指令中包含 data: URI 长 base64 内容,可能隐藏恶意载荷',
|
|
39
|
+
recommendation: '审查 data: URI 解码后的实际内容',
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
//# sourceMappingURL=suspicious-urls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suspicious-urls.js","sourceRoot":"","sources":["../../../src/audit/rules/suspicious-urls.ts"],"names":[],"mappings":"AAUA;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAc;IACrC;QACE,EAAE,EAAE,SAAS;QACb,OAAO,EAAE,0DAA0D;QACnE,QAAQ,EAAE,QAAQ;QAClB,WAAW,EAAE,kCAAkC;QAC/C,cAAc,EAAE,4BAA4B;KAC7C;IACD;QACE,EAAE,EAAE,SAAS;QACb,OAAO,EAAE,uCAAuC;QAChD,QAAQ,EAAE,MAAM;QAChB,WAAW,EAAE,8BAA8B;QAC3C,cAAc,EAAE,uBAAuB;KACxC;IACD;QACE,EAAE,EAAE,SAAS;QACb,OAAO,EAAE,4EAA4E;QACrF,QAAQ,EAAE,QAAQ;QAClB,WAAW,EAAE,0BAA0B;QACvC,cAAc,EAAE,eAAe;KAChC;IACD;QACE,EAAE,EAAE,SAAS;QACb,OAAO,EAAE,6FAA6F;QACtG,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,4BAA4B;QACzC,cAAc,EAAE,wCAAwC;KACzD;IACD;QACE,EAAE,EAAE,SAAS;QACb,OAAO,EAAE,kDAAkD;QAC3D,QAAQ,EAAE,QAAQ;QAClB,WAAW,EAAE,sCAAsC;QACnD,cAAc,EAAE,uBAAuB;KACxC;CACF,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Auditor, AuditFinding } from '../auditor.interface.js';
|
|
2
|
+
import type { IntermediateSkill } from '../../core/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* DirectoryScanner (D)
|
|
5
|
+
*
|
|
6
|
+
* Scans the full skill directory structure for security issues:
|
|
7
|
+
* - Script files in scripts/ (analyzed recursively)
|
|
8
|
+
* - Hidden files and sensitive data
|
|
9
|
+
* - Symbolic link escapes
|
|
10
|
+
* - Cross-file reference chains
|
|
11
|
+
*
|
|
12
|
+
* This auditor runs on the rootPath of a skill directory
|
|
13
|
+
* AND the parsed skill content.
|
|
14
|
+
*/
|
|
15
|
+
export declare class DirectoryScanner implements Auditor {
|
|
16
|
+
readonly id = "directory-scanner";
|
|
17
|
+
readonly name = "\u76EE\u5F55\u626B\u63CF\u5668 (D)";
|
|
18
|
+
readonly description = "\u626B\u63CF skill \u76EE\u5F55\u7ED3\u6784\u4E2D\u7684\u5B89\u5168\u9690\u60A3";
|
|
19
|
+
readonly supportsDirectory = true;
|
|
20
|
+
/**
|
|
21
|
+
* Directory audit entry point.
|
|
22
|
+
* The rootPath is passed as the instructions (hacky but works with current arch).
|
|
23
|
+
* In practice, use `auditDirectory()` directly.
|
|
24
|
+
*/
|
|
25
|
+
audit(skill: IntermediateSkill, filePath: string): AuditFinding[];
|
|
26
|
+
/**
|
|
27
|
+
* Full directory audit. Scans all files in the skill directory.
|
|
28
|
+
* Call this directly for directory-mode auditing.
|
|
29
|
+
*/
|
|
30
|
+
auditDirectory(rootPath: string, skillFilePath?: string): AuditFinding[];
|
|
31
|
+
private auditScriptFile;
|
|
32
|
+
private isHiddenFile;
|
|
33
|
+
private checkHiddenFile;
|
|
34
|
+
private isSymlink;
|
|
35
|
+
private checkSymlink;
|
|
36
|
+
private checkCrossFileRefs;
|
|
37
|
+
private locateRoot;
|
|
38
|
+
private collectFiles;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=directory-scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directory-scanner.d.ts","sourceRoot":"","sources":["../../../src/audit/scanner/directory-scanner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAG7D;;;;;;;;;;;GAWG;AACH,qBAAa,gBAAiB,YAAW,OAAO;IAC9C,QAAQ,CAAC,EAAE,uBAAuB;IAClC,QAAQ,CAAC,IAAI,wCAAe;IAC5B,QAAQ,CAAC,WAAW,qFAAyB;IAC7C,QAAQ,CAAC,iBAAiB,QAAQ;IAElC;;;;OAIG;IACH,KAAK,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,EAAE;IASjE;;;OAGG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE;IA2CxE,OAAO,CAAC,eAAe;IAsEvB,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,eAAe;IA+CvB,OAAO,CAAC,SAAS;IAQjB,OAAO,CAAC,YAAY;IAyBpB,OAAO,CAAC,kBAAkB;IAoC1B,OAAO,CAAC,UAAU;IAkBlB,OAAO,CAAC,YAAY;CAsBrB"}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync, lstatSync } from 'node:fs';
|
|
2
|
+
import { join, resolve, relative } from 'node:path';
|
|
3
|
+
import { InstructionScanner } from './instruction-scanner.js';
|
|
4
|
+
/**
|
|
5
|
+
* DirectoryScanner (D)
|
|
6
|
+
*
|
|
7
|
+
* Scans the full skill directory structure for security issues:
|
|
8
|
+
* - Script files in scripts/ (analyzed recursively)
|
|
9
|
+
* - Hidden files and sensitive data
|
|
10
|
+
* - Symbolic link escapes
|
|
11
|
+
* - Cross-file reference chains
|
|
12
|
+
*
|
|
13
|
+
* This auditor runs on the rootPath of a skill directory
|
|
14
|
+
* AND the parsed skill content.
|
|
15
|
+
*/
|
|
16
|
+
export class DirectoryScanner {
|
|
17
|
+
id = 'directory-scanner';
|
|
18
|
+
name = '目录扫描器 (D)';
|
|
19
|
+
description = '扫描 skill 目录结构中的安全隐患';
|
|
20
|
+
supportsDirectory = true;
|
|
21
|
+
/**
|
|
22
|
+
* Directory audit entry point.
|
|
23
|
+
* The rootPath is passed as the instructions (hacky but works with current arch).
|
|
24
|
+
* In practice, use `auditDirectory()` directly.
|
|
25
|
+
*/
|
|
26
|
+
audit(skill, filePath) {
|
|
27
|
+
// Normal mode: filePath is a file — extract dir from it
|
|
28
|
+
const dirPath = this.locateRoot(skill, filePath);
|
|
29
|
+
if (!dirPath || !existsSync(dirPath) || !statSync(dirPath).isDirectory()) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
return this.auditDirectory(dirPath, filePath);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Full directory audit. Scans all files in the skill directory.
|
|
36
|
+
* Call this directly for directory-mode auditing.
|
|
37
|
+
*/
|
|
38
|
+
auditDirectory(rootPath, skillFilePath) {
|
|
39
|
+
const findings = [];
|
|
40
|
+
const absRoot = resolve(rootPath);
|
|
41
|
+
if (!existsSync(absRoot)) {
|
|
42
|
+
return findings;
|
|
43
|
+
}
|
|
44
|
+
// Collect all files
|
|
45
|
+
const allFiles = this.collectFiles(absRoot);
|
|
46
|
+
for (const file of allFiles) {
|
|
47
|
+
const relPath = relative(absRoot, file);
|
|
48
|
+
// Skip the skill file itself (already analyzed by L1)
|
|
49
|
+
if (skillFilePath && resolve(file) === resolve(skillFilePath)) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
// D-001: Check scripts/ subdirectory
|
|
53
|
+
if (relPath.startsWith('scripts/') || relPath.startsWith('scripts\\')) {
|
|
54
|
+
findings.push(...this.auditScriptFile(file, relPath));
|
|
55
|
+
}
|
|
56
|
+
// D-003: Hidden files
|
|
57
|
+
if (this.isHiddenFile(relPath)) {
|
|
58
|
+
findings.push(...this.checkHiddenFile(file, relPath));
|
|
59
|
+
}
|
|
60
|
+
// D-004: Symbolic link escape
|
|
61
|
+
if (this.isSymlink(file)) {
|
|
62
|
+
findings.push(...this.checkSymlink(file, relPath, absRoot));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// D-005: Cross-file URL references
|
|
66
|
+
findings.push(...this.checkCrossFileRefs(absRoot, allFiles));
|
|
67
|
+
return findings;
|
|
68
|
+
}
|
|
69
|
+
// ── D-001: Script file audit ──
|
|
70
|
+
auditScriptFile(filePath, relPath) {
|
|
71
|
+
const findings = [];
|
|
72
|
+
try {
|
|
73
|
+
const ext = filePath.split('.').pop()?.toLowerCase();
|
|
74
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
75
|
+
// Reuse InstructionScanner rules on script content
|
|
76
|
+
const scanner = new InstructionScanner();
|
|
77
|
+
const mockSkill = {
|
|
78
|
+
name: relPath,
|
|
79
|
+
description: '',
|
|
80
|
+
instructions: content,
|
|
81
|
+
metadata: { sourceFormat: '.cursorrules' },
|
|
82
|
+
platformSpecific: {},
|
|
83
|
+
};
|
|
84
|
+
const scriptFindings = scanner.audit(mockSkill, relPath);
|
|
85
|
+
// Re-tag findings with D prefix
|
|
86
|
+
for (const f of scriptFindings) {
|
|
87
|
+
findings.push({
|
|
88
|
+
...f,
|
|
89
|
+
id: `D-001-${f.id}`,
|
|
90
|
+
filePath: relPath,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// Check file extension against allowed script types
|
|
94
|
+
const allowedExts = ['sh', 'py', 'js', 'ts', 'bash', 'zsh', 'ps1', 'bat'];
|
|
95
|
+
if (!allowedExts.includes(ext ?? '')) {
|
|
96
|
+
const size = statSync(filePath).size;
|
|
97
|
+
if (size > 0 && size < 1024 * 1024) {
|
|
98
|
+
// Small binary or unusual script
|
|
99
|
+
const header = content.slice(0, 100);
|
|
100
|
+
if (/[\x00-\x08\x0e-\x1f]/.test(header)) {
|
|
101
|
+
findings.push({
|
|
102
|
+
id: 'D-001b',
|
|
103
|
+
severity: 'medium',
|
|
104
|
+
title: 'scripts/ 中包含疑似二进制或混淆文件',
|
|
105
|
+
description: `${relPath} 包含二进制内容,可能是打包的恶意载荷`,
|
|
106
|
+
filePath: relPath,
|
|
107
|
+
recommendation: '检查该文件的真实内容',
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// Binary or unreadable file — flag if suspicious
|
|
115
|
+
try {
|
|
116
|
+
const size = statSync(filePath).size;
|
|
117
|
+
if (size > 0 && size < 10 * 1024 * 1024) {
|
|
118
|
+
findings.push({
|
|
119
|
+
id: 'D-001c',
|
|
120
|
+
severity: 'low',
|
|
121
|
+
title: '无法读取脚本文件',
|
|
122
|
+
description: `${relPath} 无法作为文本读取,可能是二进制文件`,
|
|
123
|
+
filePath: relPath,
|
|
124
|
+
recommendation: '确认该文件是否属于 skill 的必要部分',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// Skip unreadable
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return findings;
|
|
133
|
+
}
|
|
134
|
+
// ── D-003: Hidden file checks ──
|
|
135
|
+
isHiddenFile(relPath) {
|
|
136
|
+
const parts = relPath.split(/[/\\]/);
|
|
137
|
+
return parts.some((p) => p.startsWith('.') && p.length > 1);
|
|
138
|
+
}
|
|
139
|
+
checkHiddenFile(filePath, relPath) {
|
|
140
|
+
const findings = [];
|
|
141
|
+
const name = relPath.split(/[/\\]/).pop() ?? '';
|
|
142
|
+
// Check for sensitive hidden files
|
|
143
|
+
const sensitivePatterns = [
|
|
144
|
+
{ pattern: /^\.env/, severity: 'high', desc: '环境变量文件,可能包含 API key 和密码' },
|
|
145
|
+
{ pattern: /^\.git[^/]*$/, severity: 'high', desc: 'Git 配置目录/文件,可能泄露仓库信息' },
|
|
146
|
+
{ pattern: /^\.npmrc/, severity: 'medium', desc: 'npm 配置文件,可能包含认证 token' },
|
|
147
|
+
{ pattern: /^\.aws/, severity: 'high', desc: 'AWS 配置目录,可能包含云凭证' },
|
|
148
|
+
{ pattern: /^\.ssh/, severity: 'critical', desc: 'SSH 配置目录,可能包含私钥' },
|
|
149
|
+
{ pattern: /^\.docker/, severity: 'high', desc: 'Docker 配置目录' },
|
|
150
|
+
{ pattern: /^\.kube/, severity: 'high', desc: 'Kubernetes 配置目录,可能包含集群凭证' },
|
|
151
|
+
];
|
|
152
|
+
for (const sp of sensitivePatterns) {
|
|
153
|
+
if (sp.pattern.test(name)) {
|
|
154
|
+
findings.push({
|
|
155
|
+
id: 'D-003',
|
|
156
|
+
severity: sp.severity,
|
|
157
|
+
title: `目录中包含敏感文件: ${relPath}`,
|
|
158
|
+
description: sp.desc,
|
|
159
|
+
filePath: relPath,
|
|
160
|
+
recommendation: '将敏感配置文件从 skill 目录中移除,使用环境变量引用',
|
|
161
|
+
cwe: 'CWE-522',
|
|
162
|
+
});
|
|
163
|
+
return findings; // One match per file
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Generic hidden file (non-sensitive)
|
|
167
|
+
if (!relPath.startsWith('.')) {
|
|
168
|
+
findings.push({
|
|
169
|
+
id: 'D-003b',
|
|
170
|
+
severity: 'low',
|
|
171
|
+
title: `目录中包含隐藏文件: ${relPath}`,
|
|
172
|
+
description: `${relPath} 是隐藏文件,可能无意中包含在 skill 中`,
|
|
173
|
+
filePath: relPath,
|
|
174
|
+
recommendation: '确认该文件是否应该包含在 skill 中',
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
return findings;
|
|
178
|
+
}
|
|
179
|
+
// ── D-004: Symlink escape ──
|
|
180
|
+
isSymlink(filePath) {
|
|
181
|
+
try {
|
|
182
|
+
return lstatSync(filePath).isSymbolicLink();
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
checkSymlink(filePath, relPath, rootPath) {
|
|
189
|
+
const findings = [];
|
|
190
|
+
try {
|
|
191
|
+
const realPath = resolve(rootPath, readFileSync(filePath, 'utf-8'));
|
|
192
|
+
const absRoot = resolve(rootPath);
|
|
193
|
+
if (!realPath.startsWith(absRoot)) {
|
|
194
|
+
findings.push({
|
|
195
|
+
id: 'D-004',
|
|
196
|
+
severity: 'high',
|
|
197
|
+
title: '符号链接逃逸到 skill 目录外',
|
|
198
|
+
description: `${relPath} 是一个符号链接,指向目录外的路径: ${realPath}`,
|
|
199
|
+
filePath: relPath,
|
|
200
|
+
recommendation: '移除指向目录外的符号链接,或将目标文件复制到 skill 目录内',
|
|
201
|
+
cwe: 'CWE-59',
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// Can't resolve symlink
|
|
207
|
+
}
|
|
208
|
+
return findings;
|
|
209
|
+
}
|
|
210
|
+
// ── D-005: Cross-file reference chain ──
|
|
211
|
+
checkCrossFileRefs(rootPath, allFiles) {
|
|
212
|
+
const findings = [];
|
|
213
|
+
const urlRegex = /https?:\/\/[^\s'"]+/g;
|
|
214
|
+
for (const file of allFiles) {
|
|
215
|
+
try {
|
|
216
|
+
const content = readFileSync(file, 'utf-8');
|
|
217
|
+
const urls = content.match(urlRegex);
|
|
218
|
+
if (!urls)
|
|
219
|
+
continue;
|
|
220
|
+
for (const url of urls) {
|
|
221
|
+
// Flag URLs that download and execute scripts
|
|
222
|
+
if (/(?:curl|wget)\s+.*(?:https?:\/\/)/i.test(content)) {
|
|
223
|
+
const relPath = relative(rootPath, file);
|
|
224
|
+
findings.push({
|
|
225
|
+
id: 'D-005',
|
|
226
|
+
severity: 'high',
|
|
227
|
+
title: `文件引用外部 URL 并可能执行: ${relPath}`,
|
|
228
|
+
description: `${relPath} 中包含 curl/wget 外部 URL 的指令`,
|
|
229
|
+
filePath: relPath,
|
|
230
|
+
recommendation: '审查该外部 URL 的内容,确保来源可信',
|
|
231
|
+
cwe: 'CWE-494',
|
|
232
|
+
});
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
// Skip binary
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return findings;
|
|
242
|
+
}
|
|
243
|
+
// ── Helpers ──
|
|
244
|
+
locateRoot(skill, filePath) {
|
|
245
|
+
// Try attached files for directory clues
|
|
246
|
+
if (skill.metadata.attachedFiles && skill.metadata.attachedFiles.length > 0) {
|
|
247
|
+
const first = skill.metadata.attachedFiles[0];
|
|
248
|
+
const dir = join(first.absolutePath, '..');
|
|
249
|
+
if (existsSync(dir))
|
|
250
|
+
return dir;
|
|
251
|
+
}
|
|
252
|
+
// Try parent of the skill file
|
|
253
|
+
const dir = resolve(filePath, '..');
|
|
254
|
+
if (existsSync(dir))
|
|
255
|
+
return dir;
|
|
256
|
+
// Try the file path itself
|
|
257
|
+
if (existsSync(filePath) && statSync(filePath).isDirectory())
|
|
258
|
+
return filePath;
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
collectFiles(dirPath) {
|
|
262
|
+
const files = [];
|
|
263
|
+
try {
|
|
264
|
+
const entries = readdirSync(dirPath);
|
|
265
|
+
for (const entry of entries) {
|
|
266
|
+
if (entry === '.git' || entry === 'node_modules')
|
|
267
|
+
continue;
|
|
268
|
+
const fullPath = join(dirPath, entry);
|
|
269
|
+
try {
|
|
270
|
+
if (lstatSync(fullPath).isDirectory()) {
|
|
271
|
+
files.push(...this.collectFiles(fullPath));
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
files.push(fullPath);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
// Permission denied, skip
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
// Can't read, skip
|
|
284
|
+
}
|
|
285
|
+
return files;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=directory-scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directory-scanner.js","sourceRoot":"","sources":["../../../src/audit/scanner/directory-scanner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Auditor, AuditFinding } from '../auditor.interface.js';
|
|
2
|
+
import type { IntermediateSkill } from '../../core/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* InstructionScanner (L1)
|
|
5
|
+
*
|
|
6
|
+
* Scans the instructions body of any skill format for:
|
|
7
|
+
* - Dangerous shell commands (rm -rf, sudo, etc.)
|
|
8
|
+
* - Remote code execution patterns (curl|sh, eval)
|
|
9
|
+
* - Prompt injection / jailbreak attempts
|
|
10
|
+
* - Suspicious URL references
|
|
11
|
+
* - Base64/hex encoded commands
|
|
12
|
+
*/
|
|
13
|
+
export declare class InstructionScanner implements Auditor {
|
|
14
|
+
readonly id = "instruction-scanner";
|
|
15
|
+
readonly name = "\u6307\u4EE4\u626B\u63CF\u5668 (L1)";
|
|
16
|
+
readonly description = "\u626B\u63CF skill \u6307\u4EE4\u4E2D\u7684\u5371\u9669\u547D\u4EE4\u3001prompt \u6CE8\u5165\u3001\u7F51\u7EDC\u5916\u94FE\u7B49";
|
|
17
|
+
audit(skill: IntermediateSkill, filePath: string): AuditFinding[];
|
|
18
|
+
private checkEncoding;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=instruction-scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instruction-scanner.d.ts","sourceRoot":"","sources":["../../../src/audit/scanner/instruction-scanner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAK7D;;;;;;;;;GASG;AACH,qBAAa,kBAAmB,YAAW,OAAO;IAChD,QAAQ,CAAC,EAAE,yBAAyB;IACpC,QAAQ,CAAC,IAAI,yCAAgB;IAC7B,QAAQ,CAAC,WAAW,sIAAuC;IAE3D,KAAK,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,EAAE;IA4EjE,OAAO,CAAC,aAAa;CAmDtB"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { DANGEROUS_COMMAND_RULES } from '../rules/dangerous-commands.js';
|
|
2
|
+
import { PROMPT_INJECTION_RULES } from '../rules/prompt-injection.js';
|
|
3
|
+
import { URL_PATTERNS } from '../rules/suspicious-urls.js';
|
|
4
|
+
/**
|
|
5
|
+
* InstructionScanner (L1)
|
|
6
|
+
*
|
|
7
|
+
* Scans the instructions body of any skill format for:
|
|
8
|
+
* - Dangerous shell commands (rm -rf, sudo, etc.)
|
|
9
|
+
* - Remote code execution patterns (curl|sh, eval)
|
|
10
|
+
* - Prompt injection / jailbreak attempts
|
|
11
|
+
* - Suspicious URL references
|
|
12
|
+
* - Base64/hex encoded commands
|
|
13
|
+
*/
|
|
14
|
+
export class InstructionScanner {
|
|
15
|
+
id = 'instruction-scanner';
|
|
16
|
+
name = '指令扫描器 (L1)';
|
|
17
|
+
description = '扫描 skill 指令中的危险命令、prompt 注入、网络外链等';
|
|
18
|
+
audit(skill, filePath) {
|
|
19
|
+
const findings = [];
|
|
20
|
+
const instructions = skill.instructions;
|
|
21
|
+
if (!instructions || instructions.trim().length === 0) {
|
|
22
|
+
return findings;
|
|
23
|
+
}
|
|
24
|
+
const lines = instructions.split('\n');
|
|
25
|
+
// 1) Check dangerous command patterns
|
|
26
|
+
for (const rule of DANGEROUS_COMMAND_RULES) {
|
|
27
|
+
const match = instructions.match(rule.pattern);
|
|
28
|
+
if (match) {
|
|
29
|
+
const lineNumber = findLineNumber(lines, match.index ?? 0);
|
|
30
|
+
findings.push({
|
|
31
|
+
id: rule.id,
|
|
32
|
+
severity: rule.severity,
|
|
33
|
+
title: rule.description.length > 60
|
|
34
|
+
? rule.description.slice(0, 60) + '…'
|
|
35
|
+
: rule.description,
|
|
36
|
+
description: rule.description,
|
|
37
|
+
filePath,
|
|
38
|
+
lineNumber,
|
|
39
|
+
snippet: extractSnippet(lines, lineNumber),
|
|
40
|
+
recommendation: rule.recommendation,
|
|
41
|
+
cwe: rule.cwe,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// 2) Check prompt injection patterns
|
|
46
|
+
for (const rule of PROMPT_INJECTION_RULES) {
|
|
47
|
+
const match = instructions.match(rule.pattern);
|
|
48
|
+
if (match) {
|
|
49
|
+
const lineNumber = findLineNumber(lines, match.index ?? 0);
|
|
50
|
+
findings.push({
|
|
51
|
+
id: rule.id,
|
|
52
|
+
severity: rule.severity,
|
|
53
|
+
title: rule.description.length > 60
|
|
54
|
+
? rule.description.slice(0, 60) + '…'
|
|
55
|
+
: rule.description,
|
|
56
|
+
description: rule.description,
|
|
57
|
+
filePath,
|
|
58
|
+
lineNumber,
|
|
59
|
+
snippet: extractSnippet(lines, lineNumber),
|
|
60
|
+
recommendation: rule.recommendation,
|
|
61
|
+
cwe: rule.cwe,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// 3) Check for base64/hex encoding (potential obfuscation)
|
|
66
|
+
findings.push(...this.checkEncoding(instructions, filePath, lines));
|
|
67
|
+
// 4) Check for suspicious URLs
|
|
68
|
+
for (const rule of URL_PATTERNS) {
|
|
69
|
+
const match = instructions.match(rule.pattern);
|
|
70
|
+
if (match) {
|
|
71
|
+
const lineNumber = findLineNumber(lines, match.index ?? 0);
|
|
72
|
+
findings.push({
|
|
73
|
+
id: rule.id,
|
|
74
|
+
severity: rule.severity,
|
|
75
|
+
title: rule.description,
|
|
76
|
+
description: rule.description,
|
|
77
|
+
filePath,
|
|
78
|
+
lineNumber,
|
|
79
|
+
snippet: extractSnippet(lines, lineNumber),
|
|
80
|
+
recommendation: rule.recommendation,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return findings;
|
|
85
|
+
}
|
|
86
|
+
checkEncoding(instructions, filePath, lines) {
|
|
87
|
+
const findings = [];
|
|
88
|
+
// Detect base64 decode then execute patterns
|
|
89
|
+
const b64Exec = /(?:base64\s*-d|base64\s*--decode|frombase64)\s*[|;]\s*(?:sh|bash|python|node)\b/i;
|
|
90
|
+
const b64Match = instructions.match(b64Exec);
|
|
91
|
+
if (b64Match) {
|
|
92
|
+
findings.push({
|
|
93
|
+
id: 'L1-008a',
|
|
94
|
+
severity: 'high',
|
|
95
|
+
title: 'Base64 解码后执行,可能隐藏恶意指令',
|
|
96
|
+
description: '对 base64 编码内容解码后执行,可能用于隐藏恶意载荷',
|
|
97
|
+
filePath,
|
|
98
|
+
lineNumber: findLineNumber(lines, b64Match.index ?? 0),
|
|
99
|
+
snippet: extractSnippet(lines, findLineNumber(lines, b64Match.index ?? 0)),
|
|
100
|
+
recommendation: '审查解码后的实际内容是否安全',
|
|
101
|
+
cwe: 'CWE-693',
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// Detect long base64 strings embedded in commands
|
|
105
|
+
const longB64 = /['"][A-Za-z0-9+/=]{100,}['"]/g;
|
|
106
|
+
let b64StringMatch;
|
|
107
|
+
while ((b64StringMatch = longB64.exec(instructions)) !== null) {
|
|
108
|
+
// Only flag if it looks like it's being used somehow (near a command)
|
|
109
|
+
const ctxStart = Math.max(0, b64StringMatch.index - 60);
|
|
110
|
+
const ctxEnd = Math.min(instructions.length, b64StringMatch.index + b64StringMatch[0].length + 60);
|
|
111
|
+
const context = instructions.slice(ctxStart, ctxEnd);
|
|
112
|
+
const suspiciousContext = /\b(run|exec|eval|decode|execute|load|import|source)\b/i.test(context);
|
|
113
|
+
if (suspiciousContext) {
|
|
114
|
+
findings.push({
|
|
115
|
+
id: 'L1-008b',
|
|
116
|
+
severity: 'medium',
|
|
117
|
+
title: '指令中包含长 base64 编码字符串',
|
|
118
|
+
description: '疑似编码混淆,可能隐藏恶意载荷',
|
|
119
|
+
filePath,
|
|
120
|
+
lineNumber: findLineNumber(lines, b64StringMatch.index),
|
|
121
|
+
snippet: extractSnippet(lines, findLineNumber(lines, b64StringMatch.index)),
|
|
122
|
+
recommendation: '审查 base64 解码后的内容',
|
|
123
|
+
cwe: 'CWE-693',
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return findings;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// ── Helpers ──
|
|
131
|
+
function findLineNumber(lines, charIndex) {
|
|
132
|
+
let pos = 0;
|
|
133
|
+
for (let i = 0; i < lines.length; i++) {
|
|
134
|
+
pos += lines[i].length + 1; // +1 for newline
|
|
135
|
+
if (pos > charIndex)
|
|
136
|
+
return i + 1; // 1-indexed
|
|
137
|
+
}
|
|
138
|
+
return lines.length;
|
|
139
|
+
}
|
|
140
|
+
function extractSnippet(lines, lineNumber) {
|
|
141
|
+
const idx = lineNumber - 1;
|
|
142
|
+
if (idx < 0 || idx >= lines.length)
|
|
143
|
+
return '';
|
|
144
|
+
const line = lines[idx].trim();
|
|
145
|
+
return line.length > 120 ? line.slice(0, 120) + '…' : line;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=instruction-scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instruction-scanner.js","sourceRoot":"","sources":["../../../src/audit/scanner/instruction-scanner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D;;;;;;;;;GASG;AACH,MAAM,OAAO,kBAAkB;IACpB,EAAE,GAAG,qBAAqB,CAAC;IAC3B,IAAI,GAAG,YAAY,CAAC;IACpB,WAAW,GAAG,mCAAmC,CAAC;IAE3D,KAAK,CAAC,KAAwB,EAAE,QAAgB;QAC9C,MAAM,QAAQ,GAAmB,EAAE,CAAC;QACpC,MAAM,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC;QAExC,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtD,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAEvC,sCAAsC;QACtC,KAAK,MAAM,IAAI,IAAI,uBAAuB,EAAE,CAAC;YAC3C,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC/C,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;gBAC3D,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,IAAI,CAAC,EAAE;oBACX,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,EAAE;wBACjC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG;wBACrC,CAAC,CAAC,IAAI,CAAC,WAAW;oBACpB,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,QAAQ;oBACR,UAAU;oBACV,OAAO,EAAE,cAAc,CAAC,KAAK,EAAE,UAAU,CAAC;oBAC1C,cAAc,EAAE,IAAI,CAAC,cAAc;oBACnC,GAAG,EAAE,IAAI,CAAC,GAAG;iBACd,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,qCAAqC;QACrC,KAAK,MAAM,IAAI,IAAI,sBAAsB,EAAE,CAAC;YAC1C,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC/C,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;gBAC3D,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,IAAI,CAAC,EAAE;oBACX,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,EAAE;wBACjC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG;wBACrC,CAAC,CAAC,IAAI,CAAC,WAAW;oBACpB,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,QAAQ;oBACR,UAAU;oBACV,OAAO,EAAE,cAAc,CAAC,KAAK,EAAE,UAAU,CAAC;oBAC1C,cAAc,EAAE,IAAI,CAAC,cAAc;oBACnC,GAAG,EAAE,IAAI,CAAC,GAAG;iBACd,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,2DAA2D;QAC3D,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,YAAY,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;QAEpE,+BAA+B;QAC/B,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;YAChC,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC/C,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;gBAC3D,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,IAAI,CAAC,EAAE;oBACX,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,KAAK,EAAE,IAAI,CAAC,WAAW;oBACvB,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,QAAQ;oBACR,UAAU;oBACV,OAAO,EAAE,cAAc,CAAC,KAAK,EAAE,UAAU,CAAC;oBAC1C,cAAc,EAAE,IAAI,CAAC,cAAc;iBACpC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEO,aAAa,CACnB,YAAoB,EACpB,QAAgB,EAChB,KAAe;QAEf,MAAM,QAAQ,GAAmB,EAAE,CAAC;QAEpC,6CAA6C;QAC7C,MAAM,OAAO,GAAG,kFAAkF,CAAC;QACnG,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,IAAI,CAAC;gBACZ,EAAE,EAAE,SAAS;gBACb,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,uBAAuB;gBAC9B,WAAW,EAAE,+BAA+B;gBAC5C,QAAQ;gBACR,UAAU,EAAE,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC;gBACtD,OAAO,EAAE,cAAc,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;gBAC1E,cAAc,EAAE,gBAAgB;gBAChC,GAAG,EAAE,SAAS;aACf,CAAC,CAAC;QACL,CAAC;QAED,kDAAkD;QAClD,MAAM,OAAO,GAAG,+BAA+B,CAAC;QAChD,IAAI,cAAsC,CAAC;QAC3C,OAAO,CAAC,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YAC9D,sEAAsE;YACtE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;YACxD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE,cAAc,CAAC,KAAK,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;YACnG,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YACrD,MAAM,iBAAiB,GACrB,wDAAwD,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzE,IAAI,iBAAiB,EAAE,CAAC;gBACtB,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,SAAS;oBACb,QAAQ,EAAE,QAAQ;oBAClB,KAAK,EAAE,qBAAqB;oBAC5B,WAAW,EAAE,iBAAiB;oBAC9B,QAAQ;oBACR,UAAU,EAAE,cAAc,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC;oBACvD,OAAO,EAAE,cAAc,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;oBAC3E,cAAc,EAAE,kBAAkB;oBAClC,GAAG,EAAE,SAAS;iBACf,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF;AAED,gBAAgB;AAEhB,SAAS,cAAc,CAAC,KAAe,EAAE,SAAiB;IACxD,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,iBAAiB;QAC7C,IAAI,GAAG,GAAG,SAAS;YAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY;IACjD,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,CAAC;AACtB,CAAC;AAED,SAAS,cAAc,CAAC,KAAe,EAAE,UAAkB;IACzD,MAAM,GAAG,GAAG,UAAU,GAAG,CAAC,CAAC;IAC3B,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,IAAI,KAAK,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAC9C,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/B,OAAO,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Auditor, AuditFinding } from '../auditor.interface.js';
|
|
2
|
+
import type { IntermediateSkill, FormatType } from '../../core/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* MCPScanner (L3)
|
|
5
|
+
*
|
|
6
|
+
* Deep audit for MCP JSON format files.
|
|
7
|
+
* Checks for tool poisoning, tool shadowing, sensitive data exposure,
|
|
8
|
+
* and other MCP-specific risks aligned with OWASP MCP Top 10.
|
|
9
|
+
*/
|
|
10
|
+
export declare class MCPScanner implements Auditor {
|
|
11
|
+
readonly id = "mcp-scanner";
|
|
12
|
+
readonly name = "MCP \u4E13\u9879\u626B\u63CF\u5668 (L3)";
|
|
13
|
+
readonly description = "\u6DF1\u5EA6\u5BA1\u8BA1 MCP JSON \u914D\u7F6E\u4E2D\u7684\u5B89\u5168\u98CE\u9669";
|
|
14
|
+
readonly supportedFormats: FormatType[];
|
|
15
|
+
audit(skill: IntermediateSkill, filePath: string): AuditFinding[];
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=mcp-scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-scanner.d.ts","sourceRoot":"","sources":["../../../src/audit/scanner/mcp-scanner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEzE;;;;;;GAMG;AACH,qBAAa,UAAW,YAAW,OAAO;IACxC,QAAQ,CAAC,EAAE,iBAAiB;IAC5B,QAAQ,CAAC,IAAI,6CAAoB;IACjC,QAAQ,CAAC,WAAW,wFAA4B;IAEhD,QAAQ,CAAC,gBAAgB,EAAE,UAAU,EAAE,CAAgB;IAEvD,KAAK,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,EAAE;CAwLlE"}
|