@elmoxbt/agentskillguard 0.1.0 → 0.1.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/README.md +23 -138
- package/dist/rules/rules.js +1 -1
- package/package.json +1 -1
- package/src/rules/rules.ts +1 -1
package/README.md
CHANGED
|
@@ -1,180 +1,65 @@
|
|
|
1
1
|
# AgentSkillGuard
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A static security scanner for AI agent tools (MCP servers, plugins, skills) before they're granted access to a Solana wallet.
|
|
4
4
|
|
|
5
|
-
Agents increasingly install
|
|
6
|
-
|
|
7
|
-
Think `npm audit` + ClamAV, scoped to the specific things an agent tool can do to a Solana wallet.
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
skillguard scan ./my-agent-tool --policy policies/example-swap-agent.yaml
|
|
11
|
-
```
|
|
5
|
+
Agents increasingly install third-party tools. AgentSkillGuard's thesis: the tool supply chain is part of the attack surface, not just the wallet itself. It scans a tool's source for dangerous capabilities, checks them against a YAML permission manifest, and returns a clear **ALLOW / WARN / BLOCK** verdict.
|
|
12
6
|
|
|
13
7
|
```
|
|
14
|
-
|
|
15
|
-
target: /home/user/my-agent-tool
|
|
16
|
-
files scanned: 3
|
|
17
|
-
|
|
18
|
-
Capabilities detected:
|
|
19
|
-
[x] CRITICAL wallet.private_key_access — Tool references private key / secret / mnemonic material directly.
|
|
20
|
-
index.js:12 const secretKey = process.env.WALLET_PRIVATE_KEY;
|
|
21
|
-
[x] CRITICAL system.shell_exec — Tool can execute OS shell commands via child_process.
|
|
22
|
-
index.js:34 exec('curl -s ' + relayHost + '/beacon');
|
|
23
|
-
[x] CRITICAL code.dynamic_load — Tool loads or executes code dynamically at runtime.
|
|
24
|
-
index.js:30 eval(Buffer.from(patch, 'base64').toString());
|
|
25
|
-
...
|
|
26
|
-
|
|
27
|
-
Policy violations:
|
|
28
|
-
[!] CRITICAL wallet.private_key_access is never permitted, regardless of policy
|
|
29
|
-
[!] CRITICAL system.shell_exec is never permitted, regardless of policy
|
|
30
|
-
[!] CRITICAL code.dynamic_load is never permitted, regardless of policy
|
|
31
|
-
|
|
32
|
-
Recommended action: BLOCK
|
|
33
|
-
3 critical violation(s): wallet.private_key_access, system.shell_exec, code.dynamic_load.
|
|
8
|
+
npx @elmoxbt/agentskillguard scan ./my-agent-tool --policy policies/example-swap-agent.yaml
|
|
34
9
|
```
|
|
35
10
|
|
|
36
|
-
|
|
11
|
+
- **Repo:** https://github.com/elmoxbt/agentskillguard
|
|
12
|
+
- **npm:** https://www.npmjs.com/package/@elmoxbt/agentskillguard
|
|
37
13
|
|
|
38
14
|
## How it works
|
|
39
15
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
└─────────┬──────────┘
|
|
45
|
-
│
|
|
46
|
-
┌──────────▼──────────┐
|
|
47
|
-
policy.yml → │ policy enforcer │ → violations (findings checked against
|
|
48
|
-
│ (capability manifest│ the manifest + a hard floor of
|
|
49
|
-
│ + hard floor) │ never-allowed capabilities)
|
|
50
|
-
└─────────┬──────────┘
|
|
51
|
-
│
|
|
52
|
-
┌──────────▼──────────┐
|
|
53
|
-
│ verdict engine │ → ALLOW / WARN / BLOCK
|
|
54
|
-
└──────────────────────┘
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
1. **Static analyzer** (`src/scanner/staticAnalyzer.ts`) walks every `.js`/`.ts` file in the target and runs a rule set (`src/rules/rules.ts`) against it line-by-line, tagging matches with a **capability** (e.g. `wallet.signing`, `system.shell_exec`) and severity.
|
|
58
|
-
2. **Policy enforcer** (`src/policy/enforcer.ts`) loads a YAML **capability manifest** and checks detected capabilities against it. A small set of capabilities (private-key access, shell exec, dynamic code loading, obfuscated execution, unlimited approvals) are an **always-blocked floor** — no policy can grant them.
|
|
59
|
-
3. **Verdict engine** (`src/report/verdict.ts`) turns violations into a single recommendation: `BLOCK` (critical violation), `WARN` (needs manual review), or `ALLOW`.
|
|
60
|
-
4. Every scan is optionally persisted to a local SQLite database (`~/.agentskillguard/scans.db`) so you can build a `skillguard history` audit trail across every tool you've ever vetted.
|
|
61
|
-
|
|
62
|
-
### Detected capabilities
|
|
63
|
-
|
|
64
|
-
| Capability | Checks for | Severity |
|
|
65
|
-
|---|---|---|
|
|
66
|
-
| `wallet.signing` | Can the tool sign transactions/messages? | CRITICAL |
|
|
67
|
-
| `wallet.private_key_access` | Does it touch raw private keys / mnemonics? | CRITICAL |
|
|
68
|
-
| `network.http` | Does it make outbound HTTP requests? | MEDIUM |
|
|
69
|
-
| `network.dynamic_url` | Are request URLs built dynamically (unverifiable)? | HIGH |
|
|
70
|
-
| `solana.tx_modification` | Does it mutate transaction instructions/destinations? | HIGH |
|
|
71
|
-
| `system.shell_exec` | Can it run OS shell commands? | CRITICAL |
|
|
72
|
-
| `code.dynamic_load` | Does it `eval`/`new Function`/dynamic `require`? | CRITICAL |
|
|
73
|
-
| `solana.unlimited_approval` | Does it request unlimited token approvals? | HIGH |
|
|
74
|
-
| `system.env_read` | Does it read `process.env`? | MEDIUM |
|
|
75
|
-
| `code.obfuscation` | Does it decode+execute base64/hex payloads? | CRITICAL |
|
|
76
|
-
| `system.filesystem_write` | Does it write/delete files? | MEDIUM |
|
|
16
|
+
1. **Static analyzer** — walks the target's source files and flags capabilities like wallet signing, private key access, shell execution, dynamic code loading, unlimited token approvals, and arbitrary network calls.
|
|
17
|
+
2. **Policy enforcer** — checks detected capabilities against a YAML capability manifest. A fixed set of capabilities (private-key access, shell exec, `eval`/dynamic code, obfuscated execution, unlimited approvals) are never permitted, regardless of policy.
|
|
18
|
+
3. **Verdict engine** — combines the results into a single recommendation, with CI-friendly exit codes (`0` ALLOW, `1` WARN, `2` BLOCK).
|
|
19
|
+
4. Scans are optionally logged to a local history so you can audit everything you've vetted over time.
|
|
77
20
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
## The capability manifest
|
|
21
|
+
## Capability manifest
|
|
81
22
|
|
|
82
23
|
```yaml
|
|
83
24
|
name: swap-agent
|
|
84
|
-
description: "Allows a swap tool to route trades through Jupiter and call its price API."
|
|
85
|
-
|
|
86
25
|
permissions:
|
|
87
26
|
solana:
|
|
88
|
-
programs:
|
|
89
|
-
- Jupiter
|
|
27
|
+
programs: [Jupiter]
|
|
90
28
|
max_sol: 0.5
|
|
91
|
-
tokens:
|
|
92
|
-
- USDC
|
|
93
|
-
|
|
29
|
+
tokens: [USDC]
|
|
94
30
|
network:
|
|
95
|
-
domains:
|
|
96
|
-
- api.jup.ag
|
|
97
|
-
- quote-api.jup.ag
|
|
98
|
-
|
|
31
|
+
domains: [api.jup.ag]
|
|
99
32
|
wallet:
|
|
100
33
|
signing: true
|
|
101
34
|
max_transactions_per_hour: 10
|
|
102
35
|
```
|
|
103
36
|
|
|
104
|
-
The scanner statically enforces `wallet.signing`, `network.domains`, and `solana.programs`. `max_sol` and `max_transactions_per_hour` describe the intended **runtime** ceiling — static analysis can prove a tool is *capable* of signing, but it can't prove what amount it will sign at runtime. See Roadmap below.
|
|
105
|
-
|
|
106
37
|
## Install & run
|
|
107
38
|
|
|
108
39
|
```bash
|
|
109
40
|
npm install
|
|
110
41
|
npm run build
|
|
111
42
|
|
|
112
|
-
|
|
113
|
-
npm run scan:demo:
|
|
114
|
-
|
|
115
|
-
# scan the bundled "safe" fixture — should ALLOW
|
|
116
|
-
npm run scan:demo:safe
|
|
43
|
+
npm run scan:demo:malicious # bundled fixture — should BLOCK
|
|
44
|
+
npm run scan:demo:safe # bundled fixture — should ALLOW
|
|
117
45
|
|
|
118
|
-
# scan any tool on your machine
|
|
119
46
|
node dist/cli.js scan ./path/to/tool --policy policies/example-swap-agent.yaml
|
|
120
|
-
|
|
121
|
-
# scan without a policy — only the hard-blocked floor applies
|
|
122
|
-
node dist/cli.js scan ./path/to/tool
|
|
123
|
-
|
|
124
|
-
# machine-readable output (for CI / pre-install hooks)
|
|
125
47
|
node dist/cli.js scan ./path/to/tool --json
|
|
126
|
-
|
|
127
|
-
# generate a starter deny-by-default policy
|
|
128
|
-
node dist/cli.js init-policy -o my-tool.policy.yaml
|
|
129
|
-
|
|
130
|
-
# see everything you've scanned so far
|
|
131
48
|
node dist/cli.js history
|
|
132
49
|
```
|
|
133
50
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
Run the smoke tests directly with `npm test` (no test framework, just assertions — see `test/scanner.test.ts`).
|
|
137
|
-
|
|
138
|
-
## Project structure
|
|
51
|
+
Or, once published, use it directly without cloning:
|
|
139
52
|
|
|
140
|
-
```
|
|
141
|
-
agentskillguard/
|
|
142
|
-
├── src/
|
|
143
|
-
│ ├── cli.ts # commander-based CLI entry point
|
|
144
|
-
│ ├── types.ts # shared types (Finding, Violation, ScanResult...)
|
|
145
|
-
│ ├── rules/rules.ts # the rule set — add new detection rules here
|
|
146
|
-
│ ├── scanner/
|
|
147
|
-
│ │ ├── index.ts # orchestrates a scan end-to-end
|
|
148
|
-
│ │ ├── staticAnalyzer.ts # line-based rule matching + URL extraction
|
|
149
|
-
│ │ └── mcpMetadata.ts # reads package.json / mcp.json for tool identity
|
|
150
|
-
│ ├── policy/
|
|
151
|
-
│ │ ├── schema.ts # zod schema for the YAML capability manifest
|
|
152
|
-
│ │ ├── loader.ts # loads + validates a policy file
|
|
153
|
-
│ │ └── enforcer.ts # findings + policy -> violations
|
|
154
|
-
│ ├── report/
|
|
155
|
-
│ │ ├── verdict.ts # violations -> ALLOW / WARN / BLOCK
|
|
156
|
-
│ │ └── formatter.ts # colored CLI report
|
|
157
|
-
│ ├── db/store.ts # SQLite scan history (better-sqlite3)
|
|
158
|
-
│ └── utils/fileWalker.ts # recursive source-file discovery
|
|
159
|
-
├── policies/example-swap-agent.yaml
|
|
160
|
-
├── examples/
|
|
161
|
-
│ ├── malicious-tool/ # fixture that trips nearly every rule (inert — do not run)
|
|
162
|
-
│ └── safe-tool/ # fixture that scans clean
|
|
163
|
-
└── test/scanner.test.ts # smoke test asserting both fixtures verdict correctly
|
|
53
|
+
```bash
|
|
54
|
+
npx @elmoxbt/agentskillguard scan ./path/to/tool
|
|
164
55
|
```
|
|
165
56
|
|
|
166
|
-
## Scope
|
|
57
|
+
## Scope
|
|
167
58
|
|
|
168
|
-
This is
|
|
59
|
+
This is an MVP: detection is regex/line-based rather than a full AST, so it can be evaded by sufficiently obfuscated code, and runtime limits (`max_sol`, `max_transactions_per_hour`) are captured in the manifest but require a runtime enforcement layer to actually enforce — static analysis can only confirm a tool is *capable* of exceeding them.
|
|
169
60
|
|
|
170
|
-
|
|
171
|
-
- **Static analysis can't enforce runtime limits.** `max_sol` and `max_transactions_per_hour` in the manifest are real intents but need a runtime wrapper (a proxy the agent calls through, which meters actual transaction amounts/frequency against the manifest) to actually enforce — the scanner can only flag that a tool *is capable* of exceeding them if it doesn't hard-code such limits itself.
|
|
172
|
-
- **No sandboxed dynamic analysis yet.** A tool that decrypts its payload only at runtime (fetched from a CDN post-install, key derived from an unpredictable seed) won't be caught by source scanning at all. That needs an actual sandboxed execution pass — out of scope for the MVP.
|
|
61
|
+
**Roadmap:** Tree-sitter-based AST analysis, a runtime enforcement proxy for `web3.js` calls, and a hash database of previously scanned malicious tools.
|
|
173
62
|
|
|
174
|
-
##
|
|
63
|
+
## License
|
|
175
64
|
|
|
176
|
-
|
|
177
|
-
- [ ] Runtime enforcement proxy: wraps `@solana/web3.js` calls so `max_sol` / `max_transactions_per_hour` are enforced live, not just requested
|
|
178
|
-
- [ ] MCP-native scanning: intercept tool registration at the protocol level and scan before an agent is ever allowed to call it
|
|
179
|
-
- [ ] Signature/hash database of previously-scanned malicious tools (the "ClamAV" half of the pitch)
|
|
180
|
-
- [ ] `skillguard watch` — scan on every `npm install` in an agent's tool directory
|
|
65
|
+
MIT
|
package/dist/rules/rules.js
CHANGED
|
@@ -56,7 +56,7 @@ exports.RULES = [
|
|
|
56
56
|
capability: 'system.shell_exec',
|
|
57
57
|
severity: 'CRITICAL',
|
|
58
58
|
description: 'Tool can execute OS shell commands via child_process.',
|
|
59
|
-
pattern: /require\(\s*['"]child_process['"]\s*\)|\b(execSync|spawnSync|exec|spawn|fork)\s*\(/,
|
|
59
|
+
pattern: /require\(\s*['"]child_process['"]\s*\)|\b(?:child_process|cp)\.(?:exec|execSync|spawn|spawnSync|fork)\s*\(|(?<!\.)\b(?:execSync|spawnSync|exec|spawn|fork)\s*\(/,
|
|
60
60
|
recommendation: 'No legitimate Solana/agent tool needs shell access. Treat as an automatic block.',
|
|
61
61
|
},
|
|
62
62
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elmoxbt/agentskillguard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "AgentSkillGuard — a static security scanner for AI agent tools (MCP tools, plugins, skills) before they are allowed to touch a Solana wallet.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": { "access": "public" },
|
package/src/rules/rules.ts
CHANGED
|
@@ -64,7 +64,7 @@ export const RULES: Rule[] = [
|
|
|
64
64
|
capability: 'system.shell_exec',
|
|
65
65
|
severity: 'CRITICAL',
|
|
66
66
|
description: 'Tool can execute OS shell commands via child_process.',
|
|
67
|
-
pattern: /require\(\s*['"]child_process['"]\s*\)|\b(execSync|spawnSync|exec|spawn|fork)\s*\(/,
|
|
67
|
+
pattern: /require\(\s*['"]child_process['"]\s*\)|\b(?:child_process|cp)\.(?:exec|execSync|spawn|spawnSync|fork)\s*\(|(?<!\.)\b(?:execSync|spawnSync|exec|spawn|fork)\s*\(/,
|
|
68
68
|
recommendation: 'No legitimate Solana/agent tool needs shell access. Treat as an automatic block.',
|
|
69
69
|
},
|
|
70
70
|
{
|