tessen-cli 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.
Files changed (27) hide show
  1. tessen_cli-0.1.0/PKG-INFO +301 -0
  2. tessen_cli-0.1.0/README.md +273 -0
  3. tessen_cli-0.1.0/pyproject.toml +52 -0
  4. tessen_cli-0.1.0/src/tessen/__init__.py +1 -0
  5. tessen_cli-0.1.0/src/tessen/cli.py +206 -0
  6. tessen_cli-0.1.0/src/tessen/models.py +57 -0
  7. tessen_cli-0.1.0/src/tessen/output/__init__.py +0 -0
  8. tessen_cli-0.1.0/src/tessen/output/human.py +98 -0
  9. tessen_cli-0.1.0/src/tessen/output/json_output.py +68 -0
  10. tessen_cli-0.1.0/src/tessen/parsers/__init__.py +0 -0
  11. tessen_cli-0.1.0/src/tessen/parsers/auto.py +77 -0
  12. tessen_cli-0.1.0/src/tessen/parsers/claude_code.py +42 -0
  13. tessen_cli-0.1.0/src/tessen/parsers/claude_desktop.py +22 -0
  14. tessen_cli-0.1.0/src/tessen/parsers/common.py +66 -0
  15. tessen_cli-0.1.0/src/tessen/parsers/cursor.py +38 -0
  16. tessen_cli-0.1.0/src/tessen/rules/__init__.py +0 -0
  17. tessen_cli-0.1.0/src/tessen/rules/base.py +29 -0
  18. tessen_cli-0.1.0/src/tessen/rules/builtin/__init__.py +30 -0
  19. tessen_cli-0.1.0/src/tessen/rules/builtin/auto_approve.py +98 -0
  20. tessen_cli-0.1.0/src/tessen/rules/builtin/dangerous_command.py +89 -0
  21. tessen_cli-0.1.0/src/tessen/rules/builtin/hook_injection.py +153 -0
  22. tessen_cli-0.1.0/src/tessen/rules/builtin/http_no_auth.py +99 -0
  23. tessen_cli-0.1.0/src/tessen/rules/builtin/known_vulnerable.py +159 -0
  24. tessen_cli-0.1.0/src/tessen/rules/builtin/secrets_in_env.py +112 -0
  25. tessen_cli-0.1.0/src/tessen/rules/builtin/suspicious_args.py +91 -0
  26. tessen_cli-0.1.0/src/tessen/rules/engine.py +36 -0
  27. tessen_cli-0.1.0/src/tessen/rules/models.py +52 -0
@@ -0,0 +1,301 @@
1
+ Metadata-Version: 2.4
2
+ Name: tessen-cli
3
+ Version: 0.1.0
4
+ Summary: Security scanner for AI agent configurations (MCP servers, Claude Desktop, Cursor, Claude Code, Windsurf).
5
+ Keywords: security,scanner,mcp,ai-agents,claude,cursor,devsecops
6
+ Author: Slim Ben Tanfous
7
+ Author-email: Slim Ben Tanfous <slimbentanfous2002@gmail.com>
8
+ License-Expression: MIT
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Information Technology
13
+ Classifier: Topic :: Security
14
+ Classifier: Topic :: Software Development :: Quality Assurance
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Requires-Dist: pydantic>=2.13.4
20
+ Requires-Dist: pyyaml>=6.0.3
21
+ Requires-Dist: rich>=15.0.0
22
+ Requires-Dist: typer>=0.26.7
23
+ Requires-Python: >=3.11
24
+ Project-URL: Homepage, https://github.com/SlimBenTanfous1/tessen-cli
25
+ Project-URL: Repository, https://github.com/SlimBenTanfous1/tessen-cli
26
+ Project-URL: Issues, https://github.com/SlimBenTanfous1/tessen-cli/issues
27
+ Description-Content-Type: text/markdown
28
+
29
+ # tessen
30
+
31
+ ![CI](https://github.com/SlimBenTanfous1/tessen-cli/actions/workflows/ci.yml/badge.svg)
32
+
33
+ **Security scanner for AI agent configurations.**
34
+
35
+ Tessen scans MCP (Model Context Protocol) configurations — the files that tell Claude Desktop, Claude Code, Cursor, and Windsurf which tools to load — for vulnerabilities that current tooling misses: plaintext credentials, dangerous invocation patterns, invisible Unicode instruction carriers, and filesystem-wide access grants.
36
+
37
+ Point it at your config, get findings in seconds, wire it into CI to keep them from coming back.
38
+
39
+ ```
40
+ tessen scan
41
+ ```
42
+
43
+ > Named after the Japanese 鉄扇 (_tessen_), an iron war fan carried into places swords were forbidden. It looked like an ordinary fan. It was a weapon. So are the MCP servers hiding in your `claude_desktop_config.json`.
44
+
45
+ ---
46
+
47
+ ## Status
48
+
49
+ **Alpha** (`v0.1.0`). The scanner works, the tests pass, but it hasn't been published to PyPI yet and the ruleset is small. Expect breaking changes to the rule format and CLI flags before `v0.2`. If you use it, run it locally, don't wire it into production CI yet.
50
+
51
+ Current coverage: **3 built-in rules**, **3 client formats**, **26 tests**.
52
+
53
+ ---
54
+
55
+ ## What it catches
56
+
57
+ Every finding includes a severity, location in the config, message explaining the issue, and a remediation suggestion.
58
+
59
+ ### `tessen-secrets-in-env` — plaintext credentials
60
+
61
+ Detects API keys and tokens stored directly in MCP server environment variables. Recognizes patterns from GitHub (`ghp_`, `github_pat_`, `gho_`), Anthropic, OpenAI, AWS access keys, Google API keys, Slack tokens, Linear, and SendGrid. Distinguishes real secrets from environment variable references (`${VAR}`), placeholders (`<YOUR_KEY>`), and common redaction markers (`REDACTED`, `PLACEHOLDER`).
62
+
63
+ **Severity:** `critical`
64
+
65
+ ### `tessen-dangerous-command` — shell wrappers and curl-pipe-shell
66
+
67
+ Flags MCP servers invoked through a shell interpreter (`bash`, `sh`, `zsh`, `pwsh`, `cmd.exe`), which grants the server arbitrary command execution and makes any prompt injection catastrophic. Also detects `curl | sh`-style patterns that fetch and execute unverified remote code — a supply-chain risk that has produced multiple real-world incidents in 2025-2026.
68
+
69
+ **Severity:** `high` (shell wrappers), `critical` (curl-pipe-shell)
70
+
71
+ ### `tessen-suspicious-args` — Unicode injection and overbroad scopes
72
+
73
+ Two related detections. First, invisible Unicode characters (categories Cf and Cc — zero-width spaces, bidi overrides, format characters) in server arguments, which are the primary carrier for hidden prompt injection instructions. Second, filesystem-wide access grants like `/`, `/home`, or `~` passed to filesystem MCP servers.
74
+
75
+ **Severity:** `critical` (invisible Unicode), `high` (filesystem grants)
76
+
77
+ ---
78
+
79
+ ## Install
80
+
81
+ **From source (currently the only path):**
82
+
83
+ ```bash
84
+ git clone https://github.com/SlimBenTanfous1/tessen-cli.git
85
+ cd tessen-cli
86
+ uv pip install -e .
87
+ ```
88
+
89
+ Requires Python 3.11 or newer. Uses [uv](https://docs.astral.sh/uv/) as the package manager — install with `curl -LsSf https://astral.sh/uv/install.sh | sh`.
90
+
91
+ **PyPI distribution is planned for `v0.2`.** Once published:
92
+
93
+ ```bash
94
+ pipx install tessen # coming soon
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Usage
100
+
101
+ ### Auto-detect and scan everything
102
+
103
+ ```bash
104
+ tessen scan
105
+ ```
106
+
107
+ Walks the project tree upward from the current directory looking for `.mcp.json` (Claude Code) and `.cursor/mcp.json` (Cursor), then checks the OS-specific location for Claude Desktop (`~/.config/Claude/claude_desktop_config.json` on Linux, `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, `%APPDATA%\Claude\claude_desktop_config.json` on Windows). Scans every config it finds.
108
+
109
+ ### Scan a specific file
110
+
111
+ ```bash
112
+ tessen scan path/to/claude_desktop_config.json
113
+ tessen scan .cursor/mcp.json --client cursor
114
+ ```
115
+
116
+ The `--client` flag forces a specific parser when the filename doesn't match convention. Valid values: `claude_desktop`, `claude_code`, `cursor`.
117
+
118
+ ### Version
119
+
120
+ ```bash
121
+ tessen version
122
+ ```
123
+
124
+ ---
125
+
126
+ ## CI integration
127
+
128
+ Tessen exits non-zero when it finds issues at or above a severity threshold. This is the mechanism to keep vulnerable configs out of your `main` branch.
129
+
130
+ ```bash
131
+ tessen scan --fail-on high
132
+ ```
133
+
134
+ Exit codes: `0` for clean, `1` when the threshold is met, `2` for invalid flag values.
135
+
136
+ Valid `--fail-on` values: `critical`, `high`, `medium`, `low`, `never`.
137
+
138
+ **GitHub Actions example** (roughly — this pipeline hasn't been dogfooded yet):
139
+
140
+ ```yaml
141
+ name: MCP Config Security
142
+
143
+ on:
144
+ pull_request:
145
+ paths:
146
+ - ".mcp.json"
147
+ - ".cursor/mcp.json"
148
+ - ".claude/settings.json"
149
+
150
+ jobs:
151
+ scan:
152
+ runs-on: ubuntu-latest
153
+ steps:
154
+ - uses: actions/checkout@v4
155
+ - uses: actions/setup-python@v5
156
+ with:
157
+ python-version: "3.11"
158
+ - name: Install tessen
159
+ run: |
160
+ pip install git+https://github.com/SlimBenTanfous1/tessen-cli.git
161
+ - name: Scan MCP configs
162
+ run: tessen scan --fail-on high
163
+ ```
164
+
165
+ ---
166
+
167
+ ## Example output
168
+
169
+ Scanning a config with three planted issues (real GitHub PAT, bash wrapper, filesystem-wide grant):
170
+
171
+ ```
172
+ tessen scanning /path/to/claude_desktop_with_secrets.json
173
+
174
+ Findings for Claude Desktop 1 critical · 2 high
175
+
176
+ ╭─ ● [CRITICAL] GitHub Personal Access Token exposed in plaintext ─────╮
177
+ │ The env var 'GITHUB_PERSONAL_ACCESS_TOKEN' in server │
178
+ │ 'github_real_leak' contains a value matching a GitHub Personal │
179
+ │ Access Token pattern. │
180
+ │ │
181
+ │ Location: mcpServers.github_real_leak.env.GITHUB_PERSONAL_... │
182
+ │ Rule: tessen-secrets-in-env │
183
+ │ Remediation: Move the secret to a secure store (1Password, Vault, │
184
+ │ or OS keychain) and reference it via ${VAR_NAME}. │
185
+ ╰──────────────────────────────────────────────────────────────────────╯
186
+
187
+ Scan summary
188
+ Total findings: 3
189
+ Critical 1
190
+ High 2
191
+ ```
192
+
193
+ ---
194
+
195
+ ## Supported clients
196
+
197
+ | Client | Config path | Parser |
198
+ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
199
+ | Claude Desktop | `~/.config/Claude/claude_desktop_config.json` (Linux)<br>`~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)<br>`%APPDATA%\Claude\claude_desktop_config.json` (Windows) | `claude_desktop` |
200
+ | Claude Code | `<project>/.mcp.json` | `claude_code` |
201
+ | Cursor | `<project>/.cursor/mcp.json` | `cursor` |
202
+ | Windsurf | Planned for `v0.2` | — |
203
+
204
+ ---
205
+
206
+ ## Development
207
+
208
+ ```bash
209
+ git clone https://github.com/SlimBenTanfous1/tessen-cli.git
210
+ cd tessen-cli
211
+ uv sync # install runtime + dev deps
212
+ source .venv/bin/activate # or use `uv run <command>`
213
+
214
+ # Run the test suite
215
+ pytest tests/ -v
216
+
217
+ # Run tessen against test fixtures
218
+ tessen scan tests/fixtures/claude_desktop_with_secrets.json
219
+ tessen scan tests/fixtures/claude_desktop_clean.json
220
+ ```
221
+
222
+ ### Project layout
223
+
224
+ ```
225
+ tessen-cli/
226
+ ├── src/tessen/
227
+ │ ├── cli.py # Typer app: scan + version + --fail-on
228
+ │ ├── models.py # MCPConfig, ParseResult, ClientType
229
+ │ ├── output/human.py # Rich severity panels
230
+ │ ├── parsers/
231
+ │ │ ├── common.py # Shared JSON+Pydantic parse logic
232
+ │ │ ├── claude_desktop.py
233
+ │ │ ├── claude_code.py # + walker for .mcp.json
234
+ │ │ ├── cursor.py # + walker for .cursor/mcp.json
235
+ │ │ └── auto.py # Multi-client auto-detection
236
+ │ └── rules/
237
+ │ ├── base.py # Rule ABC
238
+ │ ├── engine.py # RuleEngine with buggy-rule isolation
239
+ │ ├── models.py # Finding, Severity, RuleMetadata
240
+ │ └── builtin/ # Built-in detection rules
241
+ └── tests/ # 26 tests, ~0.3s runtime
242
+ ```
243
+
244
+ ### Adding a rule
245
+
246
+ 1. Create `src/tessen/rules/builtin/your_rule.py`
247
+ 2. Subclass `Rule`, define `METADATA`, implement `check(result) -> list[Finding]`
248
+ 3. Register it in `src/tessen/rules/builtin/__init__.py` (`BUILTIN_RULES` list)
249
+ 4. Add tests in `tests/test_rules.py`
250
+
251
+ Rules should be pure functions: no I/O, no state, no side effects. Same input → same output.
252
+
253
+ ---
254
+
255
+ ## Roadmap
256
+
257
+ **`v0.1` (current) — foundation:** parsers for 3 clients, auto-detection, 3 detection rules, Rich terminal output, `--fail-on` for CI.
258
+
259
+ **`v0.2` — coverage:**
260
+
261
+ - 4 more rules: known-vulnerable MCP servers (CVE feed), HTTP transport without auth, dangerous auto-approve flags, hook injection in `.claude/settings.json`
262
+ - JSON output format
263
+ - SARIF output for GitHub Advanced Security compatibility
264
+ - Windsurf parser
265
+ - PyPI distribution (`pipx install tessen`)
266
+
267
+ **`v0.3` — trust:**
268
+
269
+ - GitHub Action for one-line CI integration
270
+ - CVE feed as a subscribable JSON endpoint
271
+ - First round of dogfooded findings on `awesome-mcp-servers`
272
+
273
+ **Later:**
274
+
275
+ - Custom rules via YAML
276
+ - Baseline files (`.tessen-baseline`) to suppress known findings
277
+ - Runtime hooks that observe tool calls at execution time
278
+
279
+ Anything you want prioritized? Open an issue.
280
+
281
+ ---
282
+
283
+ ## Why "Tessen"
284
+
285
+ A _tessen_ (鉄扇) is an iron folding fan carried by samurai as a concealed weapon into places where swords were forbidden — tea houses, temples, palaces. Ordinary-looking. Genuinely dangerous.
286
+
287
+ Same shape as a poisoned MCP server: a small config file that looks like plumbing, and behaves like an attack.
288
+
289
+ ---
290
+
291
+ ## License
292
+
293
+ MIT. See [LICENSE](./LICENSE).
294
+
295
+ ---
296
+
297
+ ## About
298
+
299
+ Built by [Slim Ben Tanfous](https://github.com/SlimBenTanfous1) — cybersecurity engineer at BrightByDesign (Zero Trust infrastructure, CI/CD security), joining the ESGI Paris Master's in Cybersecurity in September 2026. Prior: CCNA, CyberOps Associate, AWS Cloud Security Foundations, OCI Foundations.
300
+
301
+ Reach out: [github.com/SlimBenTanfous1](https://github.com/SlimBenTanfous1)
@@ -0,0 +1,273 @@
1
+ # tessen
2
+
3
+ ![CI](https://github.com/SlimBenTanfous1/tessen-cli/actions/workflows/ci.yml/badge.svg)
4
+
5
+ **Security scanner for AI agent configurations.**
6
+
7
+ Tessen scans MCP (Model Context Protocol) configurations — the files that tell Claude Desktop, Claude Code, Cursor, and Windsurf which tools to load — for vulnerabilities that current tooling misses: plaintext credentials, dangerous invocation patterns, invisible Unicode instruction carriers, and filesystem-wide access grants.
8
+
9
+ Point it at your config, get findings in seconds, wire it into CI to keep them from coming back.
10
+
11
+ ```
12
+ tessen scan
13
+ ```
14
+
15
+ > Named after the Japanese 鉄扇 (_tessen_), an iron war fan carried into places swords were forbidden. It looked like an ordinary fan. It was a weapon. So are the MCP servers hiding in your `claude_desktop_config.json`.
16
+
17
+ ---
18
+
19
+ ## Status
20
+
21
+ **Alpha** (`v0.1.0`). The scanner works, the tests pass, but it hasn't been published to PyPI yet and the ruleset is small. Expect breaking changes to the rule format and CLI flags before `v0.2`. If you use it, run it locally, don't wire it into production CI yet.
22
+
23
+ Current coverage: **3 built-in rules**, **3 client formats**, **26 tests**.
24
+
25
+ ---
26
+
27
+ ## What it catches
28
+
29
+ Every finding includes a severity, location in the config, message explaining the issue, and a remediation suggestion.
30
+
31
+ ### `tessen-secrets-in-env` — plaintext credentials
32
+
33
+ Detects API keys and tokens stored directly in MCP server environment variables. Recognizes patterns from GitHub (`ghp_`, `github_pat_`, `gho_`), Anthropic, OpenAI, AWS access keys, Google API keys, Slack tokens, Linear, and SendGrid. Distinguishes real secrets from environment variable references (`${VAR}`), placeholders (`<YOUR_KEY>`), and common redaction markers (`REDACTED`, `PLACEHOLDER`).
34
+
35
+ **Severity:** `critical`
36
+
37
+ ### `tessen-dangerous-command` — shell wrappers and curl-pipe-shell
38
+
39
+ Flags MCP servers invoked through a shell interpreter (`bash`, `sh`, `zsh`, `pwsh`, `cmd.exe`), which grants the server arbitrary command execution and makes any prompt injection catastrophic. Also detects `curl | sh`-style patterns that fetch and execute unverified remote code — a supply-chain risk that has produced multiple real-world incidents in 2025-2026.
40
+
41
+ **Severity:** `high` (shell wrappers), `critical` (curl-pipe-shell)
42
+
43
+ ### `tessen-suspicious-args` — Unicode injection and overbroad scopes
44
+
45
+ Two related detections. First, invisible Unicode characters (categories Cf and Cc — zero-width spaces, bidi overrides, format characters) in server arguments, which are the primary carrier for hidden prompt injection instructions. Second, filesystem-wide access grants like `/`, `/home`, or `~` passed to filesystem MCP servers.
46
+
47
+ **Severity:** `critical` (invisible Unicode), `high` (filesystem grants)
48
+
49
+ ---
50
+
51
+ ## Install
52
+
53
+ **From source (currently the only path):**
54
+
55
+ ```bash
56
+ git clone https://github.com/SlimBenTanfous1/tessen-cli.git
57
+ cd tessen-cli
58
+ uv pip install -e .
59
+ ```
60
+
61
+ Requires Python 3.11 or newer. Uses [uv](https://docs.astral.sh/uv/) as the package manager — install with `curl -LsSf https://astral.sh/uv/install.sh | sh`.
62
+
63
+ **PyPI distribution is planned for `v0.2`.** Once published:
64
+
65
+ ```bash
66
+ pipx install tessen # coming soon
67
+ ```
68
+
69
+ ---
70
+
71
+ ## Usage
72
+
73
+ ### Auto-detect and scan everything
74
+
75
+ ```bash
76
+ tessen scan
77
+ ```
78
+
79
+ Walks the project tree upward from the current directory looking for `.mcp.json` (Claude Code) and `.cursor/mcp.json` (Cursor), then checks the OS-specific location for Claude Desktop (`~/.config/Claude/claude_desktop_config.json` on Linux, `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, `%APPDATA%\Claude\claude_desktop_config.json` on Windows). Scans every config it finds.
80
+
81
+ ### Scan a specific file
82
+
83
+ ```bash
84
+ tessen scan path/to/claude_desktop_config.json
85
+ tessen scan .cursor/mcp.json --client cursor
86
+ ```
87
+
88
+ The `--client` flag forces a specific parser when the filename doesn't match convention. Valid values: `claude_desktop`, `claude_code`, `cursor`.
89
+
90
+ ### Version
91
+
92
+ ```bash
93
+ tessen version
94
+ ```
95
+
96
+ ---
97
+
98
+ ## CI integration
99
+
100
+ Tessen exits non-zero when it finds issues at or above a severity threshold. This is the mechanism to keep vulnerable configs out of your `main` branch.
101
+
102
+ ```bash
103
+ tessen scan --fail-on high
104
+ ```
105
+
106
+ Exit codes: `0` for clean, `1` when the threshold is met, `2` for invalid flag values.
107
+
108
+ Valid `--fail-on` values: `critical`, `high`, `medium`, `low`, `never`.
109
+
110
+ **GitHub Actions example** (roughly — this pipeline hasn't been dogfooded yet):
111
+
112
+ ```yaml
113
+ name: MCP Config Security
114
+
115
+ on:
116
+ pull_request:
117
+ paths:
118
+ - ".mcp.json"
119
+ - ".cursor/mcp.json"
120
+ - ".claude/settings.json"
121
+
122
+ jobs:
123
+ scan:
124
+ runs-on: ubuntu-latest
125
+ steps:
126
+ - uses: actions/checkout@v4
127
+ - uses: actions/setup-python@v5
128
+ with:
129
+ python-version: "3.11"
130
+ - name: Install tessen
131
+ run: |
132
+ pip install git+https://github.com/SlimBenTanfous1/tessen-cli.git
133
+ - name: Scan MCP configs
134
+ run: tessen scan --fail-on high
135
+ ```
136
+
137
+ ---
138
+
139
+ ## Example output
140
+
141
+ Scanning a config with three planted issues (real GitHub PAT, bash wrapper, filesystem-wide grant):
142
+
143
+ ```
144
+ tessen scanning /path/to/claude_desktop_with_secrets.json
145
+
146
+ Findings for Claude Desktop 1 critical · 2 high
147
+
148
+ ╭─ ● [CRITICAL] GitHub Personal Access Token exposed in plaintext ─────╮
149
+ │ The env var 'GITHUB_PERSONAL_ACCESS_TOKEN' in server │
150
+ │ 'github_real_leak' contains a value matching a GitHub Personal │
151
+ │ Access Token pattern. │
152
+ │ │
153
+ │ Location: mcpServers.github_real_leak.env.GITHUB_PERSONAL_... │
154
+ │ Rule: tessen-secrets-in-env │
155
+ │ Remediation: Move the secret to a secure store (1Password, Vault, │
156
+ │ or OS keychain) and reference it via ${VAR_NAME}. │
157
+ ╰──────────────────────────────────────────────────────────────────────╯
158
+
159
+ Scan summary
160
+ Total findings: 3
161
+ Critical 1
162
+ High 2
163
+ ```
164
+
165
+ ---
166
+
167
+ ## Supported clients
168
+
169
+ | Client | Config path | Parser |
170
+ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
171
+ | Claude Desktop | `~/.config/Claude/claude_desktop_config.json` (Linux)<br>`~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)<br>`%APPDATA%\Claude\claude_desktop_config.json` (Windows) | `claude_desktop` |
172
+ | Claude Code | `<project>/.mcp.json` | `claude_code` |
173
+ | Cursor | `<project>/.cursor/mcp.json` | `cursor` |
174
+ | Windsurf | Planned for `v0.2` | — |
175
+
176
+ ---
177
+
178
+ ## Development
179
+
180
+ ```bash
181
+ git clone https://github.com/SlimBenTanfous1/tessen-cli.git
182
+ cd tessen-cli
183
+ uv sync # install runtime + dev deps
184
+ source .venv/bin/activate # or use `uv run <command>`
185
+
186
+ # Run the test suite
187
+ pytest tests/ -v
188
+
189
+ # Run tessen against test fixtures
190
+ tessen scan tests/fixtures/claude_desktop_with_secrets.json
191
+ tessen scan tests/fixtures/claude_desktop_clean.json
192
+ ```
193
+
194
+ ### Project layout
195
+
196
+ ```
197
+ tessen-cli/
198
+ ├── src/tessen/
199
+ │ ├── cli.py # Typer app: scan + version + --fail-on
200
+ │ ├── models.py # MCPConfig, ParseResult, ClientType
201
+ │ ├── output/human.py # Rich severity panels
202
+ │ ├── parsers/
203
+ │ │ ├── common.py # Shared JSON+Pydantic parse logic
204
+ │ │ ├── claude_desktop.py
205
+ │ │ ├── claude_code.py # + walker for .mcp.json
206
+ │ │ ├── cursor.py # + walker for .cursor/mcp.json
207
+ │ │ └── auto.py # Multi-client auto-detection
208
+ │ └── rules/
209
+ │ ├── base.py # Rule ABC
210
+ │ ├── engine.py # RuleEngine with buggy-rule isolation
211
+ │ ├── models.py # Finding, Severity, RuleMetadata
212
+ │ └── builtin/ # Built-in detection rules
213
+ └── tests/ # 26 tests, ~0.3s runtime
214
+ ```
215
+
216
+ ### Adding a rule
217
+
218
+ 1. Create `src/tessen/rules/builtin/your_rule.py`
219
+ 2. Subclass `Rule`, define `METADATA`, implement `check(result) -> list[Finding]`
220
+ 3. Register it in `src/tessen/rules/builtin/__init__.py` (`BUILTIN_RULES` list)
221
+ 4. Add tests in `tests/test_rules.py`
222
+
223
+ Rules should be pure functions: no I/O, no state, no side effects. Same input → same output.
224
+
225
+ ---
226
+
227
+ ## Roadmap
228
+
229
+ **`v0.1` (current) — foundation:** parsers for 3 clients, auto-detection, 3 detection rules, Rich terminal output, `--fail-on` for CI.
230
+
231
+ **`v0.2` — coverage:**
232
+
233
+ - 4 more rules: known-vulnerable MCP servers (CVE feed), HTTP transport without auth, dangerous auto-approve flags, hook injection in `.claude/settings.json`
234
+ - JSON output format
235
+ - SARIF output for GitHub Advanced Security compatibility
236
+ - Windsurf parser
237
+ - PyPI distribution (`pipx install tessen`)
238
+
239
+ **`v0.3` — trust:**
240
+
241
+ - GitHub Action for one-line CI integration
242
+ - CVE feed as a subscribable JSON endpoint
243
+ - First round of dogfooded findings on `awesome-mcp-servers`
244
+
245
+ **Later:**
246
+
247
+ - Custom rules via YAML
248
+ - Baseline files (`.tessen-baseline`) to suppress known findings
249
+ - Runtime hooks that observe tool calls at execution time
250
+
251
+ Anything you want prioritized? Open an issue.
252
+
253
+ ---
254
+
255
+ ## Why "Tessen"
256
+
257
+ A _tessen_ (鉄扇) is an iron folding fan carried by samurai as a concealed weapon into places where swords were forbidden — tea houses, temples, palaces. Ordinary-looking. Genuinely dangerous.
258
+
259
+ Same shape as a poisoned MCP server: a small config file that looks like plumbing, and behaves like an attack.
260
+
261
+ ---
262
+
263
+ ## License
264
+
265
+ MIT. See [LICENSE](./LICENSE).
266
+
267
+ ---
268
+
269
+ ## About
270
+
271
+ Built by [Slim Ben Tanfous](https://github.com/SlimBenTanfous1) — cybersecurity engineer at BrightByDesign (Zero Trust infrastructure, CI/CD security), joining the ESGI Paris Master's in Cybersecurity in September 2026. Prior: CCNA, CyberOps Associate, AWS Cloud Security Foundations, OCI Foundations.
272
+
273
+ Reach out: [github.com/SlimBenTanfous1](https://github.com/SlimBenTanfous1)
@@ -0,0 +1,52 @@
1
+ [project]
2
+ name = "tessen-cli"
3
+ version = "0.1.0"
4
+ description = "Security scanner for AI agent configurations (MCP servers, Claude Desktop, Cursor, Claude Code, Windsurf)."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ authors = [
8
+ { name = "Slim Ben Tanfous", email = "slimbentanfous2002@gmail.com" }
9
+ ]
10
+ requires-python = ">=3.11"
11
+ keywords = ["security", "scanner", "mcp", "ai-agents", "claude", "cursor", "devsecops"]
12
+ classifiers = [
13
+ "Development Status :: 3 - Alpha",
14
+ "Environment :: Console",
15
+ "Intended Audience :: Developers",
16
+ "Intended Audience :: Information Technology",
17
+ "Topic :: Security",
18
+ "Topic :: Software Development :: Quality Assurance",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ ]
24
+ dependencies = [
25
+ "pydantic>=2.13.4",
26
+ "pyyaml>=6.0.3",
27
+ "rich>=15.0.0",
28
+ "typer>=0.26.7",
29
+ ]
30
+
31
+ [project.scripts]
32
+ tessen = "tessen.cli:app"
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/SlimBenTanfous1/tessen-cli"
36
+ Repository = "https://github.com/SlimBenTanfous1/tessen-cli"
37
+ Issues = "https://github.com/SlimBenTanfous1/tessen-cli/issues"
38
+
39
+ [build-system]
40
+ requires = ["uv_build>=0.11.24,<0.12.0"]
41
+ build-backend = "uv_build"
42
+
43
+ [tool.uv.build-backend]
44
+ module-name = "tessen"
45
+
46
+ [dependency-groups]
47
+ dev = [
48
+ "mypy>=2.1.0",
49
+ "pytest>=9.1.1",
50
+ "pytest-cov>=7.1.0",
51
+ "ruff>=0.15.19",
52
+ ]
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"