@aimarket/warden 0.9.0 → 0.13.0
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/CHANGELOG.md +36 -1
- package/README-es.md +36 -11
- package/README-fr.md +36 -11
- package/README-ru.md +38 -11
- package/README-zh.md +36 -11
- package/README.md +38 -11
- package/dist/classifier.d.ts +39 -10
- package/dist/classifier.d.ts.map +1 -1
- package/dist/classifier.js +241 -45
- package/dist/classifier.js.map +1 -1
- package/dist/encoded-text.d.ts +7 -0
- package/dist/encoded-text.d.ts.map +1 -0
- package/dist/encoded-text.js +63 -0
- package/dist/encoded-text.js.map +1 -0
- package/dist/fold.d.ts +1 -2
- package/dist/fold.d.ts.map +1 -1
- package/dist/fold.js +3 -2
- package/dist/fold.js.map +1 -1
- package/dist/histor.d.ts +39 -4
- package/dist/histor.d.ts.map +1 -1
- package/dist/histor.js +11 -7
- package/dist/histor.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -4
- package/dist/index.js.map +1 -1
- package/dist/mcp-tools.d.ts.map +1 -1
- package/dist/mcp-tools.js +29 -4
- package/dist/mcp-tools.js.map +1 -1
- package/dist/pin-store.d.ts.map +1 -1
- package/dist/pin-store.js +1 -0
- package/dist/pin-store.js.map +1 -1
- package/dist/pinning.d.ts +1 -1
- package/dist/pinning.d.ts.map +1 -1
- package/dist/pinning.js +12 -4
- package/dist/pinning.js.map +1 -1
- package/dist/result-screen.d.ts +19 -0
- package/dist/result-screen.d.ts.map +1 -0
- package/dist/result-screen.js +86 -0
- package/dist/result-screen.js.map +1 -0
- package/dist/scan-lock.d.ts.map +1 -1
- package/dist/scan-lock.js +2 -1
- package/dist/scan-lock.js.map +1 -1
- package/dist/scan-report.d.ts.map +1 -1
- package/dist/scan-report.js +15 -0
- package/dist/scan-report.js.map +1 -1
- package/dist/scan.d.ts +40 -0
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +245 -25
- package/dist/scan.js.map +1 -1
- package/dist/static-scan.d.ts +4 -3
- package/dist/static-scan.d.ts.map +1 -1
- package/dist/static-scan.js +40 -9
- package/dist/static-scan.js.map +1 -1
- package/dist/toxic-flow.d.ts +33 -0
- package/dist/toxic-flow.d.ts.map +1 -0
- package/dist/toxic-flow.js +117 -0
- package/dist/toxic-flow.js.map +1 -0
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/wrap-cli.d.ts +3 -0
- package/dist/wrap-cli.d.ts.map +1 -1
- package/dist/wrap-cli.js +17 -6
- package/dist/wrap-cli.js.map +1 -1
- package/dist/wrap-state.js +2 -2
- package/dist/wrap-state.js.map +1 -1
- package/dist/wrap.d.ts.map +1 -1
- package/dist/wrap.js +20 -2
- package/dist/wrap.js.map +1 -1
- package/docs/scan.es.md +29 -14
- package/docs/scan.fr.md +29 -14
- package/docs/scan.md +29 -14
- package/docs/scan.ru.md +29 -14
- package/docs/scan.zh.md +29 -14
- package/docs/scanner-comparison.es.md +3 -12
- package/docs/scanner-comparison.fr.md +3 -13
- package/docs/scanner-comparison.md +3 -12
- package/docs/scanner-comparison.ru.md +3 -12
- package/docs/scanner-comparison.zh.md +3 -7
- package/docs/security-hardening-language-boundary.md +306 -0
- package/package.json +6 -3
- package/scripts/check-quality-gate.mjs +64 -0
- package/scripts/quality-public-key.pem +3 -0
package/docs/scan.es.md
CHANGED
|
@@ -5,18 +5,19 @@
|
|
|
5
5
|
`warden-mcp scan` lee la configuración MCP que ya tienes, se conecta a cada servidor que inicia y revisa las definiciones de herramientas antes de que un modelo las vea. Es la misma cadena de compuertas que [`wrap`](../README-es.md) y la [biblioteca](integration.es.md), ejecutada una vez sobre tus configuraciones en lugar de delante de un solo servidor.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npx -y @aimarket/warden@0.
|
|
8
|
+
npx -y @aimarket/warden@0.11.0 scan
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
```text
|
|
12
|
-
WARDEN scan 0.
|
|
12
|
+
WARDEN scan 0.10.0 · ruleset 10 sha256-lJuKKKV5mtru… · block at high
|
|
13
13
|
read .mcp.json (claude-code, 3 servers)
|
|
14
14
|
|
|
15
|
-
✓ allow notes
|
|
16
|
-
✗ BLOCK evil-notes
|
|
17
|
-
! error broken
|
|
15
|
+
✓ allow notes claude-code 1 tool · score 0.90
|
|
16
|
+
✗ BLOCK evil-notes claude-code 1 tool · score 0.00 · TOOL_DEF_EXFIL(notes) TOOL_DEF_SECRET_REQUEST(notes) THREAT_SSH_KEY_READ(notes)
|
|
17
|
+
! error broken claude-code could not start: spawn /nonexistent/bin/server ENOENT
|
|
18
18
|
|
|
19
19
|
3 servers: 1 allowed, 1 blocked, 1 not checked, 0 skipped.
|
|
20
|
+
Details: warden-mcp scan --json, or --markdown FILE. To review a changed server: warden-mcp scan --lock warden.lock.json --update-lock.
|
|
20
21
|
```
|
|
21
22
|
|
|
22
23
|
Sin cuenta, sin clave de API, sin modelo. El único tráfico de red va a los servidores de tu configuración y, si pasas `--histor`, al registro HISTOR.
|
|
@@ -85,19 +86,21 @@ jobs:
|
|
|
85
86
|
runs-on: ubuntu-latest
|
|
86
87
|
steps:
|
|
87
88
|
- uses: actions/checkout@v4
|
|
88
|
-
- uses: alexar76/warden@v0.
|
|
89
|
+
- uses: alexar76/warden@v0.11.0
|
|
89
90
|
with:
|
|
90
91
|
upload-sarif: 'true'
|
|
91
92
|
```
|
|
92
93
|
|
|
93
94
|
Por defecto lee los archivos del proyecto, no inicia servidores stdio (ese programa lo elige el pull request), rechaza direcciones no públicas, usa `warden.lock.json` si existe, escribe el resumen del job y falla ante un servidor bloqueado. Entradas: `config`, `working-directory`, `lock`, `launch-stdio`, `public-only`, `fail-on`, `histor`, `classifier-url`, `classifier-model`, `classifier-blocks`, `sarif`, `upload-sarif`, `version`. Salidas: `blocked`, `servers`, `sarif`. En workflows de producción, fija la acción por SHA de commit.
|
|
94
95
|
|
|
96
|
+
Para enterarte de cuándo cambia un servidor que usas, ejecuta también el mismo job con un horario (`on: { schedule: [{ cron: '17 6 * * *' }] }`): con un `warden.lock.json` en el repositorio, un servidor cuyas definiciones de herramientas cambiaron hace fallar la ejecución con `TOOL_DEF_DRIFT` y el resumen muestra el diff.
|
|
97
|
+
|
|
95
98
|
## pre-commit
|
|
96
99
|
|
|
97
100
|
```yaml
|
|
98
101
|
repos:
|
|
99
102
|
- repo: https://github.com/alexar76/warden
|
|
100
|
-
rev: v0.
|
|
103
|
+
rev: v0.10.0
|
|
101
104
|
hooks:
|
|
102
105
|
- id: warden-scan # configuraciones modificadas; no inicia servidores stdio
|
|
103
106
|
- id: warden-lock # el proyecto coincide con warden.lock.json; inicia servidores stdio
|
|
@@ -114,11 +117,23 @@ Ambos ejecutan el paquete publicado mediante `npx`, así que necesitas Node 20 o
|
|
|
114
117
|
|
|
115
118
|
Al iniciar la sesión analiza los servidores que Claude Code inicia para el proyecto, te dice cuáles están bloqueados y al modelo solo le da el nombre y el código del hallazgo. Una descripción bloqueada nunca entra en el contexto del modelo. Después, un hook `PreToolUse` deniega las llamadas a herramientas de un servidor bloqueado o a una herramienta bloqueada; lee un archivo pequeño y no inicia nada. Detalles y límites: [claude-plugin/README.md](../claude-plugin/README.md).
|
|
116
119
|
|
|
120
|
+
## Flujos tóxicos: seguros uno a uno, peligrosos juntos
|
|
121
|
+
|
|
122
|
+
Un cliente entrega las herramientas de todos sus servidores a un mismo modelo. Si entre ellos hay una herramienta que lee tus datos privados, otra por la que entra texto de fuera (una página web, una issue, un correo) y otra que puede enviar datos fuera, el texto que llega por la segunda puede pedirle al modelo que lea con la primera y envíe con la tercera. Ningún servidor tiene la culpa, así que ningún veredicto por servidor lo muestra. `scan` lo informa por cliente:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
⚠ toxic flow claude-code can read private data (filesystem.read_file, …), take in text from outside (fetch.fetch) and send data out (fetch.fetch). …
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Las capacidades se leen de los nombres de las herramientas y de sus parámetros (`read_file`, `send_email`, un parámetro `url` o `to`), no de las descripciones: no depende del idioma, y una descripción no puede justificarse. Un flujo es orientativo: nunca bloquea ni cambia el código de salida. Aparece en `--json` (`flows`) y en el resumen Markdown. Para romperlo, quita una pata: un servidor `fetch` que no necesitas, o un servidor de archivos limitado a una carpeta en otro cliente.
|
|
129
|
+
|
|
117
130
|
## HISTOR: ¿este servidor te sirve lo mismo que a todos?
|
|
118
131
|
|
|
119
132
|
[HISTOR](https://histor.modelmarket.dev) es un registro público que anota cada día las definiciones de herramientas de todos los servidores remotos del registro oficial de MCP. `--histor` le hace una pregunta por cada servidor remoto: ¿el conjunto de herramientas que te acaban de servir es el que observa HISTOR?
|
|
120
133
|
|
|
121
|
-
Qué se envía: el endpoint, solo esquema, host y ruta (sin query, sin usuario ni contraseña), y el digest [MTL/1](https://github.com/alexar76/awr) del conjunto de herramientas. Ninguna descripción de herramienta, ninguna cabecera
|
|
134
|
+
Qué se envía: el endpoint, solo esquema, host y ruta (sin query, sin usuario ni contraseña), y el digest [MTL/1](https://github.com/alexar76/awr) del conjunto de herramientas. Ninguna descripción de herramienta, ninguna cabecera. Los endpoints en hosts o direcciones privadas, y las rutas que parecen llevar una clave, no se envían; el informe dice por qué.
|
|
135
|
+
|
|
136
|
+
Un servidor stdio que arranca desde el registro público de npm o PyPI (`npx`, `bunx`, `npm exec`, `pnpm dlx`, `uvx`, `uv tool run`, `pipx run`) se identifica por su paquete —`npm:<nombre>` o `pypi:<nombre>`, sin la versión— y el mismo digest. HISTOR instala cada versión publicada de ese paquete en un sandbox gVisor (credenciales señuelo, sin ruta hacia fuera; cada herramienta llamada una vez con argumentos canario, sin actuar sobre nada real) y anota sus herramientas y lo que hizo y anota sus herramientas; así, `previously-observed` significa aquí que ejecutas una versión más antigua, y `different`, una versión a la que HISTOR aún no ha llegado o un paquete que no es el publicado. Una ruta local, una URL de git o de tarball, un registro privado (`--registry`, `--index-url`) o un arranque simple con `node`/`python` nunca se envían. Para un paquete, HISTOR también indica, como hallazgos orientativos, si su nombre imita a uno popular (`HISTOR_PACKAGE_LOOKALIKE`: a un carácter, el mismo nombre bajo otro scope u otros separadores, frente a un paquete descargado al menos 100 veces más) y si la versión observada tiene las marcas de un token de publicación robado: `HISTOR_PACKAGE_PROVENANCE_LOST` (sin compilación atestiguada en CI cuando la versión anterior la tenía), `HISTOR_PACKAGE_INSTALL_SCRIPTS` (nuevos scripts de instalación), `HISTOR_PACKAGE_PUBLISHER_CHANGED`, `HISTOR_PACKAGE_NEW_DEPENDENCIES`. Y lo que hizo el paquete en el sandbox: `HISTOR_PACKAGE_READS_SECRETS` (abrió credenciales señuelo —claves SSH, tokens de nube o de registros, una wallet— al instalarse, al arrancar o al llamar a una herramienta), `HISTOR_PACKAGE_PERSISTENCE` (escribió donde sobreviviría a la sesión, como `.bashrc` o `authorized_keys`), `HISTOR_PACKAGE_STARTUP_NETWORK` (salió a la red antes de que se llamara a ninguna herramienta), `HISTOR_PACKAGE_STARTS_PROGRAMS`, `HISTOR_PACKAGE_INSTALL_BEHAVIOUR` (lo que hacen sus scripts de instalación). Cargar `.env` del directorio de trabajo y llamar a su propia API durante una llamada no son hallazgos. Con `--histor`, el informe también da un enlace a un feed de HISTOR con los cambios de los servidores escaneados que HISTOR conoce (`historWatch` en `--json`): suscríbete en cualquier lector de feeds.
|
|
122
137
|
|
|
123
138
|
| Respuesta | Significado |
|
|
124
139
|
|---|---|
|
|
@@ -135,13 +150,13 @@ Las reglas de WARDEN funcionan sin conexión y de forma determinista, y no ven l
|
|
|
135
150
|
|
|
136
151
|
```bash
|
|
137
152
|
warden-mcp scan --classifier-url http://localhost:11434/v1 --classifier-model qwen2.5:14b
|
|
138
|
-
WARDEN_CLASSIFIER_API_KEY=… warden-mcp scan --classifier-url https://api.deepseek.com --classifier-model deepseek-flash
|
|
153
|
+
WARDEN_CLASSIFIER_API_KEY=… warden-mcp scan --classifier-url https://api.deepseek.com --classifier-model deepseek-flash --classifier-reasoning-effort none
|
|
139
154
|
```
|
|
140
155
|
|
|
141
|
-
- **
|
|
142
|
-
- **Consultivo
|
|
143
|
-
- **
|
|
144
|
-
- **
|
|
156
|
+
- **Optativo; envía todos los campos publicados.** Nombre, título, descripción, esquemas, anotaciones y extensiones, con una lectura normalizada cuando procede. La clave solo viene de `WARDEN_CLASSIFIER_API_KEY`.
|
|
157
|
+
- **Consultivo por defecto; cierre seguro con `--classifier-blocks`.** Los hallazgos usan `TOOL_DEF_CLASSIFIER`. Truncamiento, incertidumbre, cobertura incompleta, respuestas inválidas, errores y tiempos de espera producen `CLASSIFIER_INCOMPLETE`: son fallos de inspección, no ataques detectados. `--update-lock` no puede aprobarlos.
|
|
158
|
+
- **Cinco categorías.** `instruction_to_model`, `exfiltration`, `secret_request`, `concealment`, `cross_tool`. Se inspecciona la autoridad de las instrucciones en cualquier idioma; la capacidad del modelo varía. El protocolo exige una decisión por herramienta y referencias a campos verificadas por WARDEN, que extrae el texto original. Cada decisión incompleta se reintenta una sola vez de forma aislada. Los delimitadores aleatorios no garantizan inmunidad.
|
|
159
|
+
- **Inspección no es aprobación.** [`wrap --require-approval`](security-hardening-language-boundary.md) exige una aprobación del operador independiente del idioma. HISTOR conserva sus resultados históricos como recomendaciones.
|
|
145
160
|
|
|
146
161
|
Medido el 9 de octubre de 2026 con `deepseek-flash`, el modelo que usa HISTOR, sobre MCPTox (el benchmark publicado que se describe en la comparación de abajo) y sobre nuestros conjuntos:
|
|
147
162
|
|
|
@@ -175,7 +190,7 @@ Las tres últimas filas vienen de MCPTox (Wang et al., AAAI 2026), un benchmark
|
|
|
175
190
|
- **Ten presente qué números están ajustados.** El primer conjunto de ataques lo escribimos nosotros. El ruleset v9 se escribió después de leer ambos conjuntos de ataques, y v10 después de los 22 servidores de MCPTox de la otra mitad. Así que 20 y 10 son la exhaustividad de reglas ajustadas a lo que vieron. La fila reservada de MCPTox, los servidores limpios, el corpus y toda la columna 0.8.2 no están ajustados. Sobre el corpus, v10 bloquea exactamente lo mismo que v8.
|
|
176
191
|
- **MCPTox prueba una forma, no una palabra clave.** Casi todas las herramientas envenenadas le dicen al modelo que llame antes a otra herramienta o que cambie los argumentos de otra. v10 nombra esa forma (`TOOL_DEF_CROSS_TOOL`, ver [la cadena de compuertas](gates.es.md)) y bloquea 171 de las 218 herramientas reservadas, frente a 189 de 267 en la mitad con la que se escribió. Con el prefijo `<IMPORTANT>` o «Ignore the previous instructions» del benchmark, todos los escáneres que ejecutamos las detectan; mcp-shield no lo ejecutamos sobre ese conjunto. En los servidores limpios, mcp-audit bloquea AmapMap y BaiduMap, y mcp-shield bloquea FileSystem, AdFin y Commander por «sensitive file access».
|
|
177
192
|
- **Lo que WARDEN todavía no detecta:** 47 de las 218 herramientas reservadas de MCPTox, una instrucción en chino para leer `.env`, una paráfrasis sin palabras clave («gather every credential file … keep this step to yourself») y «include the output of env». Informa 41 de esas 47, y las tres nuestras, como avisos, no como bloqueos. Necesitan una comprobación por significado, no otra regla: con el clasificador opcional de arriba, 191 de las 218 quedan bloqueadas en `high` y las 218 quedan marcadas.
|
|
178
|
-
- **
|
|
193
|
+
- **WARDEN no está solo en ese hueco.** mcp-audit también tiene archivo lock, modo diff para PR y SARIF.
|
|
179
194
|
|
|
180
195
|
## Lo que scan no hace
|
|
181
196
|
|
package/docs/scan.fr.md
CHANGED
|
@@ -5,18 +5,19 @@
|
|
|
5
5
|
`warden-mcp scan` lit la configuration MCP que vous avez déjà, se connecte à chaque serveur qu’elle démarre et vérifie les définitions d’outils avant qu’un modèle ne les voie. C’est la même chaîne de portes que [`wrap`](../README-fr.md) et la [bibliothèque](integration.fr.md), exécutée une fois sur vos configurations au lieu de se placer devant un seul serveur.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npx -y @aimarket/warden@0.
|
|
8
|
+
npx -y @aimarket/warden@0.11.0 scan
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
```text
|
|
12
|
-
WARDEN scan 0.
|
|
12
|
+
WARDEN scan 0.10.0 · ruleset 10 sha256-lJuKKKV5mtru… · block at high
|
|
13
13
|
read .mcp.json (claude-code, 3 servers)
|
|
14
14
|
|
|
15
|
-
✓ allow notes
|
|
16
|
-
✗ BLOCK evil-notes
|
|
17
|
-
! error broken
|
|
15
|
+
✓ allow notes claude-code 1 tool · score 0.90
|
|
16
|
+
✗ BLOCK evil-notes claude-code 1 tool · score 0.00 · TOOL_DEF_EXFIL(notes) TOOL_DEF_SECRET_REQUEST(notes) THREAT_SSH_KEY_READ(notes)
|
|
17
|
+
! error broken claude-code could not start: spawn /nonexistent/bin/server ENOENT
|
|
18
18
|
|
|
19
19
|
3 servers: 1 allowed, 1 blocked, 1 not checked, 0 skipped.
|
|
20
|
+
Details: warden-mcp scan --json, or --markdown FILE. To review a changed server: warden-mcp scan --lock warden.lock.json --update-lock.
|
|
20
21
|
```
|
|
21
22
|
|
|
22
23
|
Pas de compte, pas de clé d’API, pas de modèle. Le seul trafic réseau va vers les serveurs de votre configuration et, si vous passez `--histor`, vers le journal HISTOR.
|
|
@@ -85,19 +86,21 @@ jobs:
|
|
|
85
86
|
runs-on: ubuntu-latest
|
|
86
87
|
steps:
|
|
87
88
|
- uses: actions/checkout@v4
|
|
88
|
-
- uses: alexar76/warden@v0.
|
|
89
|
+
- uses: alexar76/warden@v0.11.0
|
|
89
90
|
with:
|
|
90
91
|
upload-sarif: 'true'
|
|
91
92
|
```
|
|
92
93
|
|
|
93
94
|
Par défaut, elle lit les fichiers du projet, ne démarre pas les serveurs stdio (c’est la pull request qui choisit ce programme), refuse les adresses non publiques, utilise `warden.lock.json` s’il existe, écrit le résumé du job et échoue sur un serveur bloqué. Entrées : `config`, `working-directory`, `lock`, `launch-stdio`, `public-only`, `fail-on`, `histor`, `classifier-url`, `classifier-model`, `classifier-blocks`, `sarif`, `upload-sarif`, `version`. Sorties : `blocked`, `servers`, `sarif`. Dans les workflows de production, épinglez l’action par SHA de commit.
|
|
94
95
|
|
|
96
|
+
Pour être prévenu quand un serveur que vous utilisez change, lancez aussi le même job selon un horaire (`on: { schedule: [{ cron: '17 6 * * *' }] }`) : avec un `warden.lock.json` versionné, un serveur dont les définitions d’outils ont changé fait échouer l’exécution avec `TOOL_DEF_DRIFT`, et le résumé montre le diff.
|
|
97
|
+
|
|
95
98
|
## pre-commit
|
|
96
99
|
|
|
97
100
|
```yaml
|
|
98
101
|
repos:
|
|
99
102
|
- repo: https://github.com/alexar76/warden
|
|
100
|
-
rev: v0.
|
|
103
|
+
rev: v0.10.0
|
|
101
104
|
hooks:
|
|
102
105
|
- id: warden-scan # configurations modifiées ; ne démarre pas les serveurs stdio
|
|
103
106
|
- id: warden-lock # le projet correspond à warden.lock.json ; démarre les serveurs stdio
|
|
@@ -114,11 +117,23 @@ Les deux exécutent le paquet publié via `npx` : Node 20 ou plus récent doit
|
|
|
114
117
|
|
|
115
118
|
Au début de la session, il analyse les serveurs que Claude Code démarre pour le projet, vous indique lesquels sont bloqués, et ne donne au modèle que le nom et le code du constat. Une description bloquée n’entre jamais dans le contexte du modèle. Ensuite, un hook `PreToolUse` refuse les appels aux outils d’un serveur bloqué ou à un outil bloqué ; il lit un petit fichier et ne démarre rien. Détails et limites : [claude-plugin/README.md](../claude-plugin/README.md).
|
|
116
119
|
|
|
120
|
+
## Flux toxiques : sûrs un par un, dangereux ensemble
|
|
121
|
+
|
|
122
|
+
Un client confie les outils de tous ses serveurs à un même modèle. Si l’un lit vos données privées, si un autre laisse entrer du texte venu de l’extérieur (une page web, une issue, un e-mail) et si un troisième peut envoyer des données dehors, un texte arrivé par le deuxième peut demander au modèle de lire avec le premier et d’envoyer avec le troisième. Aucun serveur n’est en faute : aucun verdict par serveur ne le montre. `scan` le signale par client :
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
⚠ toxic flow claude-code can read private data (filesystem.read_file, …), take in text from outside (fetch.fetch) and send data out (fetch.fetch). …
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Les capacités sont lues dans les noms des outils et de leurs paramètres (`read_file`, `send_email`, un paramètre `url` ou `to`), pas dans les descriptions : la lecture ne dépend pas de la langue, et une description ne peut pas s’en justifier. Un flux est indicatif : il ne bloque jamais et ne change pas le code de sortie. Il figure dans `--json` (`flows`) et dans le résumé Markdown. Pour le casser, retirez une patte : un serveur `fetch` inutile, ou un serveur de fichiers limité à un dossier dans un autre client.
|
|
129
|
+
|
|
117
130
|
## HISTOR : ce serveur vous sert-il ce qu’il sert à tout le monde ?
|
|
118
131
|
|
|
119
132
|
[HISTOR](https://histor.modelmarket.dev) est un journal public qui enregistre chaque jour les définitions d’outils de tous les serveurs distants du registre MCP officiel. `--histor` lui pose une question par serveur distant : l’ensemble d’outils qu’on vient de vous servir est-il celui qu’observe HISTOR ?
|
|
120
133
|
|
|
121
|
-
Ce qui est envoyé : l’endpoint, réduit au schéma, à l’hôte et au chemin (sans query, sans nom d’utilisateur ni mot de passe), et le condensé [MTL/1](https://github.com/alexar76/awr) de l’ensemble d’outils. Aucune description d’outil, aucun en-tête
|
|
134
|
+
Ce qui est envoyé : l’endpoint, réduit au schéma, à l’hôte et au chemin (sans query, sans nom d’utilisateur ni mot de passe), et le condensé [MTL/1](https://github.com/alexar76/awr) de l’ensemble d’outils. Aucune description d’outil, aucun en-tête. Les endpoints sur des hôtes ou adresses privés, et les chemins qui semblent contenir une clé, ne sont pas envoyés du tout ; le rapport dit pourquoi.
|
|
135
|
+
|
|
136
|
+
Un serveur stdio lancé depuis le registre public npm ou PyPI (`npx`, `bunx`, `npm exec`, `pnpm dlx`, `uvx`, `uv tool run`, `pipx run`) est désigné par son paquet — `npm:<nom>` ou `pypi:<nom>`, sans la version — et le même condensé. HISTOR installe chaque version publiée d’un tel paquet dans un bac à sable gVisor (identifiants leurres, sans route vers l’extérieur ; chaque outil appelé une fois avec des arguments canaris, sans agir sur rien de réel) et enregistre ses outils et ce qu’il a fait et enregistre ses outils : ici, `previously-observed` signifie que vous exécutez une version plus ancienne, et `different` une version qu’HISTOR n’a pas encore atteinte ou un paquet qui n’est pas celui publié. Un chemin local, une URL git ou tarball, un registre privé (`--registry`, `--index-url`) ou un simple lancement `node`/`python` n’est jamais envoyé. Pour un paquet, HISTOR indique aussi, en constats indicatifs, si son nom imite celui d’un paquet populaire (`HISTOR_PACKAGE_LOOKALIKE` : à un caractère près, le même nom sous un autre scope ou d’autres séparateurs, face à un paquet téléchargé au moins 100 fois plus) et si la version observée porte les marques d’un jeton de publication volé : `HISTOR_PACKAGE_PROVENANCE_LOST` (pas de compilation attestée en CI alors que la version précédente en avait une), `HISTOR_PACKAGE_INSTALL_SCRIPTS` (nouveaux scripts d’installation), `HISTOR_PACKAGE_PUBLISHER_CHANGED`, `HISTOR_PACKAGE_NEW_DEPENDENCIES`. Et ce que le paquet a fait dans le bac à sable : `HISTOR_PACKAGE_READS_SECRETS` (il a ouvert des identifiants leurres — clés SSH, jetons cloud ou de registre, portefeuille — à l’installation, au démarrage ou lors d’un appel d’outil), `HISTOR_PACKAGE_PERSISTENCE` (il a écrit là où il survivrait à la session, comme `.bashrc` ou `authorized_keys`), `HISTOR_PACKAGE_STARTUP_NETWORK` (il a contacté le réseau avant tout appel d’outil), `HISTOR_PACKAGE_STARTS_PROGRAMS`, `HISTOR_PACKAGE_INSTALL_BEHAVIOUR` (ce que font ses scripts d’installation). Charger `.env` depuis le répertoire de travail et appeler sa propre API pendant un appel ne sont pas des constats. Avec `--histor`, le rapport donne aussi un lien vers un flux HISTOR des changements des serveurs scannés qu’HISTOR connaît (`historWatch` dans `--json`) : abonnez-vous dans n’importe quel lecteur de flux.
|
|
122
137
|
|
|
123
138
|
| Réponse | Signification |
|
|
124
139
|
|---|---|
|
|
@@ -135,13 +150,13 @@ Les règles de WARDEN fonctionnent hors ligne et de façon déterministe, et ell
|
|
|
135
150
|
|
|
136
151
|
```bash
|
|
137
152
|
warden-mcp scan --classifier-url http://localhost:11434/v1 --classifier-model qwen2.5:14b
|
|
138
|
-
WARDEN_CLASSIFIER_API_KEY=… warden-mcp scan --classifier-url https://api.deepseek.com --classifier-model deepseek-flash
|
|
153
|
+
WARDEN_CLASSIFIER_API_KEY=… warden-mcp scan --classifier-url https://api.deepseek.com --classifier-model deepseek-flash --classifier-reasoning-effort none
|
|
139
154
|
```
|
|
140
155
|
|
|
141
|
-
- **
|
|
142
|
-
- **Consultatif
|
|
143
|
-
- **
|
|
144
|
-
- **
|
|
156
|
+
- **Optatif ; transmet tous les champs annoncés.** Nom, titre, description, schémas, annotations et extensions, avec une lecture normalisée si nécessaire. La clé provient uniquement de `WARDEN_CLASSIFIER_API_KEY`.
|
|
157
|
+
- **Consultatif par défaut ; refus si incomplet avec `--classifier-blocks`.** Les résultats utilisent `TOOL_DEF_CLASSIFIER`. Troncature, incertitude, couverture incomplète, réponse invalide, erreur ou délai dépassé donnent `CLASSIFIER_INCOMPLETE`. Ce sont des échecs d’inspection, pas des attaques détectées ; `--update-lock` ne peut pas les approuver.
|
|
158
|
+
- **Cinq catégories.** `instruction_to_model`, `exfiltration`, `secret_request`, `concealment`, `cross_tool`. L’inspection porte sur l’autorité des instructions, dans toute langue, selon les capacités du modèle. Le protocole exige une décision par outil et des références de champs vérifiées par WARDEN, qui extrait le texte source. Chaque décision incomplète est réessayée une seule fois isolément. Les délimiteurs aléatoires ne garantissent pas l’immunité.
|
|
159
|
+
- **Inspection ne vaut pas approbation.** [`wrap --require-approval`](security-hardening-language-boundary.md) exige l’approbation de l’opérateur indépendamment de la langue. Les résultats historiques de HISTOR restent consultatifs.
|
|
145
160
|
|
|
146
161
|
Mesuré le 9 octobre 2026 avec `deepseek-flash`, le modèle qu'utilise HISTOR, sur MCPTox (le benchmark publié décrit dans la comparaison ci-dessous) et sur nos propres jeux :
|
|
147
162
|
|
|
@@ -175,7 +190,7 @@ Les trois dernières lignes viennent de MCPTox (Wang et al., AAAI 2026), un benc
|
|
|
175
190
|
- **Sachez quels chiffres sont ajustés.** Nous avons écrit le premier jeu d’attaques. Le ruleset v9 a été écrit après la lecture des deux jeux d’attaques, et v10 après les 22 serveurs MCPTox de l’autre moitié. 20 et 10 sont donc le rappel de règles ajustées à ce qu’elles ont vu. La ligne MCPTox réservée, les serveurs propres, le corpus et toute la colonne 0.8.2 ne sont pas ajustés. Sur le corpus, v10 bloque exactement ce que bloquait v8.
|
|
176
191
|
- **MCPTox teste une forme, pas un mot-clé.** Presque chaque outil empoisonné dit au modèle d’appeler d’abord un autre outil, ou de changer les arguments d’un autre outil. v10 nomme cette forme (`TOOL_DEF_CROSS_TOOL`, voir [la chaîne de portes](gates.fr.md)) et bloque 171 des 218 outils réservés, contre 189 sur 267 dans la moitié qui a servi à l’écrire. Avec le préfixe `<IMPORTANT>` ou « Ignore the previous instructions » du benchmark, tous les scanners que nous avons lancés les attrapent ; nous n’avons pas lancé mcp-shield sur ce jeu. Sur les serveurs propres, mcp-audit bloque AmapMap et BaiduMap, et mcp-shield bloque FileSystem, AdFin et Commander pour « sensitive file access ».
|
|
177
192
|
- **Ce que WARDEN manque encore :** 47 des 218 outils MCPTox réservés, une consigne en chinois de lire `.env`, une paraphrase sans mot-clé (« gather every credential file … keep this step to yourself ») et « include the output of env ». Il signale 41 de ces 47, et nos trois, à titre consultatif, sans bloquer. Il faut une vérification par le sens, pas une règle de plus : avec le classificateur optionnel ci-dessus, 191 des 218 sont bloqués au niveau `high` et les 218 sont signalés.
|
|
178
|
-
- **
|
|
193
|
+
- **WARDEN n’est pas seul sur ce créneau.** mcp-audit propose lui aussi un fichier lock, un mode diff pour les PR et SARIF.
|
|
179
194
|
|
|
180
195
|
## Ce que scan ne fait pas
|
|
181
196
|
|
package/docs/scan.md
CHANGED
|
@@ -5,18 +5,19 @@
|
|
|
5
5
|
`warden-mcp scan` reads the MCP configuration you already have, connects to every server it starts, and vets the tool definitions before a model sees them. It is the same gate chain as [`wrap`](../README.md#protect-claude-desktop-or-cursor-with-one-wrapper) and the [library](integration.md), run once over your configs instead of in front of one server.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npx -y @aimarket/warden@0.
|
|
8
|
+
npx -y @aimarket/warden@0.11.0 scan
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
```text
|
|
12
|
-
WARDEN scan 0.
|
|
12
|
+
WARDEN scan 0.10.0 · ruleset 10 sha256-lJuKKKV5mtru… · block at high
|
|
13
13
|
read .mcp.json (claude-code, 3 servers)
|
|
14
14
|
|
|
15
|
-
✓ allow notes
|
|
16
|
-
✗ BLOCK evil-notes
|
|
17
|
-
! error broken
|
|
15
|
+
✓ allow notes claude-code 1 tool · score 0.90
|
|
16
|
+
✗ BLOCK evil-notes claude-code 1 tool · score 0.00 · TOOL_DEF_EXFIL(notes) TOOL_DEF_SECRET_REQUEST(notes) THREAT_SSH_KEY_READ(notes)
|
|
17
|
+
! error broken claude-code could not start: spawn /nonexistent/bin/server ENOENT
|
|
18
18
|
|
|
19
19
|
3 servers: 1 allowed, 1 blocked, 1 not checked, 0 skipped.
|
|
20
|
+
Details: warden-mcp scan --json, or --markdown FILE. To review a changed server: warden-mcp scan --lock warden.lock.json --update-lock.
|
|
20
21
|
```
|
|
21
22
|
|
|
22
23
|
No account, no API key, no model. The only network traffic is to the servers in your config, and to the HISTOR log if you pass `--histor`.
|
|
@@ -85,19 +86,21 @@ jobs:
|
|
|
85
86
|
runs-on: ubuntu-latest
|
|
86
87
|
steps:
|
|
87
88
|
- uses: actions/checkout@v4
|
|
88
|
-
- uses: alexar76/warden@v0.
|
|
89
|
+
- uses: alexar76/warden@v0.11.0
|
|
89
90
|
with:
|
|
90
91
|
upload-sarif: 'true'
|
|
91
92
|
```
|
|
92
93
|
|
|
93
94
|
By default it reads the project files, does not start stdio servers (a pull request chooses that program), refuses non-public addresses, uses `warden.lock.json` when it exists, writes the job summary, and fails on a blocked server. Inputs: `config`, `working-directory`, `lock`, `launch-stdio`, `public-only`, `fail-on`, `histor`, `classifier-url`, `classifier-model`, `classifier-blocks`, `sarif`, `upload-sarif`, `version`. Outputs: `blocked`, `servers`, `sarif`. Pin the action by commit SHA in production workflows.
|
|
94
95
|
|
|
96
|
+
To be told when a server you use changes, run the same job on a schedule as well (`on: { schedule: [{ cron: '17 6 * * *' }] }`): with a committed `warden.lock.json`, a server whose tool definitions changed fails the run with `TOOL_DEF_DRIFT` and the summary shows the diff.
|
|
97
|
+
|
|
95
98
|
## pre-commit
|
|
96
99
|
|
|
97
100
|
```yaml
|
|
98
101
|
repos:
|
|
99
102
|
- repo: https://github.com/alexar76/warden
|
|
100
|
-
rev: v0.
|
|
103
|
+
rev: v0.10.0
|
|
101
104
|
hooks:
|
|
102
105
|
- id: warden-scan # changed configs; does not start stdio servers
|
|
103
106
|
- id: warden-lock # the project matches warden.lock.json; starts stdio servers
|
|
@@ -114,11 +117,23 @@ Both run the published package through `npx`, so Node 20 or later must be on `PA
|
|
|
114
117
|
|
|
115
118
|
At session start it scans the servers Claude Code starts for the project, tells you which are blocked, and tells the model by name and finding code only. A blocked description never enters the model's context. Calls to tools of a blocked server, or to a blocked tool, are then denied by a `PreToolUse` hook that reads one small file and starts nothing. Details and limits: [claude-plugin/README.md](../claude-plugin/README.md).
|
|
116
119
|
|
|
120
|
+
## Toxic flows: safe one by one, dangerous together
|
|
121
|
+
|
|
122
|
+
A client hands every server's tools to one model. When the servers it starts include a tool that reads your private data, one that lets text from outside in (a web page, an issue, an email) and one that can send data out, text arriving through the second can tell the model to read with the first and send with the third. No server is at fault, so no per-server verdict shows it. `scan` reports it per client:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
⚠ toxic flow claude-code can read private data (filesystem.read_file, …), take in text from outside (fetch.fetch) and send data out (fetch.fetch). …
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The capabilities are read from tool names and parameter names (`read_file`, `send_email`, a `url` or `to` parameter), not from descriptions: the reading does not depend on the description's language, and a description cannot talk its way out of it. A flow is advisory: it never blocks and never changes the exit code. It is in `--json` (`flows`) and in the Markdown summary. To break one, drop a leg — a `fetch` server you do not need, or a filesystem server scoped to one folder in a separate client.
|
|
129
|
+
|
|
117
130
|
## HISTOR: is this server serving you what it serves everyone?
|
|
118
131
|
|
|
119
132
|
[HISTOR](https://histor.modelmarket.dev) is a public log that records, every day, the tool definitions of every remote server in the official MCP registry. `--histor` asks it one question per remote server: is the tool set you were just served the one HISTOR observes?
|
|
120
133
|
|
|
121
|
-
What is sent: the endpoint with scheme, host and path only (no query, no user name or password), and the [MTL/1](https://github.com/alexar76/awr) digest of the tool set. No tool description, no header
|
|
134
|
+
What is sent: the endpoint with scheme, host and path only (no query, no user name or password), and the [MTL/1](https://github.com/alexar76/awr) digest of the tool set. No tool description, no header. Endpoints on private hosts or addresses, and paths that look like they carry a key, are not sent at all; the report says why.
|
|
135
|
+
|
|
136
|
+
A stdio server started from the public npm or PyPI registry (`npx`, `bunx`, `npm exec`, `pnpm dlx`, `uvx`, `uv tool run`, `pipx run`) is named by its package — `npm:<name>` or `pypi:<name>`, without the version — and the same digest. HISTOR installs each published version of such a package in a gVisor sandbox (decoy credentials, no route out; each tool called once with canary arguments, against nothing real) and records its tools and what it did and records its tools, so `previously-observed` here means you run an older version, and `different` a version HISTOR has not reached yet or a package that is not the published one. A local path, a git or tarball URL, a private registry (`--registry`, `--index-url`) or a plain `node`/`python` launch is never sent. For a package HISTOR also says, as advisory findings, whether its name imitates a popular one (`HISTOR_PACKAGE_LOOKALIKE`: one character away, the same name under another scope, or other separators, from a package downloaded at least 100 times more) and whether the version it observed carries the marks of a stolen publishing token: `HISTOR_PACKAGE_PROVENANCE_LOST` (no attested CI build where the previous version had one), `HISTOR_PACKAGE_INSTALL_SCRIPTS` (new install scripts), `HISTOR_PACKAGE_PUBLISHER_CHANGED`, `HISTOR_PACKAGE_NEW_DEPENDENCIES`. And what the package did in the sandbox: `HISTOR_PACKAGE_READS_SECRETS` (it opened decoy credentials — SSH keys, cloud or registry tokens, a wallet — at install, at startup or when a tool was called), `HISTOR_PACKAGE_PERSISTENCE` (it wrote where it would outlive the session, such as `.bashrc` or `authorized_keys`), `HISTOR_PACKAGE_STARTUP_NETWORK` (it reached out before any tool was called), `HISTOR_PACKAGE_STARTS_PROGRAMS`, `HISTOR_PACKAGE_INSTALL_BEHAVIOUR` (what its install scripts do). Loading `.env` from the working directory and calling its own API during a tool call are not findings. With `--histor`, the report also gives a link to a HISTOR feed of changes to the scanned servers HISTOR knows (`historWatch` in `--json`): subscribe in any feed reader.
|
|
122
137
|
|
|
123
138
|
| Answer | Meaning |
|
|
124
139
|
|---|---|
|
|
@@ -135,13 +150,13 @@ WARDEN's rules are offline and deterministic, and they miss what no rule names:
|
|
|
135
150
|
|
|
136
151
|
```bash
|
|
137
152
|
warden-mcp scan --classifier-url http://localhost:11434/v1 --classifier-model qwen2.5:14b
|
|
138
|
-
WARDEN_CLASSIFIER_API_KEY=… warden-mcp scan --classifier-url https://api.deepseek.com --classifier-model deepseek-flash
|
|
153
|
+
WARDEN_CLASSIFIER_API_KEY=… warden-mcp scan --classifier-url https://api.deepseek.com --classifier-model deepseek-flash --classifier-reasoning-effort none
|
|
139
154
|
```
|
|
140
155
|
|
|
141
|
-
- **
|
|
142
|
-
- **Advisory
|
|
143
|
-
- **
|
|
144
|
-
- **
|
|
156
|
+
- **Opt-in; sends every advertised field.** Name, title, description, both schemas, annotations and extension metadata go to the endpoint, together with a normalized reading when needed. The key comes only from `WARDEN_CLASSIFIER_API_KEY`.
|
|
157
|
+
- **Advisory by default; fail closed with `--classifier-blocks`.** Findings use `TOOL_DEF_CLASSIFIER`. Enforcing mode also refuses incomplete coverage, truncation, uncertainty, malformed evidence, timeouts and provider errors (`CLASSIFIER_INCOMPLETE`). These refusals are inspection failures, not detected attacks. They cannot enter a lock through `--update-lock`.
|
|
158
|
+
- **Authority, in any language.** Categories are `instruction_to_model`, `exfiltration`, `secret_request`, `concealment` and `cross_tool`. The prompt separates legitimate tool requirements from attempts to override the user, invent consent or control another tool. Random fences help delimit untrusted data; they are not a proof against injection. The field-reference protocol requires one decision per input and host-validated evidence field IDs. WARDEN resolves source previews itself and retries each incomplete input once in isolation. Provider outages do not fan out into per-tool requests.
|
|
159
|
+
- **Review is not approval.** Model language coverage varies. For a language-independent admission boundary use [`wrap --require-approval`](security-hardening-language-boundary.md). An LLM cannot create that approval. Historical HISTOR classifier results remain advisory and use their historical protocol.
|
|
145
160
|
|
|
146
161
|
Measured on 2026-10-09 with `deepseek-flash`, the model HISTOR uses, on MCPTox (the published benchmark described in the comparison below) and on our own sets:
|
|
147
162
|
|
|
@@ -175,7 +190,7 @@ The last three rows come from MCPTox (Wang et al., AAAI 2026), a published bench
|
|
|
175
190
|
- **Know which numbers are fitted.** We wrote the first attack set. Ruleset v9 was written after we had read both attack sets, and v10 after the 22 MCPTox servers of the other half. So 20 and 10 are the recall of rules fitted to what they were shown. The held-out MCPTox row, the clean servers, the corpus and the whole 0.8.2 column are not fitted. On the corpus v10 blocks exactly what v8 blocked.
|
|
176
191
|
- **MCPTox tests a shape, not a keyword.** Almost every poisoned tool tells the model to call another tool first, or to change another tool's arguments. v10 names that shape (`TOOL_DEF_CROSS_TOOL`, see [the gate chain](gates.md)) and blocks 171 of 218 held-out tools, against 189 of 267 on the half it was written from. Add the benchmark's `<IMPORTANT>` or "Ignore the previous instructions" prefix and every scanner we ran catches it; we did not run mcp-shield on that set. On the clean servers mcp-audit blocks AmapMap and BaiduMap, and mcp-shield blocks FileSystem, AdFin and Commander on "sensitive file access".
|
|
177
192
|
- **What WARDEN still misses:** 47 of the 218 held-out MCPTox tools, an instruction in Chinese to read `.env`, a paraphrase with no keyword ("gather every credential file … keep this step to yourself") and "include the output of env". It reports 41 of those 47, and all three of ours, as advisory, not as blocks. They need a meaning-based check, not another rule: with the optional classifier above, 191 of the 218 are blocked at `high` and all 218 are flagged.
|
|
178
|
-
- **
|
|
193
|
+
- **WARDEN is not alone in that slot.** mcp-audit also ships a lock file, a PR diff mode and SARIF.
|
|
179
194
|
|
|
180
195
|
## What scan does not do
|
|
181
196
|
|
package/docs/scan.ru.md
CHANGED
|
@@ -5,18 +5,19 @@
|
|
|
5
5
|
`warden-mcp scan` читает уже существующую у вас конфигурацию MCP, подключается к каждому серверу из неё и проверяет определения инструментов до того, как их увидит модель. Это та же цепочка гейтов, что у [`wrap`](../README-ru.md) и у [библиотеки](integration.ru.md), только пройденная один раз по вашим конфигам, а не перед одним сервером.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npx -y @aimarket/warden@0.
|
|
8
|
+
npx -y @aimarket/warden@0.11.0 scan
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
```text
|
|
12
|
-
WARDEN scan 0.
|
|
12
|
+
WARDEN scan 0.10.0 · ruleset 10 sha256-lJuKKKV5mtru… · block at high
|
|
13
13
|
read .mcp.json (claude-code, 3 servers)
|
|
14
14
|
|
|
15
|
-
✓ allow notes
|
|
16
|
-
✗ BLOCK evil-notes
|
|
17
|
-
! error broken
|
|
15
|
+
✓ allow notes claude-code 1 tool · score 0.90
|
|
16
|
+
✗ BLOCK evil-notes claude-code 1 tool · score 0.00 · TOOL_DEF_EXFIL(notes) TOOL_DEF_SECRET_REQUEST(notes) THREAT_SSH_KEY_READ(notes)
|
|
17
|
+
! error broken claude-code could not start: spawn /nonexistent/bin/server ENOENT
|
|
18
18
|
|
|
19
19
|
3 servers: 1 allowed, 1 blocked, 1 not checked, 0 skipped.
|
|
20
|
+
Details: warden-mcp scan --json, or --markdown FILE. To review a changed server: warden-mcp scan --lock warden.lock.json --update-lock.
|
|
20
21
|
```
|
|
21
22
|
|
|
22
23
|
Без аккаунта, без API-ключа, без модели. Сетевой трафик идёт только к серверам из вашего конфига и, если передан `--histor`, к журналу HISTOR.
|
|
@@ -85,19 +86,21 @@ jobs:
|
|
|
85
86
|
runs-on: ubuntu-latest
|
|
86
87
|
steps:
|
|
87
88
|
- uses: actions/checkout@v4
|
|
88
|
-
- uses: alexar76/warden@v0.
|
|
89
|
+
- uses: alexar76/warden@v0.11.0
|
|
89
90
|
with:
|
|
90
91
|
upload-sarif: 'true'
|
|
91
92
|
```
|
|
92
93
|
|
|
93
94
|
По умолчанию Action читает файлы проекта, не запускает stdio-серверы (эту программу выбирает pull request), отказывает непубличным адресам, использует `warden.lock.json`, если он есть, пишет сводку задания и падает на заблокированном сервере. Входы: `config`, `working-directory`, `lock`, `launch-stdio`, `public-only`, `fail-on`, `histor`, `classifier-url`, `classifier-model`, `classifier-blocks`, `sarif`, `upload-sarif`, `version`. Выходы: `blocked`, `servers`, `sarif`. В боевых workflow закрепляйте Action по SHA коммита.
|
|
94
95
|
|
|
96
|
+
Чтобы узнавать, когда меняется сервер, которым вы пользуетесь, запускайте ту же задачу ещё и по расписанию (`on: { schedule: [{ cron: '17 6 * * *' }] }`): при закоммиченном `warden.lock.json` сервер, у которого изменились определения инструментов, роняет запуск с `TOOL_DEF_DRIFT`, а в сводке виден diff.
|
|
97
|
+
|
|
95
98
|
## pre-commit
|
|
96
99
|
|
|
97
100
|
```yaml
|
|
98
101
|
repos:
|
|
99
102
|
- repo: https://github.com/alexar76/warden
|
|
100
|
-
rev: v0.
|
|
103
|
+
rev: v0.10.0
|
|
101
104
|
hooks:
|
|
102
105
|
- id: warden-scan # изменённые конфиги; stdio-серверы не запускаются
|
|
103
106
|
- id: warden-lock # проект соответствует warden.lock.json; stdio-серверы запускаются
|
|
@@ -114,11 +117,23 @@ repos:
|
|
|
114
117
|
|
|
115
118
|
В начале сессии плагин проверяет серверы, которые Claude Code запускает для проекта, сообщает вам, какие из них заблокированы, а модели — только имя и код находки. Описание заблокированного инструмента никогда не попадает в контекст модели. Вызовы инструментов заблокированного сервера или заблокированного инструмента затем запрещает хук `PreToolUse`: он читает один небольшой файл и ничего не запускает. Подробности и ограничения: [claude-plugin/README.md](../claude-plugin/README.md).
|
|
116
119
|
|
|
120
|
+
## Опасные связки: по отдельности безопасно, вместе — нет
|
|
121
|
+
|
|
122
|
+
Клиент отдаёт инструменты всех серверов одной модели. Если среди серверов есть инструмент, который читает ваши приватные данные, инструмент, через который приходит чужой текст (веб-страница, задача, письмо), и инструмент, который может отправить данные наружу, то текст, пришедший через второй, может велеть модели прочитать первым и отправить третьим. Ни один сервер не виноват, поэтому вердикт по отдельному серверу этого не покажет. `scan` сообщает об этом по каждому клиенту:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
⚠ toxic flow claude-code can read private data (filesystem.read_file, …), take in text from outside (fetch.fetch) and send data out (fetch.fetch). …
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Возможности определяются по именам инструментов и параметров (`read_file`, `send_email`, параметр `url` или `to`), а не по описаниям: язык описания не важен, и описание не может себя оправдать. Связка — рекомендательная находка: она никогда не блокирует и не меняет код выхода. Она есть в `--json` (`flows`) и в сводке Markdown. Чтобы разорвать связку, уберите одну из ног — например, ненужный сервер `fetch` или вынесите файловый сервер с доступом к одной папке в отдельный клиент.
|
|
129
|
+
|
|
117
130
|
## HISTOR: отдаёт ли сервер вам то же, что всем?
|
|
118
131
|
|
|
119
132
|
[HISTOR](https://histor.modelmarket.dev) — публичный журнал, который каждый день записывает определения инструментов всех удалённых серверов из официального реестра MCP. `--histor` задаёт ему один вопрос о каждом удалённом сервере: тот ли это набор инструментов, который видит HISTOR?
|
|
120
133
|
|
|
121
|
-
Что отправляется: адрес эндпоинта — только схема, хост и путь (без query, без имени пользователя и пароля) — и дайджест набора инструментов по [MTL/1](https://github.com/alexar76/awr). Ни описаний инструментов, ни
|
|
134
|
+
Что отправляется: адрес эндпоинта — только схема, хост и путь (без query, без имени пользователя и пароля) — и дайджест набора инструментов по [MTL/1](https://github.com/alexar76/awr). Ни описаний инструментов, ни заголовков. Эндпоинты на частных хостах и адресах, а также пути, похожие на содержащие ключ, не отправляются вовсе; отчёт объясняет почему.
|
|
135
|
+
|
|
136
|
+
Stdio-сервер, запущенный из публичного реестра npm или PyPI (`npx`, `bunx`, `npm exec`, `pnpm dlx`, `uvx`, `uv tool run`, `pipx run`), называется по пакету — `npm:<имя>` или `pypi:<имя>`, без версии — с тем же дайджестом. HISTOR ставит каждую опубликованную версию такого пакета в песочнице gVisor (приманки вместо секретов, без маршрута наружу; каждый инструмент вызывается один раз с подставными аргументами, ни на что настоящее не действуя) и записывает его инструменты и то, что он делал и записывает его инструменты, поэтому `previously-observed` здесь значит, что у вас более старая версия, а `different` — версия, до которой HISTOR ещё не дошёл, или пакет, который не совпадает с опубликованным. Локальный путь, git- или tarball-адрес, частный реестр (`--registry`, `--index-url`) или простой запуск `node`/`python` не отправляются никогда. Для пакета HISTOR также сообщает, рекомендательными находками, не подражает ли его имя популярному (`HISTOR_PACKAGE_LOOKALIKE`: на один символ, то же имя под другим scope или с другими разделителями, при том что популярный скачивают хотя бы в 100 раз чаще) и нет ли у прочитанной версии признаков украденного токена публикации: `HISTOR_PACKAGE_PROVENANCE_LOST` (нет подтверждённой сборки в CI, хотя у предыдущей версии была), `HISTOR_PACKAGE_INSTALL_SCRIPTS` (появились установочные скрипты), `HISTOR_PACKAGE_PUBLISHER_CHANGED`, `HISTOR_PACKAGE_NEW_DEPENDENCIES`. И что пакет делал в песочнице: `HISTOR_PACKAGE_READS_SECRETS` (открыл приманки — SSH-ключи, токены облаков или реестров, кошелёк — при установке, при запуске или при вызове инструмента), `HISTOR_PACKAGE_PERSISTENCE` (писал туда, где пережил бы сессию, например в `.bashrc` или `authorized_keys`), `HISTOR_PACKAGE_STARTUP_NETWORK` (выходил в сеть до вызова любого инструмента), `HISTOR_PACKAGE_STARTS_PROGRAMS`, `HISTOR_PACKAGE_INSTALL_BEHAVIOUR` (что делают его установочные скрипты). Загрузка `.env` из рабочей папки и обращения к собственному API во время вызова инструмента находками не считаются. С `--histor` отчёт также даёт ссылку на ленту HISTOR с изменениями тех просканированных серверов, которые HISTOR знает (`historWatch` в `--json`): на неё можно подписаться в любом читателе лент.
|
|
122
137
|
|
|
123
138
|
| Ответ | Значение |
|
|
124
139
|
|---|---|
|
|
@@ -135,13 +150,13 @@ repos:
|
|
|
135
150
|
|
|
136
151
|
```bash
|
|
137
152
|
warden-mcp scan --classifier-url http://localhost:11434/v1 --classifier-model qwen2.5:14b
|
|
138
|
-
WARDEN_CLASSIFIER_API_KEY=… warden-mcp scan --classifier-url https://api.deepseek.com --classifier-model deepseek-flash
|
|
153
|
+
WARDEN_CLASSIFIER_API_KEY=… warden-mcp scan --classifier-url https://api.deepseek.com --classifier-model deepseek-flash --classifier-reasoning-effort none
|
|
139
154
|
```
|
|
140
155
|
|
|
141
|
-
-
|
|
142
|
-
-
|
|
143
|
-
-
|
|
144
|
-
-
|
|
156
|
+
- **Включается явно и отправляет все объявленные поля.** Имя, заголовок, описание, обе схемы, annotations и метаданные расширений, а при необходимости и нормализованное прочтение. Ключ берётся только из `WARDEN_CLASSIFIER_API_KEY`.
|
|
157
|
+
- **По умолчанию — рекомендация; с `--classifier-blocks` неполная проверка запрещает допуск.** Находки имеют код `TOOL_DEF_CLASSIFIER`. Ошибки провайдера, таймаут, усечение текста, неопределённость, неполный или некорректный ответ дают `CLASSIFIER_INCOMPLETE`. Это отказ проверки, а не обнаруженная атака. Такой результат нельзя утвердить через `--update-lock`.
|
|
158
|
+
- **Проверка полномочий на любом языке.** Пять категорий: `instruction_to_model`, `exfiltration`, `secret_request`, `concealment`, `cross_tool`. Промпт отличает требования самого инструмента от подмены указаний пользователя, выдуманного согласия и управления чужими инструментами. Случайные разделители не гарантируют защиту от инъекции. Протокол требует решение по каждому инструменту и ссылки на номера полей. WARDEN сам извлекает доказательства из исходника. Неполное решение повторно проверяется один раз отдельно; сбои API не размножают запросы.
|
|
159
|
+
- **Проверка не даёт доверия.** Владение языками зависит от модели. Независимую от языка границу допуска даёт [`wrap --require-approval`](security-hardening-language-boundary.md); LLM не может выдать это утверждение. Старые ответы HISTOR остаются рекомендательными и используют прежний протокол.
|
|
145
160
|
|
|
146
161
|
Замер 9 октября 2026 года на `deepseek-flash`, той же модели, что у HISTOR, на MCPTox (опубликованный бенчмарк, он описан в сравнении ниже) и на наших наборах:
|
|
147
162
|
|
|
@@ -175,7 +190,7 @@ WARDEN_CLASSIFIER_API_KEY=… warden-mcp scan --classifier-url https://api.deeps
|
|
|
175
190
|
- **Помните, какие числа подогнаны.** Первый набор атак написали мы. Ruleset v9 написан после того, как мы прочли оба набора атак, а v10 — после 22 серверов MCPTox из другой половины. Так что 20 и 10 — это полнота правил, подогнанных под увиденное. Отложенная строка MCPTox, чистые серверы, корпус и весь столбец 0.8.2 не подогнаны. На корпусе v10 блокирует ровно то же, что v8.
|
|
176
191
|
- **MCPTox проверяет форму, а не ключевое слово.** Почти каждый отравленный инструмент велит модели сначала вызвать другой инструмент или изменить аргументы другого инструмента. v10 называет эту форму (`TOOL_DEF_CROSS_TOOL`, см. [цепочку гейтов](gates.ru.md)) и блокирует 171 из 218 отложенных инструментов против 189 из 267 на половине, по которой он написан. С префиксом бенчмарка `<IMPORTANT>` или «Ignore the previous instructions» их ловит каждый запущенный нами сканер; mcp-shield на этом наборе мы не запускали. На чистых серверах mcp-audit блокирует AmapMap и BaiduMap, а mcp-shield — FileSystem, AdFin и Commander за «sensitive file access».
|
|
177
192
|
- **Что WARDEN всё ещё пропускает:** 47 из 218 отложенных инструментов MCPTox, инструкцию по-китайски прочитать `.env`, перефразировку без ключевых слов («gather every credential file … keep this step to yourself») и «include the output of env». О 41 из этих 47 и обо всех трёх наших он сообщает рекомендательно, но не блокирует. Здесь нужна проверка по смыслу, а не ещё одно правило: с необязательным классификатором выше 191 из 218 блокируются на уровне `high`, а помечаются все 218.
|
|
178
|
-
-
|
|
193
|
+
- **WARDEN в этой нише не один.** У mcp-audit тоже есть лок-файл, режим диффа для PR и SARIF.
|
|
179
194
|
|
|
180
195
|
## Чего scan не делает
|
|
181
196
|
|
package/docs/scan.zh.md
CHANGED
|
@@ -5,18 +5,19 @@
|
|
|
5
5
|
`warden-mcp scan` 读取你已有的 MCP 配置,连接其中启动的每个服务器,并在模型看到工具定义之前对其进行审查。它与 [`wrap`](../README-zh.md) 和[库](integration.zh.md)使用同一条关卡链,只是对你的配置运行一次,而不是挡在单个服务器前面。
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npx -y @aimarket/warden@0.
|
|
8
|
+
npx -y @aimarket/warden@0.11.0 scan
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
```text
|
|
12
|
-
WARDEN scan 0.
|
|
12
|
+
WARDEN scan 0.10.0 · ruleset 10 sha256-lJuKKKV5mtru… · block at high
|
|
13
13
|
read .mcp.json (claude-code, 3 servers)
|
|
14
14
|
|
|
15
|
-
✓ allow notes
|
|
16
|
-
✗ BLOCK evil-notes
|
|
17
|
-
! error broken
|
|
15
|
+
✓ allow notes claude-code 1 tool · score 0.90
|
|
16
|
+
✗ BLOCK evil-notes claude-code 1 tool · score 0.00 · TOOL_DEF_EXFIL(notes) TOOL_DEF_SECRET_REQUEST(notes) THREAT_SSH_KEY_READ(notes)
|
|
17
|
+
! error broken claude-code could not start: spawn /nonexistent/bin/server ENOENT
|
|
18
18
|
|
|
19
19
|
3 servers: 1 allowed, 1 blocked, 1 not checked, 0 skipped.
|
|
20
|
+
Details: warden-mcp scan --json, or --markdown FILE. To review a changed server: warden-mcp scan --lock warden.lock.json --update-lock.
|
|
20
21
|
```
|
|
21
22
|
|
|
22
23
|
无需账号、无需 API 密钥、不调用模型。唯一的网络流量是发往你配置中的服务器;如果传入 `--histor`,还会发往 HISTOR 日志。
|
|
@@ -85,19 +86,21 @@ jobs:
|
|
|
85
86
|
runs-on: ubuntu-latest
|
|
86
87
|
steps:
|
|
87
88
|
- uses: actions/checkout@v4
|
|
88
|
-
- uses: alexar76/warden@v0.
|
|
89
|
+
- uses: alexar76/warden@v0.11.0
|
|
89
90
|
with:
|
|
90
91
|
upload-sarif: 'true'
|
|
91
92
|
```
|
|
92
93
|
|
|
93
94
|
默认情况下,它读取项目文件,不启动 stdio 服务器(该程序由 pull request 决定),拒绝非公网地址,存在 `warden.lock.json` 时使用它,写入作业摘要,并在有服务器被阻止时失败。输入:`config`、`working-directory`、`lock`、`launch-stdio`、`public-only`、`fail-on`、`histor`、`classifier-url`、`classifier-model`、`classifier-blocks`、`sarif`、`upload-sarif`、`version`。输出:`blocked`、`servers`、`sarif`。在生产 workflow 中请按提交 SHA 固定该 Action。
|
|
94
95
|
|
|
96
|
+
若想在你使用的服务器发生变化时得到通知,可以再按计划运行同一个 job(`on: { schedule: [{ cron: '17 6 * * *' }] }`):只要仓库里提交了 `warden.lock.json`,工具定义发生变化的服务器就会以 `TOOL_DEF_DRIFT` 让这次运行失败,摘要中会显示 diff。
|
|
97
|
+
|
|
95
98
|
## pre-commit
|
|
96
99
|
|
|
97
100
|
```yaml
|
|
98
101
|
repos:
|
|
99
102
|
- repo: https://github.com/alexar76/warden
|
|
100
|
-
rev: v0.
|
|
103
|
+
rev: v0.10.0
|
|
101
104
|
hooks:
|
|
102
105
|
- id: warden-scan # 改动过的配置;不启动 stdio 服务器
|
|
103
106
|
- id: warden-lock # 项目与 warden.lock.json 一致;会启动 stdio 服务器
|
|
@@ -114,11 +117,23 @@ repos:
|
|
|
114
117
|
|
|
115
118
|
会话开始时,它扫描 Claude Code 为该项目启动的服务器,告诉你哪些被阻止,而对模型只给出名称和发现代码。被阻止的描述永远不会进入模型的上下文。之后,`PreToolUse` 钩子会拒绝对被阻止服务器的工具或被阻止工具的调用;它只读取一个小文件,不启动任何程序。详情与限制:[claude-plugin/README.md](../claude-plugin/README.md)。
|
|
116
119
|
|
|
120
|
+
## 有毒数据流:单个安全,组合危险
|
|
121
|
+
|
|
122
|
+
客户端把所有服务器的工具交给同一个模型。如果其中有一个工具读取你的私有数据,一个工具引入外部文本(网页、issue、邮件),还有一个工具能把数据发出去,那么经第二个工具进来的文本就可以让模型用第一个读取、用第三个发送。没有哪个服务器有错,所以按服务器给出的结论看不到它。`scan` 按客户端报告:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
⚠ toxic flow claude-code can read private data (filesystem.read_file, …), take in text from outside (fetch.fetch) and send data out (fetch.fetch). …
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
能力从工具名和参数名(`read_file`、`send_email`、`url` 或 `to` 参数)中读取,而不是从描述中读取:结果与描述的语言无关,描述也无法为自己开脱。数据流只是提示:从不阻止,也不改变退出码。它出现在 `--json`(`flows`)和 Markdown 摘要中。要打断它,去掉其中一环——例如不需要的 `fetch` 服务器,或把只开放一个文件夹的文件服务器放到另一个客户端。
|
|
129
|
+
|
|
117
130
|
## HISTOR:这个服务器给你的是否和给所有人的一样?
|
|
118
131
|
|
|
119
132
|
[HISTOR](https://histor.modelmarket.dev) 是一个公开日志,每天记录官方 MCP 注册表中所有远程服务器的工具定义。`--histor` 对每个远程服务器问它一个问题:你刚收到的工具集,是否就是 HISTOR 观察到的那一个?
|
|
120
133
|
|
|
121
|
-
发送的内容:端点(只有协议、主机和路径,不含 query、用户名或密码),以及工具集的 [MTL/1](https://github.com/alexar76/awr)
|
|
134
|
+
发送的内容:端点(只有协议、主机和路径,不含 query、用户名或密码),以及工具集的 [MTL/1](https://github.com/alexar76/awr) 摘要。不发送任何工具描述或请求头。私有主机或地址上的端点,以及看起来带有密钥的路径,根本不会发送;报告会说明原因。
|
|
135
|
+
|
|
136
|
+
从公共 npm 或 PyPI 注册表启动的 stdio 服务器(`npx`、`bunx`、`npm exec`、`pnpm dlx`、`uvx`、`uv tool run`、`pipx run`)以其软件包命名——`npm:<名称>` 或 `pypi:<名称>`,不带版本——并附上同样的摘要。HISTOR 会在 gVisor 沙箱中(诱饵凭据、无对外路由;每个工具用金丝雀参数调用一次,不作用于任何真实对象)并记录其工具及其行为安装这类软件包的每个已发布版本并记录其工具,因此这里的 `previously-observed` 表示你运行的是较旧的版本,`different` 表示 HISTOR 尚未覆盖到的版本,或者与已发布版本不一致的软件包。本地路径、git 或 tarball 地址、私有注册表(`--registry`、`--index-url`)以及直接用 `node`/`python` 启动的服务器,永远不会被发送。 对于软件包,HISTOR 还会以提示性发现告诉你:它的名称是否在模仿热门软件包(`HISTOR_PACKAGE_LOOKALIKE`:相差一个字符、在另一个 scope 下同名或仅分隔符不同,且热门软件包的下载量至少高 100 倍),以及所观察的版本是否带有发布令牌被盗的迹象:`HISTOR_PACKAGE_PROVENANCE_LOST`(上一个版本有经证明的 CI 构建,这个版本没有)、`HISTOR_PACKAGE_INSTALL_SCRIPTS`(新增安装脚本)、`HISTOR_PACKAGE_PUBLISHER_CHANGED`、`HISTOR_PACKAGE_NEW_DEPENDENCIES`。 以及软件包在沙箱中的行为:`HISTOR_PACKAGE_READS_SECRETS`(在安装、启动或工具被调用时打开了诱饵凭据——SSH 密钥、云或注册表令牌、钱包),`HISTOR_PACKAGE_PERSISTENCE`(写入了会在会话之后留存的位置,例如 `.bashrc` 或 `authorized_keys`),`HISTOR_PACKAGE_STARTUP_NETWORK`(在调用任何工具之前就访问了网络),`HISTOR_PACKAGE_STARTS_PROGRAMS`,`HISTOR_PACKAGE_INSTALL_BEHAVIOUR`(其安装脚本做了什么)。从工作目录加载 `.env`、在工具调用期间访问其自身 API,都不算发现。 使用 `--histor` 时,报告还会给出一个 HISTOR 订阅源链接,包含 HISTOR 已知的被扫描服务器的变更(`--json` 中的 `historWatch`):可在任意订阅阅读器中订阅。
|
|
122
137
|
|
|
123
138
|
| 回答 | 含义 |
|
|
124
139
|
|---|---|
|
|
@@ -135,13 +150,13 @@ WARDEN 的规则离线、确定性地运行,也会漏掉任何规则都没提
|
|
|
135
150
|
|
|
136
151
|
```bash
|
|
137
152
|
warden-mcp scan --classifier-url http://localhost:11434/v1 --classifier-model qwen2.5:14b
|
|
138
|
-
WARDEN_CLASSIFIER_API_KEY=… warden-mcp scan --classifier-url https://api.deepseek.com --classifier-model deepseek-flash
|
|
153
|
+
WARDEN_CLASSIFIER_API_KEY=… warden-mcp scan --classifier-url https://api.deepseek.com --classifier-model deepseek-flash --classifier-reasoning-effort none
|
|
139
154
|
```
|
|
140
155
|
|
|
141
|
-
-
|
|
142
|
-
-
|
|
143
|
-
-
|
|
144
|
-
-
|
|
156
|
+
- **显式启用,并发送所有声明字段。** 包括名称、标题、描述、输入/输出 schema、annotations 和扩展元数据;需要时附带归一化文本。密钥仅来自 `WARDEN_CLASSIFIER_API_KEY`。
|
|
157
|
+
- **默认仅建议;`--classifier-blocks` 对不完整检查拒绝放行。** 检测结果使用 `TOOL_DEF_CLASSIFIER`。截断、不确定、覆盖不完整、无效回答、超时及服务错误使用 `CLASSIFIER_INCOMPLETE`;这是检查失败,不是检测到攻击,`--update-lock` 不能批准这些结果。
|
|
158
|
+
- **五类语义检查。** `instruction_to_model`、`exfiltration`、`secret_request`、`concealment`、`cross_tool`。检查任何语言中的权限越界,但效果取决于模型的语言能力。协议要求每个工具有明确决定及有效的字段编号;WARDEN 自行从原文提取证据。每个未完成的工具最多单独重试一次。随机分隔符不保证免疫提示注入。
|
|
159
|
+
- **检查不等于批准。** [`wrap --require-approval`](security-hardening-language-boundary.md) 提供独立于语言的操作员批准边界。HISTOR 的历史模型结果仍然仅供参考。
|
|
145
160
|
|
|
146
161
|
2026 年 10 月 9 日用 `deepseek-flash`(HISTOR 使用的同一模型)在 MCPTox(下文对比中介绍的公开基准)和我们自己的样本集上测得:
|
|
147
162
|
|
|
@@ -175,7 +190,7 @@ WARDEN_CLASSIFIER_API_KEY=… warden-mcp scan --classifier-url https://api.deeps
|
|
|
175
190
|
- **要清楚哪些数字是调出来的。** 第一组攻击样本是我们写的。ruleset v9 是在读过两组攻击样本之后写的,v10 是在读过另一半的 22 台 MCPTox 服务器之后写的。所以 20 和 10 是“照着见过的样本调出来的规则”的召回率。MCPTox 留出的那一行、干净服务器、语料以及整个 0.8.2 列都没有经过调整。在语料上,v10 阻止的与 v8 完全相同。
|
|
176
191
|
- **MCPTox 考的是结构,不是关键词。** 几乎每个投毒工具都要求模型先调用另一个工具,或修改另一个工具的参数。v10 为这种结构命名(`TOOL_DEF_CROSS_TOOL`,见[关卡链](gates.zh.md)),在留出的 218 个工具中阻止 171 个;在用来编写它的那一半上是 267 个中的 189 个。加上基准自带的 `<IMPORTANT>` 或 “Ignore the previous instructions” 前缀后,我们运行的每个扫描器都能发现;mcp-shield 没有在这一组上运行。在干净服务器上,mcp-audit 阻止了 AmapMap 和 BaiduMap,mcp-shield 以 “sensitive file access” 为由阻止了 FileSystem、AdFin 和 Commander。
|
|
177
192
|
- **WARDEN 仍会漏掉的:** 留出的 218 个 MCPTox 工具中的 47 个、用中文写的读取 `.env` 的指令、没有关键词的改写(“gather every credential file … keep this step to yourself”),以及 “include the output of env”。这 47 个中的 41 个以及我们的这三个,它会以建议的形式报告,但不会阻止。这些需要基于语义的检查,而不是再加一条规则:加上上文的可选分类器,218 个中有 191 个在 `high` 级别被阻止,218 个全部被标记。
|
|
178
|
-
-
|
|
193
|
+
- **在这个位置上 WARDEN 并不是唯一的选择。** mcp-audit 同样提供 lock 文件、PR diff 模式和 SARIF。
|
|
179
194
|
|
|
180
195
|
## scan 不做什么
|
|
181
196
|
|