claude-skill-antivirus 2.0.0 → 2.0.1
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/package.json +1 -1
- package/src/index.js +1 -1
- package/src/scanner/permissions.js +132 -49
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -14,7 +14,7 @@ const program = new Command();
|
|
|
14
14
|
program
|
|
15
15
|
.name('skill-install')
|
|
16
16
|
.description('A secure Claude Skills installer with malicious operation detection')
|
|
17
|
-
.version('2.0.
|
|
17
|
+
.version('2.0.1');
|
|
18
18
|
|
|
19
19
|
program
|
|
20
20
|
.argument('<source>', 'Skill URL (SkillsMP link) or local path')
|
|
@@ -3,47 +3,86 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export class PermissionScanner {
|
|
5
5
|
constructor() {
|
|
6
|
-
//
|
|
6
|
+
// Bash-specific risk patterns (checked in order, first match wins)
|
|
7
|
+
// Note: These are CAPABILITY warnings only. Actual malicious commands
|
|
8
|
+
// are detected by DangerousCommandScanner with higher severity.
|
|
9
|
+
this.bashRiskPatterns = [
|
|
10
|
+
// HIGH - Unrestricted (capability warning)
|
|
11
|
+
{ pattern: /^Bash\(\*\)$/i, risk: 'high', desc: 'Unrestricted shell access (capability)' },
|
|
12
|
+
|
|
13
|
+
// MEDIUM - Potentially dangerous operations (capability warning)
|
|
14
|
+
{ pattern: /^Bash\(rm[\s:]/i, risk: 'medium', desc: 'Delete operations (capability)' },
|
|
15
|
+
{ pattern: /^Bash\(sudo[\s:]/i, risk: 'medium', desc: 'Sudo operations (capability)' },
|
|
16
|
+
{ pattern: /^Bash\(chmod[\s:]/i, risk: 'medium', desc: 'Permission changes (capability)' },
|
|
17
|
+
{ pattern: /^Bash\(chown[\s:]/i, risk: 'medium', desc: 'Ownership changes (capability)' },
|
|
18
|
+
{ pattern: /^Bash\(curl[\s:]/i, risk: 'medium', desc: 'Network requests (capability)' },
|
|
19
|
+
{ pattern: /^Bash\(wget[\s:]/i, risk: 'medium', desc: 'Network requests (capability)' },
|
|
20
|
+
{ pattern: /^Bash\(nc[\s:]/i, risk: 'medium', desc: 'Netcat operations (capability)' },
|
|
21
|
+
{ pattern: /^Bash\(ssh[\s:]/i, risk: 'medium', desc: 'SSH operations (capability)' },
|
|
22
|
+
{ pattern: /^Bash\(scp[\s:]/i, risk: 'medium', desc: 'SCP operations (capability)' },
|
|
23
|
+
|
|
24
|
+
// LOW - Common dev tools with wildcards
|
|
25
|
+
{ pattern: /^Bash\(git:\*\)$/i, risk: 'low', desc: 'Git operations (all)' },
|
|
26
|
+
{ pattern: /^Bash\(npm:\*\)$/i, risk: 'low', desc: 'NPM operations (all)' },
|
|
27
|
+
{ pattern: /^Bash\(pnpm:\*\)$/i, risk: 'low', desc: 'PNPM operations (all)' },
|
|
28
|
+
{ pattern: /^Bash\(yarn:\*\)$/i, risk: 'low', desc: 'Yarn operations (all)' },
|
|
29
|
+
{ pattern: /^Bash\(pip:\*\)$/i, risk: 'low', desc: 'PIP operations (all)' },
|
|
30
|
+
{ pattern: /^Bash\(gh:\*\)$/i, risk: 'low', desc: 'GitHub CLI (all)' },
|
|
31
|
+
{ pattern: /^Bash\(docker:\*\)$/i, risk: 'low', desc: 'Docker operations (all)' },
|
|
32
|
+
{ pattern: /^Bash\(make:\*\)$/i, risk: 'low', desc: 'Make operations (all)' },
|
|
33
|
+
|
|
34
|
+
// INFO - Specific safe commands (just informational)
|
|
35
|
+
{ pattern: /^Bash\(git\s+status\)/i, risk: 'info', desc: 'Git status (read-only)' },
|
|
36
|
+
{ pattern: /^Bash\(git\s+log\)/i, risk: 'info', desc: 'Git log (read-only)' },
|
|
37
|
+
{ pattern: /^Bash\(git\s+diff\)/i, risk: 'info', desc: 'Git diff (read-only)' },
|
|
38
|
+
{ pattern: /^Bash\(git\s+branch\)/i, risk: 'info', desc: 'Git branch (read-only)' },
|
|
39
|
+
{ pattern: /^Bash\(npm\s+test\)/i, risk: 'info', desc: 'NPM test' },
|
|
40
|
+
{ pattern: /^Bash\(npm\s+run\)/i, risk: 'info', desc: 'NPM run script' },
|
|
41
|
+
{ pattern: /^Bash\(ls[\s\)]/i, risk: 'info', desc: 'List directory' },
|
|
42
|
+
{ pattern: /^Bash\(pwd\)/i, risk: 'info', desc: 'Print working directory' },
|
|
43
|
+
{ pattern: /^Bash\(echo[\s\)]/i, risk: 'info', desc: 'Echo command' },
|
|
44
|
+
{ pattern: /^Bash\(cat[\s\)]/i, risk: 'info', desc: 'Cat file (read)' },
|
|
45
|
+
|
|
46
|
+
// LOW - Other Bash with specific scope (has parentheses but not matched above)
|
|
47
|
+
{ pattern: /^Bash\([^)]+\)$/i, risk: 'low', desc: 'Bash with specific scope' },
|
|
48
|
+
|
|
49
|
+
// MEDIUM - Bare "Bash" without scope (unspecified)
|
|
50
|
+
{ pattern: /^Bash$/i, risk: 'medium', desc: 'Unscoped Bash access (capability)' },
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
// Define tool risk levels (non-Bash tools)
|
|
54
|
+
// Note: These are CAPABILITY warnings only - lower severity than actual malicious content
|
|
7
55
|
this.toolRiskLevels = {
|
|
8
|
-
// Critical
|
|
9
|
-
critical: [
|
|
10
|
-
'Bash(*)', // Unrestricted bash access
|
|
11
|
-
'Bash', // General bash (depends on context)
|
|
12
|
-
'Execute',
|
|
13
|
-
'Shell',
|
|
14
|
-
'Terminal'
|
|
15
|
-
],
|
|
56
|
+
// Critical - reserved for actual malicious content (not capabilities)
|
|
57
|
+
critical: [],
|
|
16
58
|
|
|
17
|
-
// High risk
|
|
59
|
+
// High risk capabilities - unrestricted execution
|
|
18
60
|
high: [
|
|
61
|
+
'Execute', // Arbitrary code execution
|
|
62
|
+
'Shell', // Shell access
|
|
63
|
+
'Terminal' // Terminal access
|
|
64
|
+
],
|
|
65
|
+
|
|
66
|
+
// Medium risk capabilities - can make changes
|
|
67
|
+
medium: [
|
|
19
68
|
'Write', // Can write to any file
|
|
20
69
|
'Edit', // Can modify any file
|
|
21
|
-
'Delete',
|
|
22
|
-
'Bash(rm:*)', // Delete operations
|
|
23
|
-
'Bash(chmod:*)', // Permission changes
|
|
24
|
-
'Bash(chown:*)', // Ownership changes
|
|
25
|
-
'Bash(sudo:*)', // Sudo operations
|
|
26
|
-
'Bash(curl:*)', // Network requests
|
|
27
|
-
'Bash(wget:*)', // Download operations
|
|
70
|
+
'Delete', // Can delete files
|
|
28
71
|
'WebFetch', // External web requests
|
|
29
72
|
'mcp_*' // MCP tools (depends on implementation)
|
|
30
73
|
],
|
|
31
74
|
|
|
32
|
-
//
|
|
33
|
-
|
|
75
|
+
// Low risk capabilities - read/discover
|
|
76
|
+
low: [
|
|
34
77
|
'Read', // Can read files
|
|
35
78
|
'Glob', // Can discover files
|
|
36
79
|
'Grep', // Can search file contents
|
|
37
|
-
'Bash(git:*)', // Git operations
|
|
38
|
-
'Bash(npm:*)', // NPM operations
|
|
39
|
-
'Bash(pip:*)', // PIP operations
|
|
40
|
-
'Bash(gh:*)', // GitHub CLI
|
|
41
80
|
'Task', // Can spawn sub-agents
|
|
42
81
|
'TodoWrite' // Task management
|
|
43
82
|
],
|
|
44
83
|
|
|
45
|
-
//
|
|
46
|
-
|
|
84
|
+
// Info - safe tools
|
|
85
|
+
info: [
|
|
47
86
|
'Read(*)', // Read with specific patterns
|
|
48
87
|
'Glob(*)', // Limited glob patterns
|
|
49
88
|
'AskUser',
|
|
@@ -51,49 +90,49 @@ export class PermissionScanner {
|
|
|
51
90
|
]
|
|
52
91
|
};
|
|
53
92
|
|
|
54
|
-
// Dangerous tool combinations
|
|
93
|
+
// Dangerous tool combinations (capability warnings - not actual threats)
|
|
55
94
|
this.dangerousCombinations = [
|
|
56
95
|
{
|
|
57
96
|
tools: ['Bash', 'Write'],
|
|
58
|
-
risk: '
|
|
59
|
-
reason: 'Can execute commands and persist
|
|
97
|
+
risk: 'medium',
|
|
98
|
+
reason: 'Can execute commands and persist files (capability)'
|
|
60
99
|
},
|
|
61
100
|
{
|
|
62
101
|
tools: ['Read', 'WebFetch'],
|
|
63
|
-
risk: '
|
|
64
|
-
reason: 'Can read
|
|
102
|
+
risk: 'medium',
|
|
103
|
+
reason: 'Can read data and send via network (capability)'
|
|
65
104
|
},
|
|
66
105
|
{
|
|
67
106
|
tools: ['Bash(curl:*)', 'Bash'],
|
|
68
|
-
risk: '
|
|
69
|
-
reason: 'Can download and execute remote code'
|
|
107
|
+
risk: 'high',
|
|
108
|
+
reason: 'Can download and execute remote code (capability)'
|
|
70
109
|
},
|
|
71
110
|
{
|
|
72
111
|
tools: ['Glob', 'Read', 'Bash(curl:*)'],
|
|
73
|
-
risk: '
|
|
74
|
-
reason: 'Can discover, read, and
|
|
112
|
+
risk: 'medium',
|
|
113
|
+
reason: 'Can discover, read, and send files (capability)'
|
|
75
114
|
}
|
|
76
115
|
];
|
|
77
116
|
|
|
78
|
-
// Overly permissive patterns
|
|
117
|
+
// Overly permissive patterns (capability warnings)
|
|
79
118
|
this.overlyPermissivePatterns = [
|
|
80
119
|
{
|
|
81
120
|
pattern: /Bash\(\*\)/i,
|
|
82
|
-
risk: '
|
|
83
|
-
title: 'Unrestricted Bash access',
|
|
84
|
-
description: 'Skill
|
|
121
|
+
risk: 'high',
|
|
122
|
+
title: 'Unrestricted Bash access (capability)',
|
|
123
|
+
description: 'Skill declares unrestricted shell access'
|
|
85
124
|
},
|
|
86
125
|
{
|
|
87
126
|
pattern: /Bash\([^)]*\*[^)]*\)/i,
|
|
88
|
-
risk: '
|
|
89
|
-
title: 'Wildcard Bash permissions',
|
|
90
|
-
description: 'Bash permissions use wildcards
|
|
127
|
+
risk: 'medium',
|
|
128
|
+
title: 'Wildcard Bash permissions (capability)',
|
|
129
|
+
description: 'Bash permissions use wildcards'
|
|
91
130
|
},
|
|
92
131
|
{
|
|
93
132
|
pattern: /\*/g,
|
|
94
|
-
risk: '
|
|
133
|
+
risk: 'low',
|
|
95
134
|
title: 'Wildcard in tool permissions',
|
|
96
|
-
description: 'Wildcards in permissions may grant broader access
|
|
135
|
+
description: 'Wildcards in permissions may grant broader access'
|
|
97
136
|
}
|
|
98
137
|
];
|
|
99
138
|
}
|
|
@@ -173,16 +212,58 @@ export class PermissionScanner {
|
|
|
173
212
|
}
|
|
174
213
|
|
|
175
214
|
parseToolString(toolStr) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
215
|
+
// Handle undefined/null
|
|
216
|
+
if (!toolStr) {
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Handle array format (YAML list)
|
|
221
|
+
if (Array.isArray(toolStr)) {
|
|
222
|
+
return toolStr
|
|
223
|
+
.flatMap(t => typeof t === 'string' ? t.split(/[,;]/) : [])
|
|
224
|
+
.map(t => t.trim())
|
|
225
|
+
.filter(t => t.length > 0);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Handle string format
|
|
229
|
+
if (typeof toolStr === 'string') {
|
|
230
|
+
return toolStr
|
|
231
|
+
.split(/[,;]/)
|
|
232
|
+
.map(t => t.trim())
|
|
233
|
+
.filter(t => t.length > 0);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Unknown format, return empty
|
|
237
|
+
return [];
|
|
180
238
|
}
|
|
181
239
|
|
|
182
240
|
analyzeToolRisk(tool, findings) {
|
|
183
241
|
const normalizedTool = tool.trim();
|
|
184
242
|
|
|
185
|
-
//
|
|
243
|
+
// Special handling for Bash tools - use pattern matching
|
|
244
|
+
if (normalizedTool.toLowerCase().startsWith('bash')) {
|
|
245
|
+
for (const { pattern, risk, desc } of this.bashRiskPatterns) {
|
|
246
|
+
if (pattern.test(normalizedTool)) {
|
|
247
|
+
findings[risk].push({
|
|
248
|
+
title: `${risk.toUpperCase()} risk tool: ${normalizedTool}`,
|
|
249
|
+
description: desc,
|
|
250
|
+
location: 'allowed-tools',
|
|
251
|
+
scanner: 'PermissionScanner'
|
|
252
|
+
});
|
|
253
|
+
return; // First match wins
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// No pattern matched - treat as medium (unknown Bash variant)
|
|
257
|
+
findings.medium.push({
|
|
258
|
+
title: `MEDIUM risk tool: ${normalizedTool}`,
|
|
259
|
+
description: 'Bash tool with unknown scope',
|
|
260
|
+
location: 'allowed-tools',
|
|
261
|
+
scanner: 'PermissionScanner'
|
|
262
|
+
});
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Non-Bash tools - check each risk level
|
|
186
267
|
for (const [riskLevel, tools] of Object.entries(this.toolRiskLevels)) {
|
|
187
268
|
for (const riskTool of tools) {
|
|
188
269
|
if (this.toolMatches(normalizedTool, riskTool)) {
|
|
@@ -200,8 +281,10 @@ export class PermissionScanner {
|
|
|
200
281
|
findings.high.push(finding);
|
|
201
282
|
} else if (riskLevel === 'medium') {
|
|
202
283
|
findings.medium.push(finding);
|
|
203
|
-
} else {
|
|
284
|
+
} else if (riskLevel === 'low') {
|
|
204
285
|
findings.low.push(finding);
|
|
286
|
+
} else {
|
|
287
|
+
findings.info.push(finding);
|
|
205
288
|
}
|
|
206
289
|
return; // Found the risk level, stop checking
|
|
207
290
|
}
|