agent-audit 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.
- agent_audit-0.1.0/PKG-INFO +219 -0
- agent_audit-0.1.0/README.md +181 -0
- agent_audit-0.1.0/agent_audit/__init__.py +3 -0
- agent_audit-0.1.0/agent_audit/__main__.py +13 -0
- agent_audit-0.1.0/agent_audit/cli/__init__.py +1 -0
- agent_audit-0.1.0/agent_audit/cli/commands/__init__.py +1 -0
- agent_audit-0.1.0/agent_audit/cli/commands/init.py +44 -0
- agent_audit-0.1.0/agent_audit/cli/commands/inspect.py +236 -0
- agent_audit-0.1.0/agent_audit/cli/commands/scan.py +329 -0
- agent_audit-0.1.0/agent_audit/cli/formatters/__init__.py +1 -0
- agent_audit-0.1.0/agent_audit/cli/formatters/json.py +138 -0
- agent_audit-0.1.0/agent_audit/cli/formatters/sarif.py +155 -0
- agent_audit-0.1.0/agent_audit/cli/formatters/terminal.py +221 -0
- agent_audit-0.1.0/agent_audit/cli/main.py +34 -0
- agent_audit-0.1.0/agent_audit/config/__init__.py +1 -0
- agent_audit-0.1.0/agent_audit/config/ignore.py +477 -0
- agent_audit-0.1.0/agent_audit/core_utils/__init__.py +1 -0
- agent_audit-0.1.0/agent_audit/models/__init__.py +18 -0
- agent_audit-0.1.0/agent_audit/models/finding.py +159 -0
- agent_audit-0.1.0/agent_audit/models/risk.py +77 -0
- agent_audit-0.1.0/agent_audit/models/tool.py +182 -0
- agent_audit-0.1.0/agent_audit/rules/__init__.py +6 -0
- agent_audit-0.1.0/agent_audit/rules/engine.py +503 -0
- agent_audit-0.1.0/agent_audit/rules/loader.py +160 -0
- agent_audit-0.1.0/agent_audit/scanners/__init__.py +5 -0
- agent_audit-0.1.0/agent_audit/scanners/base.py +32 -0
- agent_audit-0.1.0/agent_audit/scanners/config_scanner.py +390 -0
- agent_audit-0.1.0/agent_audit/scanners/mcp_config_scanner.py +321 -0
- agent_audit-0.1.0/agent_audit/scanners/mcp_inspector.py +421 -0
- agent_audit-0.1.0/agent_audit/scanners/python_scanner.py +544 -0
- agent_audit-0.1.0/agent_audit/scanners/secret_scanner.py +521 -0
- agent_audit-0.1.0/agent_audit/utils/__init__.py +21 -0
- agent_audit-0.1.0/agent_audit/utils/compat.py +98 -0
- agent_audit-0.1.0/agent_audit/utils/mcp_client.py +343 -0
- agent_audit-0.1.0/agent_audit/version.py +3 -0
- agent_audit-0.1.0/pyproject.toml +56 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: agent-audit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Security scanner for AI agents and MCP configurations - Based on OWASP Agentic Top 10
|
|
5
|
+
Home-page: https://github.com/HeadyZhang/agent-audit
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: ai,agent,security,mcp,audit,owasp,vulnerability,scanner
|
|
8
|
+
Author: Agent Security Team
|
|
9
|
+
Author-email: security@example.com
|
|
10
|
+
Requires-Python: >=3.9,<4.0
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Information Technology
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Security
|
|
24
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
25
|
+
Classifier: Topic :: Software Development :: Testing
|
|
26
|
+
Requires-Dist: aiofiles (>=23.0,<24.0)
|
|
27
|
+
Requires-Dist: aiohttp (>=3.9,<4.0)
|
|
28
|
+
Requires-Dist: click (>=8.1.0,<9.0.0)
|
|
29
|
+
Requires-Dist: pydantic (>=2.0,<3.0)
|
|
30
|
+
Requires-Dist: pyyaml (>=6.0,<7.0)
|
|
31
|
+
Requires-Dist: rich (>=13.0.0,<14.0.0)
|
|
32
|
+
Project-URL: Bug Tracker, https://github.com/HeadyZhang/agent-audit/issues
|
|
33
|
+
Project-URL: Changelog, https://github.com/HeadyZhang/agent-audit/releases
|
|
34
|
+
Project-URL: Documentation, https://github.com/HeadyZhang/agent-audit#readme
|
|
35
|
+
Project-URL: Repository, https://github.com/HeadyZhang/agent-audit
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# Agent Audit
|
|
39
|
+
|
|
40
|
+
[](https://badge.fury.io/py/agent-audit)
|
|
41
|
+
[](https://pypi.org/project/agent-audit/)
|
|
42
|
+
[](https://opensource.org/licenses/MIT)
|
|
43
|
+
[](https://github.com/HeadyZhang/agent-audit/actions/workflows/ci.yml)
|
|
44
|
+
[](https://codecov.io/gh/HeadyZhang/agent-audit)
|
|
45
|
+
|
|
46
|
+
> 🛡️ Security scanner for AI agents and MCP configurations. Detects vulnerabilities based on the **OWASP Agentic Top 10**.
|
|
47
|
+
|
|
48
|
+
<p align="center">
|
|
49
|
+
<img src="docs/demo.gif" alt="Agent Audit Demo" width="800">
|
|
50
|
+
</p>
|
|
51
|
+
|
|
52
|
+
## ✨ Features
|
|
53
|
+
|
|
54
|
+
- **🔍 Python AST Scanning** - Detects dangerous patterns like `shell=True`, `eval()`, and tainted input flows
|
|
55
|
+
- **⚙️ MCP Configuration Scanning** - Validates MCP server configurations for security issues
|
|
56
|
+
- **🔐 Secret Detection** - Finds hardcoded credentials (AWS keys, API tokens, private keys)
|
|
57
|
+
- **🌐 Runtime MCP Inspection** - Probes MCP servers without executing tools ("Agent Nmap")
|
|
58
|
+
- **📊 Multiple Output Formats** - Terminal, JSON, SARIF (for GitHub Code Scanning), Markdown
|
|
59
|
+
|
|
60
|
+
## 🚀 Quick Start
|
|
61
|
+
|
|
62
|
+
### Installation
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install agent-audit
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Basic Usage
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Scan current directory
|
|
72
|
+
agent-audit scan .
|
|
73
|
+
|
|
74
|
+
# Scan with JSON output
|
|
75
|
+
agent-audit scan ./my-agent --format json
|
|
76
|
+
|
|
77
|
+
# Scan with SARIF output for GitHub Code Scanning
|
|
78
|
+
agent-audit scan . --format sarif --output results.sarif
|
|
79
|
+
|
|
80
|
+
# Fail CI on critical findings only
|
|
81
|
+
agent-audit scan . --fail-on critical
|
|
82
|
+
|
|
83
|
+
# Inspect an MCP server at runtime
|
|
84
|
+
agent-audit inspect stdio -- npx -y @modelcontextprotocol/server-filesystem /tmp
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## 🔗 GitHub Action
|
|
88
|
+
|
|
89
|
+
Add Agent Audit to your CI/CD pipeline with just a few lines:
|
|
90
|
+
|
|
91
|
+
```yaml
|
|
92
|
+
name: Security Scan
|
|
93
|
+
on: [push, pull_request]
|
|
94
|
+
|
|
95
|
+
jobs:
|
|
96
|
+
agent-audit:
|
|
97
|
+
runs-on: ubuntu-latest
|
|
98
|
+
steps:
|
|
99
|
+
- uses: actions/checkout@v4
|
|
100
|
+
|
|
101
|
+
- name: Run Agent Audit
|
|
102
|
+
uses: HeadyZhang/agent-audit@v1
|
|
103
|
+
with:
|
|
104
|
+
path: '.'
|
|
105
|
+
fail-on: 'high'
|
|
106
|
+
upload-sarif: 'true'
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Action Inputs
|
|
110
|
+
|
|
111
|
+
| Input | Description | Default |
|
|
112
|
+
|-------|-------------|---------|
|
|
113
|
+
| `path` | Path to scan | `.` |
|
|
114
|
+
| `format` | Output format: `terminal`, `json`, `sarif`, `markdown` | `sarif` |
|
|
115
|
+
| `severity` | Minimum severity to report: `info`, `low`, `medium`, `high`, `critical` | `low` |
|
|
116
|
+
| `fail-on` | Exit with error if findings at this severity | `high` |
|
|
117
|
+
| `baseline` | Path to baseline file for incremental scanning | - |
|
|
118
|
+
| `upload-sarif` | Upload SARIF to GitHub Security tab | `true` |
|
|
119
|
+
|
|
120
|
+
## 🎯 Detected Issues
|
|
121
|
+
|
|
122
|
+
| Rule ID | Title | Severity |
|
|
123
|
+
|---------|-------|----------|
|
|
124
|
+
| AGENT-001 | Command Injection via Unsanitized Input | 🔴 Critical |
|
|
125
|
+
| AGENT-002 | Excessive Agent Permissions | 🟡 Medium |
|
|
126
|
+
| AGENT-003 | Potential Data Exfiltration Chain | 🟠 High |
|
|
127
|
+
| AGENT-004 | Hardcoded Credentials | 🔴 Critical |
|
|
128
|
+
| AGENT-005 | Unverified MCP Server | 🟠 High |
|
|
129
|
+
|
|
130
|
+
## ⚙️ Configuration
|
|
131
|
+
|
|
132
|
+
Create a `.agent-audit.yaml` file to customize scanning:
|
|
133
|
+
|
|
134
|
+
```yaml
|
|
135
|
+
# Allowed network hosts (reduces AGENT-003 confidence)
|
|
136
|
+
allowed_hosts:
|
|
137
|
+
- "*.internal.company.com"
|
|
138
|
+
- "api.openai.com"
|
|
139
|
+
|
|
140
|
+
# Ignore rules
|
|
141
|
+
ignore:
|
|
142
|
+
- rule_id: AGENT-003
|
|
143
|
+
paths:
|
|
144
|
+
- "auth/**"
|
|
145
|
+
reason: "Auth module legitimately communicates externally"
|
|
146
|
+
|
|
147
|
+
# Scan settings
|
|
148
|
+
scan:
|
|
149
|
+
exclude:
|
|
150
|
+
- "tests/**"
|
|
151
|
+
- "venv/**"
|
|
152
|
+
min_severity: low
|
|
153
|
+
fail_on: high
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## 📈 Baseline Scanning
|
|
157
|
+
|
|
158
|
+
Track new findings incrementally:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
# Save current findings as baseline
|
|
162
|
+
agent-audit scan . --save-baseline baseline.json
|
|
163
|
+
|
|
164
|
+
# Only report new findings
|
|
165
|
+
agent-audit scan . --baseline baseline.json
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## 📖 CLI Reference
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
Usage: agent-audit [OPTIONS] COMMAND [ARGS]...
|
|
172
|
+
|
|
173
|
+
Commands:
|
|
174
|
+
scan Scan agent code and configurations
|
|
175
|
+
inspect Inspect an MCP server at runtime
|
|
176
|
+
init Initialize configuration file
|
|
177
|
+
|
|
178
|
+
Options:
|
|
179
|
+
--version Show version
|
|
180
|
+
-v Enable verbose output
|
|
181
|
+
-q Only show errors
|
|
182
|
+
--help Show this message
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## 🛠️ Development
|
|
186
|
+
|
|
187
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
# Clone the repository
|
|
191
|
+
git clone https://github.com/HeadyZhang/agent-audit
|
|
192
|
+
cd agent-security-suite
|
|
193
|
+
|
|
194
|
+
# Install dependencies
|
|
195
|
+
cd packages/core && poetry install
|
|
196
|
+
cd ../audit && poetry install
|
|
197
|
+
|
|
198
|
+
# Run tests
|
|
199
|
+
poetry run pytest tests/ -v
|
|
200
|
+
|
|
201
|
+
# Run the scanner
|
|
202
|
+
poetry run agent-audit scan .
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## 📄 License
|
|
206
|
+
|
|
207
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
208
|
+
|
|
209
|
+
## 🙏 Acknowledgments
|
|
210
|
+
|
|
211
|
+
- Based on the [OWASP Agentic Security Top 10](https://owasp.org/www-project-agentic-security/)
|
|
212
|
+
- Inspired by the need for better AI agent security tooling
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
<p align="center">
|
|
217
|
+
Made with ❤️ for the AI agent security community
|
|
218
|
+
</p>
|
|
219
|
+
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Agent Audit
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/py/agent-audit)
|
|
4
|
+
[](https://pypi.org/project/agent-audit/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://github.com/HeadyZhang/agent-audit/actions/workflows/ci.yml)
|
|
7
|
+
[](https://codecov.io/gh/HeadyZhang/agent-audit)
|
|
8
|
+
|
|
9
|
+
> 🛡️ Security scanner for AI agents and MCP configurations. Detects vulnerabilities based on the **OWASP Agentic Top 10**.
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<img src="docs/demo.gif" alt="Agent Audit Demo" width="800">
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
## ✨ Features
|
|
16
|
+
|
|
17
|
+
- **🔍 Python AST Scanning** - Detects dangerous patterns like `shell=True`, `eval()`, and tainted input flows
|
|
18
|
+
- **⚙️ MCP Configuration Scanning** - Validates MCP server configurations for security issues
|
|
19
|
+
- **🔐 Secret Detection** - Finds hardcoded credentials (AWS keys, API tokens, private keys)
|
|
20
|
+
- **🌐 Runtime MCP Inspection** - Probes MCP servers without executing tools ("Agent Nmap")
|
|
21
|
+
- **📊 Multiple Output Formats** - Terminal, JSON, SARIF (for GitHub Code Scanning), Markdown
|
|
22
|
+
|
|
23
|
+
## 🚀 Quick Start
|
|
24
|
+
|
|
25
|
+
### Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install agent-audit
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Basic Usage
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Scan current directory
|
|
35
|
+
agent-audit scan .
|
|
36
|
+
|
|
37
|
+
# Scan with JSON output
|
|
38
|
+
agent-audit scan ./my-agent --format json
|
|
39
|
+
|
|
40
|
+
# Scan with SARIF output for GitHub Code Scanning
|
|
41
|
+
agent-audit scan . --format sarif --output results.sarif
|
|
42
|
+
|
|
43
|
+
# Fail CI on critical findings only
|
|
44
|
+
agent-audit scan . --fail-on critical
|
|
45
|
+
|
|
46
|
+
# Inspect an MCP server at runtime
|
|
47
|
+
agent-audit inspect stdio -- npx -y @modelcontextprotocol/server-filesystem /tmp
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 🔗 GitHub Action
|
|
51
|
+
|
|
52
|
+
Add Agent Audit to your CI/CD pipeline with just a few lines:
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
name: Security Scan
|
|
56
|
+
on: [push, pull_request]
|
|
57
|
+
|
|
58
|
+
jobs:
|
|
59
|
+
agent-audit:
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/checkout@v4
|
|
63
|
+
|
|
64
|
+
- name: Run Agent Audit
|
|
65
|
+
uses: HeadyZhang/agent-audit@v1
|
|
66
|
+
with:
|
|
67
|
+
path: '.'
|
|
68
|
+
fail-on: 'high'
|
|
69
|
+
upload-sarif: 'true'
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Action Inputs
|
|
73
|
+
|
|
74
|
+
| Input | Description | Default |
|
|
75
|
+
|-------|-------------|---------|
|
|
76
|
+
| `path` | Path to scan | `.` |
|
|
77
|
+
| `format` | Output format: `terminal`, `json`, `sarif`, `markdown` | `sarif` |
|
|
78
|
+
| `severity` | Minimum severity to report: `info`, `low`, `medium`, `high`, `critical` | `low` |
|
|
79
|
+
| `fail-on` | Exit with error if findings at this severity | `high` |
|
|
80
|
+
| `baseline` | Path to baseline file for incremental scanning | - |
|
|
81
|
+
| `upload-sarif` | Upload SARIF to GitHub Security tab | `true` |
|
|
82
|
+
|
|
83
|
+
## 🎯 Detected Issues
|
|
84
|
+
|
|
85
|
+
| Rule ID | Title | Severity |
|
|
86
|
+
|---------|-------|----------|
|
|
87
|
+
| AGENT-001 | Command Injection via Unsanitized Input | 🔴 Critical |
|
|
88
|
+
| AGENT-002 | Excessive Agent Permissions | 🟡 Medium |
|
|
89
|
+
| AGENT-003 | Potential Data Exfiltration Chain | 🟠 High |
|
|
90
|
+
| AGENT-004 | Hardcoded Credentials | 🔴 Critical |
|
|
91
|
+
| AGENT-005 | Unverified MCP Server | 🟠 High |
|
|
92
|
+
|
|
93
|
+
## ⚙️ Configuration
|
|
94
|
+
|
|
95
|
+
Create a `.agent-audit.yaml` file to customize scanning:
|
|
96
|
+
|
|
97
|
+
```yaml
|
|
98
|
+
# Allowed network hosts (reduces AGENT-003 confidence)
|
|
99
|
+
allowed_hosts:
|
|
100
|
+
- "*.internal.company.com"
|
|
101
|
+
- "api.openai.com"
|
|
102
|
+
|
|
103
|
+
# Ignore rules
|
|
104
|
+
ignore:
|
|
105
|
+
- rule_id: AGENT-003
|
|
106
|
+
paths:
|
|
107
|
+
- "auth/**"
|
|
108
|
+
reason: "Auth module legitimately communicates externally"
|
|
109
|
+
|
|
110
|
+
# Scan settings
|
|
111
|
+
scan:
|
|
112
|
+
exclude:
|
|
113
|
+
- "tests/**"
|
|
114
|
+
- "venv/**"
|
|
115
|
+
min_severity: low
|
|
116
|
+
fail_on: high
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## 📈 Baseline Scanning
|
|
120
|
+
|
|
121
|
+
Track new findings incrementally:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# Save current findings as baseline
|
|
125
|
+
agent-audit scan . --save-baseline baseline.json
|
|
126
|
+
|
|
127
|
+
# Only report new findings
|
|
128
|
+
agent-audit scan . --baseline baseline.json
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## 📖 CLI Reference
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
Usage: agent-audit [OPTIONS] COMMAND [ARGS]...
|
|
135
|
+
|
|
136
|
+
Commands:
|
|
137
|
+
scan Scan agent code and configurations
|
|
138
|
+
inspect Inspect an MCP server at runtime
|
|
139
|
+
init Initialize configuration file
|
|
140
|
+
|
|
141
|
+
Options:
|
|
142
|
+
--version Show version
|
|
143
|
+
-v Enable verbose output
|
|
144
|
+
-q Only show errors
|
|
145
|
+
--help Show this message
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## 🛠️ Development
|
|
149
|
+
|
|
150
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
# Clone the repository
|
|
154
|
+
git clone https://github.com/HeadyZhang/agent-audit
|
|
155
|
+
cd agent-security-suite
|
|
156
|
+
|
|
157
|
+
# Install dependencies
|
|
158
|
+
cd packages/core && poetry install
|
|
159
|
+
cd ../audit && poetry install
|
|
160
|
+
|
|
161
|
+
# Run tests
|
|
162
|
+
poetry run pytest tests/ -v
|
|
163
|
+
|
|
164
|
+
# Run the scanner
|
|
165
|
+
poetry run agent-audit scan .
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## 📄 License
|
|
169
|
+
|
|
170
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
171
|
+
|
|
172
|
+
## 🙏 Acknowledgments
|
|
173
|
+
|
|
174
|
+
- Based on the [OWASP Agentic Security Top 10](https://owasp.org/www-project-agentic-security/)
|
|
175
|
+
- Inspired by the need for better AI agent security tooling
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
<p align="center">
|
|
180
|
+
Made with ❤️ for the AI agent security community
|
|
181
|
+
</p>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Entry point for running agent-audit as a module."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
# Windows event loop policy fix - must be set before any asyncio imports
|
|
6
|
+
if sys.platform == "win32":
|
|
7
|
+
import asyncio
|
|
8
|
+
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
9
|
+
|
|
10
|
+
from agent_audit.cli.main import cli
|
|
11
|
+
|
|
12
|
+
if __name__ == "__main__":
|
|
13
|
+
cli()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI module for agent-audit."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI commands for agent-audit."""
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Init command for creating configuration files."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
|
|
9
|
+
from agent_audit.config.ignore import create_default_config
|
|
10
|
+
|
|
11
|
+
console = Console()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@click.command()
|
|
15
|
+
@click.option('--force', '-f', is_flag=True, help='Overwrite existing config')
|
|
16
|
+
def init(force: bool):
|
|
17
|
+
"""
|
|
18
|
+
Initialize agent-audit configuration.
|
|
19
|
+
|
|
20
|
+
Creates a .agent-audit.yaml file in the current directory with
|
|
21
|
+
default settings and example ignore rules.
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
|
|
25
|
+
agent-audit init
|
|
26
|
+
|
|
27
|
+
agent-audit init --force
|
|
28
|
+
"""
|
|
29
|
+
config_path = Path('.agent-audit.yaml')
|
|
30
|
+
|
|
31
|
+
if config_path.exists() and not force:
|
|
32
|
+
console.print(f"[yellow]Configuration file already exists: {config_path}[/yellow]")
|
|
33
|
+
console.print("Use --force to overwrite")
|
|
34
|
+
sys.exit(1)
|
|
35
|
+
|
|
36
|
+
config_content = create_default_config()
|
|
37
|
+
config_path.write_text(config_content, encoding="utf-8")
|
|
38
|
+
|
|
39
|
+
console.print(f"[green]Created configuration file: {config_path}[/green]")
|
|
40
|
+
console.print()
|
|
41
|
+
console.print("Edit this file to:")
|
|
42
|
+
console.print(" - Add allowed hosts for network destinations")
|
|
43
|
+
console.print(" - Configure ignore rules for false positives")
|
|
44
|
+
console.print(" - Set scan exclusion patterns")
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""Inspect command for probing MCP servers."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import sys
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
|
|
12
|
+
from agent_audit.scanners.mcp_inspector import MCPInspector, MCPInspectionResult
|
|
13
|
+
from agent_audit.utils.mcp_client import TransportType
|
|
14
|
+
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def render_inspection_result(result: MCPInspectionResult, output_format: str = "terminal"):
|
|
19
|
+
"""Render inspection result to the console."""
|
|
20
|
+
if output_format == "json":
|
|
21
|
+
_render_json(result)
|
|
22
|
+
else:
|
|
23
|
+
_render_terminal(result)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _render_terminal(result: MCPInspectionResult):
|
|
27
|
+
"""Render result as Rich terminal output."""
|
|
28
|
+
# Status indicator
|
|
29
|
+
if result.connected:
|
|
30
|
+
status = "[green]✓ Connected[/green]"
|
|
31
|
+
border_style = "blue" if result.risk_score < 5 else "red"
|
|
32
|
+
else:
|
|
33
|
+
status = "[red]✗ Failed[/red]"
|
|
34
|
+
border_style = "red"
|
|
35
|
+
|
|
36
|
+
# Header panel
|
|
37
|
+
header_text = (
|
|
38
|
+
f"[bold]MCP Server Inspection[/bold]\n"
|
|
39
|
+
f"Server: {result.server_name}"
|
|
40
|
+
)
|
|
41
|
+
if result.server_version:
|
|
42
|
+
header_text += f" v{result.server_version}"
|
|
43
|
+
header_text += "\n"
|
|
44
|
+
header_text += f"Status: {status} | Response: {result.response_time_ms:.0f}ms\n"
|
|
45
|
+
header_text += f"Risk Score: {result.risk_score:.1f}/10"
|
|
46
|
+
|
|
47
|
+
console.print(Panel.fit(header_text, border_style=border_style))
|
|
48
|
+
|
|
49
|
+
if not result.connected:
|
|
50
|
+
console.print(f"[red]Error: {result.connection_error}[/red]")
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
# Capabilities
|
|
54
|
+
if result.capabilities_declared:
|
|
55
|
+
caps = ", ".join(result.capabilities_declared) or "none"
|
|
56
|
+
console.print(f"\n[dim]Capabilities:[/dim] {caps}")
|
|
57
|
+
|
|
58
|
+
# Tools table
|
|
59
|
+
console.print(f"\n[bold]Tools ({result.tool_count})[/bold]")
|
|
60
|
+
|
|
61
|
+
if result.tools:
|
|
62
|
+
tool_table = Table(show_header=True, header_style="bold cyan")
|
|
63
|
+
tool_table.add_column("Tool", style="cyan")
|
|
64
|
+
tool_table.add_column("Permissions", style="yellow")
|
|
65
|
+
tool_table.add_column("Risk", justify="center")
|
|
66
|
+
tool_table.add_column("Validation")
|
|
67
|
+
|
|
68
|
+
risk_emoji = {
|
|
69
|
+
1: "🟢", # SAFE
|
|
70
|
+
2: "🟢", # LOW
|
|
71
|
+
3: "🟡", # MEDIUM
|
|
72
|
+
4: "🟠", # HIGH
|
|
73
|
+
5: "🔴", # CRITICAL
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for tool in result.tools:
|
|
77
|
+
perms = ", ".join(p.name for p in tool.permissions) or "none"
|
|
78
|
+
risk_value = tool.risk_level.value if hasattr(tool.risk_level, 'value') else 1
|
|
79
|
+
risk = risk_emoji.get(risk_value, "⚪")
|
|
80
|
+
validation = "✅" if tool.has_input_validation else "❌"
|
|
81
|
+
|
|
82
|
+
# Truncate description if needed
|
|
83
|
+
tool_name = tool.name
|
|
84
|
+
if len(tool_name) > 30:
|
|
85
|
+
tool_name = tool_name[:27] + "..."
|
|
86
|
+
|
|
87
|
+
tool_table.add_row(tool_name, perms, risk, validation)
|
|
88
|
+
|
|
89
|
+
console.print(tool_table)
|
|
90
|
+
else:
|
|
91
|
+
console.print("[dim]No tools exposed[/dim]")
|
|
92
|
+
|
|
93
|
+
# Resources
|
|
94
|
+
if result.resources:
|
|
95
|
+
console.print(f"\n[bold]Resources ({result.resource_count})[/bold]")
|
|
96
|
+
for res in result.resources[:10]: # Limit display
|
|
97
|
+
uri = res.get('uri', 'unknown')
|
|
98
|
+
console.print(f" 📄 {uri}")
|
|
99
|
+
if result.resource_count > 10:
|
|
100
|
+
console.print(f" [dim]... and {result.resource_count - 10} more[/dim]")
|
|
101
|
+
|
|
102
|
+
# Prompts
|
|
103
|
+
if result.prompts:
|
|
104
|
+
console.print(f"\n[bold]Prompts ({result.prompt_count})[/bold]")
|
|
105
|
+
for prompt in result.prompts[:10]:
|
|
106
|
+
name = prompt.get('name', 'unknown')
|
|
107
|
+
console.print(f" 💬 {name}")
|
|
108
|
+
if result.prompt_count > 10:
|
|
109
|
+
console.print(f" [dim]... and {result.prompt_count - 10} more[/dim]")
|
|
110
|
+
|
|
111
|
+
# Security findings
|
|
112
|
+
if result.findings:
|
|
113
|
+
console.print(f"\n[bold red]Security Findings ({len(result.findings)})[/bold red]")
|
|
114
|
+
|
|
115
|
+
severity_colors = {
|
|
116
|
+
"critical": "red",
|
|
117
|
+
"high": "red",
|
|
118
|
+
"medium": "yellow",
|
|
119
|
+
"low": "blue"
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
for finding in result.findings:
|
|
123
|
+
severity = finding.get("severity", "medium")
|
|
124
|
+
color = severity_colors.get(severity, "white")
|
|
125
|
+
desc = finding.get("description", "Unknown issue")
|
|
126
|
+
tool_name = finding.get("tool", "")
|
|
127
|
+
|
|
128
|
+
if tool_name:
|
|
129
|
+
console.print(f" [{color}]⚠ {severity.upper()}[/{color}]: {desc} (tool: {tool_name})")
|
|
130
|
+
else:
|
|
131
|
+
console.print(f" [{color}]⚠ {severity.upper()}[/{color}]: {desc}")
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _render_json(result: MCPInspectionResult):
|
|
135
|
+
"""Render result as JSON."""
|
|
136
|
+
import json
|
|
137
|
+
|
|
138
|
+
output = {
|
|
139
|
+
"server_name": result.server_name,
|
|
140
|
+
"server_version": result.server_version,
|
|
141
|
+
"transport": result.transport.value,
|
|
142
|
+
"connected": result.connected,
|
|
143
|
+
"connection_error": result.connection_error,
|
|
144
|
+
"response_time_ms": result.response_time_ms,
|
|
145
|
+
"risk_score": result.risk_score,
|
|
146
|
+
"capabilities": result.capabilities_declared,
|
|
147
|
+
"tool_count": result.tool_count,
|
|
148
|
+
"tools": [t.to_dict() for t in result.tools],
|
|
149
|
+
"resource_count": result.resource_count,
|
|
150
|
+
"resources": result.resources,
|
|
151
|
+
"prompt_count": result.prompt_count,
|
|
152
|
+
"prompts": result.prompts,
|
|
153
|
+
"findings": result.findings,
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
console.print_json(json.dumps(output, indent=2))
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
async def run_inspect_async(
|
|
160
|
+
target: str,
|
|
161
|
+
transport: Optional[str],
|
|
162
|
+
timeout: int,
|
|
163
|
+
output_format: str
|
|
164
|
+
) -> int:
|
|
165
|
+
"""Run the inspection asynchronously."""
|
|
166
|
+
inspector = MCPInspector(timeout=timeout)
|
|
167
|
+
|
|
168
|
+
# Determine transport type
|
|
169
|
+
transport_type: Optional[TransportType] = None
|
|
170
|
+
if transport:
|
|
171
|
+
transport_type = TransportType(transport)
|
|
172
|
+
|
|
173
|
+
result = await inspector.inspect(target, transport_type)
|
|
174
|
+
render_inspection_result(result, output_format)
|
|
175
|
+
|
|
176
|
+
# Return exit code based on risk
|
|
177
|
+
if not result.connected:
|
|
178
|
+
return 2 # Connection failure
|
|
179
|
+
elif result.risk_score >= 7.0:
|
|
180
|
+
return 1 # High risk
|
|
181
|
+
return 0
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def run_inspect(
|
|
185
|
+
target: str,
|
|
186
|
+
transport: Optional[str],
|
|
187
|
+
timeout: int,
|
|
188
|
+
output_format: str
|
|
189
|
+
) -> int:
|
|
190
|
+
"""Run the inspection."""
|
|
191
|
+
return asyncio.run(run_inspect_async(target, transport, timeout, output_format))
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@click.command()
|
|
195
|
+
@click.argument('transport_type', type=click.Choice(['stdio', 'sse']), required=True)
|
|
196
|
+
@click.argument('target', nargs=-1, required=True)
|
|
197
|
+
@click.option('--timeout', '-t', default=30, help='Connection timeout in seconds')
|
|
198
|
+
@click.option('--format', '-f', 'output_format',
|
|
199
|
+
type=click.Choice(['terminal', 'json']), default='terminal',
|
|
200
|
+
help='Output format')
|
|
201
|
+
def inspect(transport_type: str, target: tuple, timeout: int, output_format: str):
|
|
202
|
+
"""
|
|
203
|
+
Inspect a running MCP server and analyze its tools.
|
|
204
|
+
|
|
205
|
+
TRANSPORT_TYPE is either 'stdio' or 'sse'.
|
|
206
|
+
|
|
207
|
+
For stdio, TARGET is the command to run the server:
|
|
208
|
+
|
|
209
|
+
agent-audit inspect stdio -- python my_mcp_server.py
|
|
210
|
+
|
|
211
|
+
For sse, TARGET is the URL:
|
|
212
|
+
|
|
213
|
+
agent-audit inspect sse https://example.com/sse
|
|
214
|
+
|
|
215
|
+
The inspector connects to the server, retrieves its tool definitions,
|
|
216
|
+
and analyzes them for security risks WITHOUT executing any tools.
|
|
217
|
+
"""
|
|
218
|
+
# Join target parts back together
|
|
219
|
+
target_str = ' '.join(target)
|
|
220
|
+
|
|
221
|
+
# Handle the "--" separator for stdio
|
|
222
|
+
if target_str.startswith('-- '):
|
|
223
|
+
target_str = target_str[3:]
|
|
224
|
+
|
|
225
|
+
if not target_str:
|
|
226
|
+
console.print("[red]Error: No target specified[/red]")
|
|
227
|
+
sys.exit(1)
|
|
228
|
+
|
|
229
|
+
exit_code = run_inspect(
|
|
230
|
+
target=target_str,
|
|
231
|
+
transport=transport_type,
|
|
232
|
+
timeout=timeout,
|
|
233
|
+
output_format=output_format
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
sys.exit(exit_code)
|