sealwall 0.1.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.
- sealwall-0.1.0/LICENSE +21 -0
- sealwall-0.1.0/PKG-INFO +281 -0
- sealwall-0.1.0/README.md +270 -0
- sealwall-0.1.0/pyproject.toml +20 -0
- sealwall-0.1.0/sealwall.egg-info/PKG-INFO +281 -0
- sealwall-0.1.0/sealwall.egg-info/SOURCES.txt +9 -0
- sealwall-0.1.0/sealwall.egg-info/dependency_links.txt +1 -0
- sealwall-0.1.0/sealwall.egg-info/entry_points.txt +2 -0
- sealwall-0.1.0/sealwall.egg-info/top_level.txt +1 -0
- sealwall-0.1.0/sealwall.py +494 -0
- sealwall-0.1.0/setup.cfg +4 -0
sealwall-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vishal Murugan
|
|
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.
|
sealwall-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sealwall
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Firewall and tamper-evident audit log for AI agents using MCP
|
|
5
|
+
Author: Vishal Murugan
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Dynamic: license-file
|
|
11
|
+
|
|
12
|
+
# sealwall
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://github.com/vishalmurugan1986/sealwall"><img src="https://img.shields.io/badge/version-0.1.0-blue.svg" alt="Version 0.1.0" /></a>
|
|
16
|
+
<a href="https://python.org"><img src="https://img.shields.io/badge/python-3.9+-3776ab.svg" alt="Python 3.9+" /></a>
|
|
17
|
+
<a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/MCP-Compatible-9333ea.svg" alt="MCP Compatible" /></a>
|
|
18
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License: MIT" /></a>
|
|
19
|
+
<a href="#running-tests"><img src="https://img.shields.io/badge/tests-23%20passed-success.svg" alt="Tests" /></a>
|
|
20
|
+
<a href="https://github.com/vishalmurugan1986/sealwall/actions"><img src="https://github.com/vishalmurugan1986/sealwall/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
|
|
21
|
+
<img src="https://img.shields.io/badge/dependencies-0-black.svg" alt="Zero Dependencies" />
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
<p align="center">
|
|
25
|
+
<strong>A zero-dependency firewall and tamper-evident audit log proxy for Model Context Protocol (MCP) AI agents.</strong>
|
|
26
|
+
</p>
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
AI agents can be hijacked by untrusted text they encounterβmalicious web pages, documents, or emails can instruct agents to leak files, exfiltrate API keys, or run destructive tools.
|
|
31
|
+
|
|
32
|
+
`sealwall` is a lightweight, local-first proxy that sits between your AI client (Claude Desktop, Cursor, local agents) and any MCP stdio server. It enforces fine-grained policy **before** execution, intercepts prompt injections in tool outputs, and records every action in a cryptographically chained, tamper-evident audit log.
|
|
33
|
+
|
|
34
|
+
<p align="center">
|
|
35
|
+
<img src="assets/demo.gif" alt="sealwall terminal demo" width="760" />
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Key Features
|
|
41
|
+
|
|
42
|
+
- **π‘οΈ Fail-Closed Policy Enforcement**: Default-deny model. Define exact tool whitelists, regex argument restrictions (e.g., blocking `.ssh`, `.env`, `id_rsa`), and require approval for sensitive tools.
|
|
43
|
+
- **π Tamper-Evident Audit Chain**: Every tool call, argument, and output is hashed with SHA-256 into a SHA-256 hash chain. Modifying, deleting, or reordering past entries breaks verification. (Someone with write access to the whole file could rebuild the chain, so store the latest hash somewhere else for strong guarantees.)
|
|
44
|
+
- **π Output Injection Defense**: Scans tool responses for common prompt-injection / goal-hijacking triggers and withholds dangerous content from entering the agent's context window.
|
|
45
|
+
- **β οΈ Tool-Poisoning & Rug-Pull Defense** *(new in 0.2)*: Scans `tools/list` for hidden instructions in tool descriptions (e.g. `<IMPORTANT>` blocks telling the model to read `~/.ssh`) and removes those tools. Tool definitions are pinned on first sight; if a definition later changes, the tool is blocked until you re-approve with `--accept-changes`.
|
|
46
|
+
- **π Remote MCP (streamable HTTP)** *(new in 0.2)*: Run `sealwall --policy policy.json --http-upstream https://host/mcp` and point your client at `http://127.0.0.1:8787/`. Same policy, same audit log.
|
|
47
|
+
- **π Path Allowlists** *(new in 0.3)*: `"allow_paths": ["/home/me/project"]` and `"deny_paths"`. Paths in tool arguments are expanded (`~`, `$VAR`, `file://`), resolved through symlinks and `..`, then checked. This closes most of the regex-bypass tricks.
|
|
48
|
+
- **π Signed Audit Heads** *(new in 0.3)*: `sealwall seal` signs the log head with an HMAC key, so truncating or rewriting the log is detected, even by someone who rebuilds the hash chain.
|
|
49
|
+
- **π Reports & Live View** *(new in 0.3)*: `sealwall report` makes a self-contained HTML (and CSV) evidence report. `sealwall tail` streams events live.
|
|
50
|
+
- **π§© Pluggable Classifier** *(new in 0.3)*: point `"classifier"` at any command (your own model or API wrapper) to judge tool outputs and tool descriptions. Non-zero exit means unsafe; failures fail closed.
|
|
51
|
+
- **π Secret Scanning & Redaction** *(new in 0.4)*: Blocks tool calls whose arguments contain AWS keys, GitHub/Slack tokens, API keys or private-key blocks, and redacts the same patterns from tool outputs. Disable with `"block_secrets": false` / `"redact_secrets": false`.
|
|
52
|
+
- **β‘ Zero Dependencies**: Pure Python standard library. Installs in seconds with zero supply-chain risk.
|
|
53
|
+
- **π₯οΈ Cross-Platform**: First-class support for Windows, macOS, and Linux.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## How It Works
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
ββββββββββββββββββ ββββββββββββββββββββββββββββββββββ ββββββββββββββββββ
|
|
61
|
+
β AI Client β β sealwall β β MCP Server β
|
|
62
|
+
β (Claude/Cursor)β ββJSON-RPCββ> 1. Intercept tools/call β β (FS, DB, CLI) β
|
|
63
|
+
β β β 2. Evaluate policy (allow/deny)β ββForwardββ> β
|
|
64
|
+
β β β 3. Log to hash-chained audit β β β
|
|
65
|
+
β β <ββββββββββ 4. Scan output for injections βββResponseββ β
|
|
66
|
+
β β β 5. Return sanitized response β β β
|
|
67
|
+
ββββββββββββββββββ ββββββββββββββββββββββββββββββββββ ββββββββββββββββββ
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Quickstart
|
|
73
|
+
|
|
74
|
+
### 1. Installation
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
git clone https://github.com/vishalmurugan1986/sealwall.git
|
|
78
|
+
cd sealwall
|
|
79
|
+
pip install .
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Or install in editable mode for local development:
|
|
83
|
+
```bash
|
|
84
|
+
pip install -e .
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 2. Run the Demo
|
|
88
|
+
|
|
89
|
+
Run the end-to-end demo and verify tampering detection:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
python demo.py
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Expected output:
|
|
96
|
+
```text
|
|
97
|
+
[1] Executing tool calls through sealwall firewall proxy:
|
|
98
|
+
|
|
99
|
+
read_file -> file contents
|
|
100
|
+
read_file -> Blocked by sealwall: argument matches \.ssh
|
|
101
|
+
delete_file -> Blocked by sealwall: destructive action
|
|
102
|
+
send_email -> Blocked by sealwall: outbound communication (human review: fail-closed)
|
|
103
|
+
fetch_page -> [sealwall] Output withheld: possible prompt injection
|
|
104
|
+
|
|
105
|
+
[2] Verifying cryptographic hash chain integrity:
|
|
106
|
+
Audit log intact
|
|
107
|
+
|
|
108
|
+
[3] Simulating log tampering (mutating line 1 'allow' -> 'deny'):
|
|
109
|
+
TAMPERED at line 1
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## CLI Usage
|
|
115
|
+
|
|
116
|
+
### Wrapping an MCP Server
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
sealwall --policy policy.json --log audit.jsonl -- <mcp-server-command>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Example:** Wrapping the standard filesystem MCP server:
|
|
123
|
+
```bash
|
|
124
|
+
sealwall --policy policy.json --log audit.jsonl -- npx -y @modelcontextprotocol/server-filesystem ./workspace
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Options
|
|
128
|
+
|
|
129
|
+
| Flag | Description | Default |
|
|
130
|
+
|------|-------------|---------|
|
|
131
|
+
| `--policy <path>` | Path to JSON policy file (**required**) | - |
|
|
132
|
+
| `--log <path>` | Path to write hash-chained JSONL audit log | `audit.jsonl` |
|
|
133
|
+
| `--interactive` | Prompt for human approval in terminal for `"action": "ask"` | `False` (fail-closed) |
|
|
134
|
+
| `--ask-timeout <sec>` | Seconds to wait for approval before failing closed | `15.0` |
|
|
135
|
+
| `verify <path>` | Audit verification command to validate integrity | - |
|
|
136
|
+
|
|
137
|
+
### Verifying Audit Log Integrity
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
sealwall verify audit.jsonl
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
If any past event, argument, timestamp, or line in `audit.jsonl` was modified or deleted:
|
|
144
|
+
```text
|
|
145
|
+
TAMPERED at line 1
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Configuration
|
|
151
|
+
|
|
152
|
+
### Policy Schema (`policy.json`)
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"default": "deny",
|
|
157
|
+
"deny_args": [
|
|
158
|
+
"\\.ssh",
|
|
159
|
+
"\\.env",
|
|
160
|
+
"id_rsa",
|
|
161
|
+
"api[_-]?key"
|
|
162
|
+
],
|
|
163
|
+
"rules": [
|
|
164
|
+
{ "tool": "read_*", "action": "allow" },
|
|
165
|
+
{ "tool": "fetch_*", "action": "allow" },
|
|
166
|
+
{ "tool": "send_*", "action": "ask", "reason": "outbound communication" },
|
|
167
|
+
{ "tool": "delete_*", "action": "deny", "reason": "destructive action" }
|
|
168
|
+
]
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
- **`rules`**: Match tool names using standard wildcards (`*`, `?`). Actions:
|
|
173
|
+
- `allow`: Permit execution immediately.
|
|
174
|
+
- `deny`: Block execution with JSON-RPC error.
|
|
175
|
+
- `ask`: Require human approval. In non-interactive environments (Claude Desktop / background processes), automatically fails closed.
|
|
176
|
+
- **`deny_args`**: Regular expressions evaluated against all tool arguments. Matches are blocked regardless of the tool rule.
|
|
177
|
+
- **`default`**: Fallback action when no rules match (`"deny"` recommended).
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Integration with Claude Desktop & Cursor
|
|
182
|
+
|
|
183
|
+
Add `sealwall` as the wrapper command in your `claude_desktop_config.json`:
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"mcpServers": {
|
|
188
|
+
"secure-filesystem": {
|
|
189
|
+
"command": "sealwall",
|
|
190
|
+
"args": [
|
|
191
|
+
"--policy", "/path/to/policy.json",
|
|
192
|
+
"--log", "/path/to/audit.jsonl",
|
|
193
|
+
"--",
|
|
194
|
+
"npx", "-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/dir"
|
|
195
|
+
]
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Running Tests
|
|
204
|
+
|
|
205
|
+
Run the built-in test suite:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
python -m unittest test_sealwall.py
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Operations
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
sealwall keygen audit.key # create an HMAC key (keep it off this machine)
|
|
217
|
+
sealwall seal audit.jsonl --key audit.key # sign the current log head -> audit.jsonl.seal
|
|
218
|
+
sealwall verify audit.jsonl --key audit.key # chain check + seal check (appends after sealing are fine)
|
|
219
|
+
sealwall report audit.jsonl --out report.html --csv events.csv
|
|
220
|
+
sealwall tail audit.jsonl # live colored event stream
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Policy additions:
|
|
224
|
+
|
|
225
|
+
```json
|
|
226
|
+
{"allow_paths": ["/home/me/project"], "deny_paths": ["/home/me/.config"],
|
|
227
|
+
"classifier": ["python", "my_classifier.py"]}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Relative paths are resolved against the directory sealwall runs in. The classifier receives text on stdin and must exit 0 for safe.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Benchmark: `bench.py`
|
|
235
|
+
|
|
236
|
+
A reproducible suite of 12 attacks (SSH key read, `../` traversal, symlink escape, case tricks, `.env`, destructive delete, secrets in arguments, JSON-RPC batch bypass, tool poisoning, prompt injection in output, secret in output, rug pull) plus a control that checks legitimate use still works. It checks what **actually reached the server or the client**, so it works against any stdio proxy:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
python bench.py --wrap "" # baseline: no proxy
|
|
240
|
+
python bench.py --wrap "sealwall --policy policy.json --log b.jsonl --"
|
|
241
|
+
python bench.py --wrap "npx -y mcpwall --" # or any other proxy
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Honest notes: I wrote this suite, so sealwall naturally covers it; treat it as a regression test, not an independent audit. Results depend on each tool's configuration, so always publish the config with the numbers. Pull requests adding attacks that sealwall misses are welcome.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Realistic Scope & Roadmap
|
|
249
|
+
|
|
250
|
+
- **Status (v0.4.0)**: stdio and streamable-HTTP proxy, fail-closed policy with path allowlists, tool-poisoning and rug-pull checks, signed tamper-evident logs, HTML/CSV reports.
|
|
251
|
+
- **Tested against real servers**: `@modelcontextprotocol/server-filesystem` (stdio) and `@modelcontextprotocol/server-everything` (streamable HTTP), plus 23 unit tests and the 12-attack benchmark.
|
|
252
|
+
- **Known limits (please read)**:
|
|
253
|
+
- Path checks have a time-of-check/time-of-use gap (a symlink swapped after the check), and they only inspect arguments that look like paths. Shell commands passed as strings are not parsed.
|
|
254
|
+
- Injection and poisoning detection is pattern-based unless you plug in a classifier. It will miss paraphrased or encoded attacks.
|
|
255
|
+
- HTTP mode buffers responses (no long-lived streams), rejects `GET`/`DELETE`, and does not inspect server-initiated messages (e.g. sampling).
|
|
256
|
+
- JSON-RPC batches are rejected. The pin file is trust-on-first-use.
|
|
257
|
+
- The report maps controls to SOC 2 / HIPAA / ISO items for convenience. It is evidence, not certification.
|
|
258
|
+
- **Roadmap**:
|
|
259
|
+
- [x] Remote MCP over HTTP
|
|
260
|
+
- [x] Tool-description scanning and tool pinning
|
|
261
|
+
- [x] Path allowlists (symlink and traversal safe)
|
|
262
|
+
- [x] Signed audit heads, HTML/CSV reports, live tail
|
|
263
|
+
- [x] Pluggable classifier hook
|
|
264
|
+
- [ ] Bundled ML injection classifier
|
|
265
|
+
- [ ] Hosted multi-user dashboard
|
|
266
|
+
- [ ] Streaming (long-lived SSE) support
|
|
267
|
+
- [ ] Shell-command argument parsing
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Author & Support
|
|
272
|
+
|
|
273
|
+
Created and maintained by **[Vishal Murugan](https://github.com/vishalmurugan1986)**.
|
|
274
|
+
|
|
275
|
+
For pilot inquiries, enterprise security reviews, or questions, please open an [issue](https://github.com/vishalmurugan1986/sealwall/issues) or reach out directly.
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## License
|
|
280
|
+
|
|
281
|
+
MIT License. See [LICENSE](LICENSE) for details. Copyright (c) 2026 Vishal Murugan.
|
sealwall-0.1.0/README.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# sealwall
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://github.com/vishalmurugan1986/sealwall"><img src="https://img.shields.io/badge/version-0.1.0-blue.svg" alt="Version 0.1.0" /></a>
|
|
5
|
+
<a href="https://python.org"><img src="https://img.shields.io/badge/python-3.9+-3776ab.svg" alt="Python 3.9+" /></a>
|
|
6
|
+
<a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/MCP-Compatible-9333ea.svg" alt="MCP Compatible" /></a>
|
|
7
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License: MIT" /></a>
|
|
8
|
+
<a href="#running-tests"><img src="https://img.shields.io/badge/tests-23%20passed-success.svg" alt="Tests" /></a>
|
|
9
|
+
<a href="https://github.com/vishalmurugan1986/sealwall/actions"><img src="https://github.com/vishalmurugan1986/sealwall/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
|
|
10
|
+
<img src="https://img.shields.io/badge/dependencies-0-black.svg" alt="Zero Dependencies" />
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<strong>A zero-dependency firewall and tamper-evident audit log proxy for Model Context Protocol (MCP) AI agents.</strong>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
AI agents can be hijacked by untrusted text they encounterβmalicious web pages, documents, or emails can instruct agents to leak files, exfiltrate API keys, or run destructive tools.
|
|
20
|
+
|
|
21
|
+
`sealwall` is a lightweight, local-first proxy that sits between your AI client (Claude Desktop, Cursor, local agents) and any MCP stdio server. It enforces fine-grained policy **before** execution, intercepts prompt injections in tool outputs, and records every action in a cryptographically chained, tamper-evident audit log.
|
|
22
|
+
|
|
23
|
+
<p align="center">
|
|
24
|
+
<img src="assets/demo.gif" alt="sealwall terminal demo" width="760" />
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Key Features
|
|
30
|
+
|
|
31
|
+
- **π‘οΈ Fail-Closed Policy Enforcement**: Default-deny model. Define exact tool whitelists, regex argument restrictions (e.g., blocking `.ssh`, `.env`, `id_rsa`), and require approval for sensitive tools.
|
|
32
|
+
- **π Tamper-Evident Audit Chain**: Every tool call, argument, and output is hashed with SHA-256 into a SHA-256 hash chain. Modifying, deleting, or reordering past entries breaks verification. (Someone with write access to the whole file could rebuild the chain, so store the latest hash somewhere else for strong guarantees.)
|
|
33
|
+
- **π Output Injection Defense**: Scans tool responses for common prompt-injection / goal-hijacking triggers and withholds dangerous content from entering the agent's context window.
|
|
34
|
+
- **β οΈ Tool-Poisoning & Rug-Pull Defense** *(new in 0.2)*: Scans `tools/list` for hidden instructions in tool descriptions (e.g. `<IMPORTANT>` blocks telling the model to read `~/.ssh`) and removes those tools. Tool definitions are pinned on first sight; if a definition later changes, the tool is blocked until you re-approve with `--accept-changes`.
|
|
35
|
+
- **π Remote MCP (streamable HTTP)** *(new in 0.2)*: Run `sealwall --policy policy.json --http-upstream https://host/mcp` and point your client at `http://127.0.0.1:8787/`. Same policy, same audit log.
|
|
36
|
+
- **π Path Allowlists** *(new in 0.3)*: `"allow_paths": ["/home/me/project"]` and `"deny_paths"`. Paths in tool arguments are expanded (`~`, `$VAR`, `file://`), resolved through symlinks and `..`, then checked. This closes most of the regex-bypass tricks.
|
|
37
|
+
- **π Signed Audit Heads** *(new in 0.3)*: `sealwall seal` signs the log head with an HMAC key, so truncating or rewriting the log is detected, even by someone who rebuilds the hash chain.
|
|
38
|
+
- **π Reports & Live View** *(new in 0.3)*: `sealwall report` makes a self-contained HTML (and CSV) evidence report. `sealwall tail` streams events live.
|
|
39
|
+
- **π§© Pluggable Classifier** *(new in 0.3)*: point `"classifier"` at any command (your own model or API wrapper) to judge tool outputs and tool descriptions. Non-zero exit means unsafe; failures fail closed.
|
|
40
|
+
- **π Secret Scanning & Redaction** *(new in 0.4)*: Blocks tool calls whose arguments contain AWS keys, GitHub/Slack tokens, API keys or private-key blocks, and redacts the same patterns from tool outputs. Disable with `"block_secrets": false` / `"redact_secrets": false`.
|
|
41
|
+
- **β‘ Zero Dependencies**: Pure Python standard library. Installs in seconds with zero supply-chain risk.
|
|
42
|
+
- **π₯οΈ Cross-Platform**: First-class support for Windows, macOS, and Linux.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## How It Works
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
ββββββββββββββββββ ββββββββββββββββββββββββββββββββββ ββββββββββββββββββ
|
|
50
|
+
β AI Client β β sealwall β β MCP Server β
|
|
51
|
+
β (Claude/Cursor)β ββJSON-RPCββ> 1. Intercept tools/call β β (FS, DB, CLI) β
|
|
52
|
+
β β β 2. Evaluate policy (allow/deny)β ββForwardββ> β
|
|
53
|
+
β β β 3. Log to hash-chained audit β β β
|
|
54
|
+
β β <ββββββββββ 4. Scan output for injections βββResponseββ β
|
|
55
|
+
β β β 5. Return sanitized response β β β
|
|
56
|
+
ββββββββββββββββββ ββββββββββββββββββββββββββββββββββ ββββββββββββββββββ
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Quickstart
|
|
62
|
+
|
|
63
|
+
### 1. Installation
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
git clone https://github.com/vishalmurugan1986/sealwall.git
|
|
67
|
+
cd sealwall
|
|
68
|
+
pip install .
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Or install in editable mode for local development:
|
|
72
|
+
```bash
|
|
73
|
+
pip install -e .
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 2. Run the Demo
|
|
77
|
+
|
|
78
|
+
Run the end-to-end demo and verify tampering detection:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
python demo.py
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Expected output:
|
|
85
|
+
```text
|
|
86
|
+
[1] Executing tool calls through sealwall firewall proxy:
|
|
87
|
+
|
|
88
|
+
read_file -> file contents
|
|
89
|
+
read_file -> Blocked by sealwall: argument matches \.ssh
|
|
90
|
+
delete_file -> Blocked by sealwall: destructive action
|
|
91
|
+
send_email -> Blocked by sealwall: outbound communication (human review: fail-closed)
|
|
92
|
+
fetch_page -> [sealwall] Output withheld: possible prompt injection
|
|
93
|
+
|
|
94
|
+
[2] Verifying cryptographic hash chain integrity:
|
|
95
|
+
Audit log intact
|
|
96
|
+
|
|
97
|
+
[3] Simulating log tampering (mutating line 1 'allow' -> 'deny'):
|
|
98
|
+
TAMPERED at line 1
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## CLI Usage
|
|
104
|
+
|
|
105
|
+
### Wrapping an MCP Server
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
sealwall --policy policy.json --log audit.jsonl -- <mcp-server-command>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Example:** Wrapping the standard filesystem MCP server:
|
|
112
|
+
```bash
|
|
113
|
+
sealwall --policy policy.json --log audit.jsonl -- npx -y @modelcontextprotocol/server-filesystem ./workspace
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Options
|
|
117
|
+
|
|
118
|
+
| Flag | Description | Default |
|
|
119
|
+
|------|-------------|---------|
|
|
120
|
+
| `--policy <path>` | Path to JSON policy file (**required**) | - |
|
|
121
|
+
| `--log <path>` | Path to write hash-chained JSONL audit log | `audit.jsonl` |
|
|
122
|
+
| `--interactive` | Prompt for human approval in terminal for `"action": "ask"` | `False` (fail-closed) |
|
|
123
|
+
| `--ask-timeout <sec>` | Seconds to wait for approval before failing closed | `15.0` |
|
|
124
|
+
| `verify <path>` | Audit verification command to validate integrity | - |
|
|
125
|
+
|
|
126
|
+
### Verifying Audit Log Integrity
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
sealwall verify audit.jsonl
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
If any past event, argument, timestamp, or line in `audit.jsonl` was modified or deleted:
|
|
133
|
+
```text
|
|
134
|
+
TAMPERED at line 1
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Configuration
|
|
140
|
+
|
|
141
|
+
### Policy Schema (`policy.json`)
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"default": "deny",
|
|
146
|
+
"deny_args": [
|
|
147
|
+
"\\.ssh",
|
|
148
|
+
"\\.env",
|
|
149
|
+
"id_rsa",
|
|
150
|
+
"api[_-]?key"
|
|
151
|
+
],
|
|
152
|
+
"rules": [
|
|
153
|
+
{ "tool": "read_*", "action": "allow" },
|
|
154
|
+
{ "tool": "fetch_*", "action": "allow" },
|
|
155
|
+
{ "tool": "send_*", "action": "ask", "reason": "outbound communication" },
|
|
156
|
+
{ "tool": "delete_*", "action": "deny", "reason": "destructive action" }
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
- **`rules`**: Match tool names using standard wildcards (`*`, `?`). Actions:
|
|
162
|
+
- `allow`: Permit execution immediately.
|
|
163
|
+
- `deny`: Block execution with JSON-RPC error.
|
|
164
|
+
- `ask`: Require human approval. In non-interactive environments (Claude Desktop / background processes), automatically fails closed.
|
|
165
|
+
- **`deny_args`**: Regular expressions evaluated against all tool arguments. Matches are blocked regardless of the tool rule.
|
|
166
|
+
- **`default`**: Fallback action when no rules match (`"deny"` recommended).
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Integration with Claude Desktop & Cursor
|
|
171
|
+
|
|
172
|
+
Add `sealwall` as the wrapper command in your `claude_desktop_config.json`:
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"mcpServers": {
|
|
177
|
+
"secure-filesystem": {
|
|
178
|
+
"command": "sealwall",
|
|
179
|
+
"args": [
|
|
180
|
+
"--policy", "/path/to/policy.json",
|
|
181
|
+
"--log", "/path/to/audit.jsonl",
|
|
182
|
+
"--",
|
|
183
|
+
"npx", "-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/dir"
|
|
184
|
+
]
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Running Tests
|
|
193
|
+
|
|
194
|
+
Run the built-in test suite:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
python -m unittest test_sealwall.py
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Operations
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
sealwall keygen audit.key # create an HMAC key (keep it off this machine)
|
|
206
|
+
sealwall seal audit.jsonl --key audit.key # sign the current log head -> audit.jsonl.seal
|
|
207
|
+
sealwall verify audit.jsonl --key audit.key # chain check + seal check (appends after sealing are fine)
|
|
208
|
+
sealwall report audit.jsonl --out report.html --csv events.csv
|
|
209
|
+
sealwall tail audit.jsonl # live colored event stream
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Policy additions:
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{"allow_paths": ["/home/me/project"], "deny_paths": ["/home/me/.config"],
|
|
216
|
+
"classifier": ["python", "my_classifier.py"]}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Relative paths are resolved against the directory sealwall runs in. The classifier receives text on stdin and must exit 0 for safe.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Benchmark: `bench.py`
|
|
224
|
+
|
|
225
|
+
A reproducible suite of 12 attacks (SSH key read, `../` traversal, symlink escape, case tricks, `.env`, destructive delete, secrets in arguments, JSON-RPC batch bypass, tool poisoning, prompt injection in output, secret in output, rug pull) plus a control that checks legitimate use still works. It checks what **actually reached the server or the client**, so it works against any stdio proxy:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
python bench.py --wrap "" # baseline: no proxy
|
|
229
|
+
python bench.py --wrap "sealwall --policy policy.json --log b.jsonl --"
|
|
230
|
+
python bench.py --wrap "npx -y mcpwall --" # or any other proxy
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Honest notes: I wrote this suite, so sealwall naturally covers it; treat it as a regression test, not an independent audit. Results depend on each tool's configuration, so always publish the config with the numbers. Pull requests adding attacks that sealwall misses are welcome.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Realistic Scope & Roadmap
|
|
238
|
+
|
|
239
|
+
- **Status (v0.4.0)**: stdio and streamable-HTTP proxy, fail-closed policy with path allowlists, tool-poisoning and rug-pull checks, signed tamper-evident logs, HTML/CSV reports.
|
|
240
|
+
- **Tested against real servers**: `@modelcontextprotocol/server-filesystem` (stdio) and `@modelcontextprotocol/server-everything` (streamable HTTP), plus 23 unit tests and the 12-attack benchmark.
|
|
241
|
+
- **Known limits (please read)**:
|
|
242
|
+
- Path checks have a time-of-check/time-of-use gap (a symlink swapped after the check), and they only inspect arguments that look like paths. Shell commands passed as strings are not parsed.
|
|
243
|
+
- Injection and poisoning detection is pattern-based unless you plug in a classifier. It will miss paraphrased or encoded attacks.
|
|
244
|
+
- HTTP mode buffers responses (no long-lived streams), rejects `GET`/`DELETE`, and does not inspect server-initiated messages (e.g. sampling).
|
|
245
|
+
- JSON-RPC batches are rejected. The pin file is trust-on-first-use.
|
|
246
|
+
- The report maps controls to SOC 2 / HIPAA / ISO items for convenience. It is evidence, not certification.
|
|
247
|
+
- **Roadmap**:
|
|
248
|
+
- [x] Remote MCP over HTTP
|
|
249
|
+
- [x] Tool-description scanning and tool pinning
|
|
250
|
+
- [x] Path allowlists (symlink and traversal safe)
|
|
251
|
+
- [x] Signed audit heads, HTML/CSV reports, live tail
|
|
252
|
+
- [x] Pluggable classifier hook
|
|
253
|
+
- [ ] Bundled ML injection classifier
|
|
254
|
+
- [ ] Hosted multi-user dashboard
|
|
255
|
+
- [ ] Streaming (long-lived SSE) support
|
|
256
|
+
- [ ] Shell-command argument parsing
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## Author & Support
|
|
261
|
+
|
|
262
|
+
Created and maintained by **[Vishal Murugan](https://github.com/vishalmurugan1986)**.
|
|
263
|
+
|
|
264
|
+
For pilot inquiries, enterprise security reviews, or questions, please open an [issue](https://github.com/vishalmurugan1986/sealwall/issues) or reach out directly.
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## License
|
|
269
|
+
|
|
270
|
+
MIT License. See [LICENSE](LICENSE) for details. Copyright (c) 2026 Vishal Murugan.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sealwall"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Firewall and tamper-evident audit log for AI agents using MCP"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Vishal Murugan"}
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.scripts]
|
|
17
|
+
sealwall = "sealwall:main"
|
|
18
|
+
|
|
19
|
+
[tool.setuptools]
|
|
20
|
+
py-modules = ["sealwall"]
|