agentsec-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.
- agentsec_cli-0.1.0/LICENSE +21 -0
- agentsec_cli-0.1.0/PKG-INFO +161 -0
- agentsec_cli-0.1.0/README.md +138 -0
- agentsec_cli-0.1.0/agentsec/__init__.py +3 -0
- agentsec_cli-0.1.0/agentsec/baseline.py +60 -0
- agentsec_cli-0.1.0/agentsec/cli.py +84 -0
- agentsec_cli-0.1.0/agentsec/owasp.py +212 -0
- agentsec_cli-0.1.0/agentsec/parsers/__init__.py +5 -0
- agentsec_cli-0.1.0/agentsec/parsers/core.py +8 -0
- agentsec_cli-0.1.0/agentsec/parsers/json_parser.py +69 -0
- agentsec_cli-0.1.0/agentsec/parsers/toml_parser.py +28 -0
- agentsec_cli-0.1.0/agentsec/parsers/yaml_parser.py +28 -0
- agentsec_cli-0.1.0/agentsec/report.py +36 -0
- agentsec_cli-0.1.0/agentsec/rules/__init__.py +5 -0
- agentsec_cli-0.1.0/agentsec/rules/additional.py +227 -0
- agentsec_cli-0.1.0/agentsec/rules/base.py +108 -0
- agentsec_cli-0.1.0/agentsec/sarif.py +110 -0
- agentsec_cli-0.1.0/agentsec/scanner.py +119 -0
- agentsec_cli-0.1.0/agentsec_cli.egg-info/PKG-INFO +161 -0
- agentsec_cli-0.1.0/agentsec_cli.egg-info/SOURCES.txt +27 -0
- agentsec_cli-0.1.0/agentsec_cli.egg-info/dependency_links.txt +1 -0
- agentsec_cli-0.1.0/agentsec_cli.egg-info/entry_points.txt +2 -0
- agentsec_cli-0.1.0/agentsec_cli.egg-info/requires.txt +6 -0
- agentsec_cli-0.1.0/agentsec_cli.egg-info/top_level.txt +1 -0
- agentsec_cli-0.1.0/pyproject.toml +42 -0
- agentsec_cli-0.1.0/setup.cfg +4 -0
- agentsec_cli-0.1.0/tests/test_baseline.py +40 -0
- agentsec_cli-0.1.0/tests/test_owasp.py +79 -0
- agentsec_cli-0.1.0/tests/test_scanner.py +187 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 locface
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agentsec-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Static security scanner for AI coding agents and MCP configurations
|
|
5
|
+
Author-email: locface <locface@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: click>=8.0.0
|
|
18
|
+
Requires-Dist: pyyaml>=6.0
|
|
19
|
+
Requires-Dist: toml>=0.10.2
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# AgentSec
|
|
25
|
+
|
|
26
|
+
**Static security scanner for AI coding agents and MCP configurations.**
|
|
27
|
+
|
|
28
|
+
AgentSec finds dangerous permissions, prompt injection risks, secret exposure, and unsafe tool access in:
|
|
29
|
+
- **Cursor** agent configurations
|
|
30
|
+
- **Claude Desktop** MCP servers
|
|
31
|
+
- **Codex** / **Cline** / **Continue** agent setups
|
|
32
|
+
- **MCP** (Model Context Protocol) server manifests
|
|
33
|
+
- **Markdown** instruction files (CLAUDE.md, AGENTS.md, etc.)
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- **40+ security rules** covering shell execution, filesystem access, network exfiltration, OAuth scopes, prompt injection, container escape risks, browser automation, and credential helper exposure
|
|
38
|
+
- **Supports JSON, YAML, TOML, Markdown, Cursor/Claude/Cline/Codex config files** — detects common AI-agent configs in multiple formats
|
|
39
|
+
- **SARIF output** for integration with GitHub CodeQL and other CI/CD tools
|
|
40
|
+
- **GitHub Action** ready (coming soon)
|
|
41
|
+
- **Zero dependencies on LLMs** — purely static analysis
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install agentsec
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Or from source:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
git clone https://github.com/locface/AgentSec.git
|
|
53
|
+
cd AgentSec
|
|
54
|
+
pip install -e .
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
### Basic scan
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
agentsec scan /path/to/your/project
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Output formats
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
agentsec scan . --format json # JSON output
|
|
69
|
+
agentsec scan . --format markdown # Markdown report
|
|
70
|
+
agentsec scan . --format sarif # SARIF (for CI/CD)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Filter by severity
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
agentsec scan . --severity critical # only critical findings
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Include hidden files
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
agentsec scan . --include-hidden
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Examples
|
|
86
|
+
|
|
87
|
+
Scan a repository with MCP servers:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
agentsec scan ~/projects/mcp-servers --format sarif > results.sarif
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Scan your current directory for agent configs:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
agentsec scan . --include-hidden --format markdown > report.md
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Rules
|
|
100
|
+
|
|
101
|
+
AgentSec includes rules for:
|
|
102
|
+
|
|
103
|
+
| Severity | Examples |
|
|
104
|
+
|----------|----------|
|
|
105
|
+
| **Critical** | MCP shell execution, filesystem write access, secret exposure, Docker socket access, privileged containers, host mounts, dynamic code execution |
|
|
106
|
+
| **High** | Broad path access, prompt injection, remote script install, communication write permissions, browser + local file access, wildcard tool allowlists |
|
|
107
|
+
| **Medium** | Unpinned dependencies, excessive autonomy, telemetry endpoints, missing input validation |
|
|
108
|
+
| **Low** | Missing policy file |
|
|
109
|
+
|
|
110
|
+
## Supported config files
|
|
111
|
+
|
|
112
|
+
AgentSec scans common AI-agent and MCP files, including:
|
|
113
|
+
|
|
114
|
+
- `mcp.json`, `mcp.yaml`, `mcp.yml`, `mcp.toml`, `mcp-config.json`
|
|
115
|
+
- `claude_desktop_config.json`, `CLAUDE.md`, `AGENTS.md`
|
|
116
|
+
- `.cursorrules`, `.cursor/rules/*`, `.clinerules`, `cline_mcp`, `codex.toml`
|
|
117
|
+
- `settings.json`, `package.json`, `requirements.txt`, `Dockerfile`, `docker-compose.yml`
|
|
118
|
+
|
|
119
|
+
See [`agentsec/rules/additional.py`](https://github.com/locface/AgentSec/blob/main/agentsec/rules/additional.py) for the complete list.
|
|
120
|
+
|
|
121
|
+
## CI/CD Integration
|
|
122
|
+
|
|
123
|
+
Add AgentSec to your GitHub workflow:
|
|
124
|
+
|
|
125
|
+
```yaml
|
|
126
|
+
- name: Run AgentSec
|
|
127
|
+
run: |
|
|
128
|
+
pip install agentsec
|
|
129
|
+
agentsec scan . --format sarif > results.sarif
|
|
130
|
+
|
|
131
|
+
- name: Upload SARIF to GitHub CodeQL
|
|
132
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
133
|
+
with:
|
|
134
|
+
sarif_file: results.sarif
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Development
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
# Install dev dependencies
|
|
141
|
+
pip install -e .[dev]
|
|
142
|
+
|
|
143
|
+
# Run tests
|
|
144
|
+
pytest
|
|
145
|
+
|
|
146
|
+
# Build package
|
|
147
|
+
python -m build
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Documentation
|
|
151
|
+
|
|
152
|
+
- **[Landing Page](https://locface.github.io/AgentSec/)** — interactive demo, features, OWASP mapping, CI/CD setup
|
|
153
|
+
- **[Full Documentation](https://locface.github.io/AgentSec/docs/)** — installation, usage, rules reference, FAQ
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
MIT © 2026 locface
|
|
158
|
+
|
|
159
|
+
## Status
|
|
160
|
+
|
|
161
|
+
**Alpha** — under active development. Contributions welcome!
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# AgentSec
|
|
2
|
+
|
|
3
|
+
**Static security scanner for AI coding agents and MCP configurations.**
|
|
4
|
+
|
|
5
|
+
AgentSec finds dangerous permissions, prompt injection risks, secret exposure, and unsafe tool access in:
|
|
6
|
+
- **Cursor** agent configurations
|
|
7
|
+
- **Claude Desktop** MCP servers
|
|
8
|
+
- **Codex** / **Cline** / **Continue** agent setups
|
|
9
|
+
- **MCP** (Model Context Protocol) server manifests
|
|
10
|
+
- **Markdown** instruction files (CLAUDE.md, AGENTS.md, etc.)
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **40+ security rules** covering shell execution, filesystem access, network exfiltration, OAuth scopes, prompt injection, container escape risks, browser automation, and credential helper exposure
|
|
15
|
+
- **Supports JSON, YAML, TOML, Markdown, Cursor/Claude/Cline/Codex config files** — detects common AI-agent configs in multiple formats
|
|
16
|
+
- **SARIF output** for integration with GitHub CodeQL and other CI/CD tools
|
|
17
|
+
- **GitHub Action** ready (coming soon)
|
|
18
|
+
- **Zero dependencies on LLMs** — purely static analysis
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install agentsec
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Or from source:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
git clone https://github.com/locface/AgentSec.git
|
|
30
|
+
cd AgentSec
|
|
31
|
+
pip install -e .
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
### Basic scan
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
agentsec scan /path/to/your/project
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Output formats
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
agentsec scan . --format json # JSON output
|
|
46
|
+
agentsec scan . --format markdown # Markdown report
|
|
47
|
+
agentsec scan . --format sarif # SARIF (for CI/CD)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Filter by severity
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
agentsec scan . --severity critical # only critical findings
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Include hidden files
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
agentsec scan . --include-hidden
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Examples
|
|
63
|
+
|
|
64
|
+
Scan a repository with MCP servers:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
agentsec scan ~/projects/mcp-servers --format sarif > results.sarif
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Scan your current directory for agent configs:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
agentsec scan . --include-hidden --format markdown > report.md
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Rules
|
|
77
|
+
|
|
78
|
+
AgentSec includes rules for:
|
|
79
|
+
|
|
80
|
+
| Severity | Examples |
|
|
81
|
+
|----------|----------|
|
|
82
|
+
| **Critical** | MCP shell execution, filesystem write access, secret exposure, Docker socket access, privileged containers, host mounts, dynamic code execution |
|
|
83
|
+
| **High** | Broad path access, prompt injection, remote script install, communication write permissions, browser + local file access, wildcard tool allowlists |
|
|
84
|
+
| **Medium** | Unpinned dependencies, excessive autonomy, telemetry endpoints, missing input validation |
|
|
85
|
+
| **Low** | Missing policy file |
|
|
86
|
+
|
|
87
|
+
## Supported config files
|
|
88
|
+
|
|
89
|
+
AgentSec scans common AI-agent and MCP files, including:
|
|
90
|
+
|
|
91
|
+
- `mcp.json`, `mcp.yaml`, `mcp.yml`, `mcp.toml`, `mcp-config.json`
|
|
92
|
+
- `claude_desktop_config.json`, `CLAUDE.md`, `AGENTS.md`
|
|
93
|
+
- `.cursorrules`, `.cursor/rules/*`, `.clinerules`, `cline_mcp`, `codex.toml`
|
|
94
|
+
- `settings.json`, `package.json`, `requirements.txt`, `Dockerfile`, `docker-compose.yml`
|
|
95
|
+
|
|
96
|
+
See [`agentsec/rules/additional.py`](https://github.com/locface/AgentSec/blob/main/agentsec/rules/additional.py) for the complete list.
|
|
97
|
+
|
|
98
|
+
## CI/CD Integration
|
|
99
|
+
|
|
100
|
+
Add AgentSec to your GitHub workflow:
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
- name: Run AgentSec
|
|
104
|
+
run: |
|
|
105
|
+
pip install agentsec
|
|
106
|
+
agentsec scan . --format sarif > results.sarif
|
|
107
|
+
|
|
108
|
+
- name: Upload SARIF to GitHub CodeQL
|
|
109
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
110
|
+
with:
|
|
111
|
+
sarif_file: results.sarif
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Development
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# Install dev dependencies
|
|
118
|
+
pip install -e .[dev]
|
|
119
|
+
|
|
120
|
+
# Run tests
|
|
121
|
+
pytest
|
|
122
|
+
|
|
123
|
+
# Build package
|
|
124
|
+
python -m build
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Documentation
|
|
128
|
+
|
|
129
|
+
- **[Landing Page](https://locface.github.io/AgentSec/)** — interactive demo, features, OWASP mapping, CI/CD setup
|
|
130
|
+
- **[Full Documentation](https://locface.github.io/AgentSec/docs/)** — installation, usage, rules reference, FAQ
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
MIT © 2026 locface
|
|
135
|
+
|
|
136
|
+
## Status
|
|
137
|
+
|
|
138
|
+
**Alpha** — under active development. Contributions welcome!
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Baseline (lockfile) management for AgentSec."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import hashlib
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Dict, List, Tuple
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def compute_finding_id(finding: dict) -> str:
|
|
10
|
+
"""Compute a stable unique ID for a finding based on rule, file, and server."""
|
|
11
|
+
key = f"{finding['rule']}|{finding['file']}|{finding.get('server', '')}"
|
|
12
|
+
return hashlib.md5(key.encode()).hexdigest()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def load_baseline(path: str) -> Dict[str, str]:
|
|
16
|
+
"""Load baseline from JSON file. Returns dict of id -> severity."""
|
|
17
|
+
p = Path(path)
|
|
18
|
+
if not p.exists():
|
|
19
|
+
return {}
|
|
20
|
+
with open(p, 'r') as f:
|
|
21
|
+
data = json.load(f)
|
|
22
|
+
return data.get('findings', {})
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def save_baseline(path: str, findings: List[dict]) -> None:
|
|
26
|
+
"""Save current findings as baseline."""
|
|
27
|
+
baseline = {}
|
|
28
|
+
for f in findings:
|
|
29
|
+
fid = compute_finding_id(f)
|
|
30
|
+
baseline[fid] = f['severity']
|
|
31
|
+
with open(path, 'w') as f:
|
|
32
|
+
json.dump({"findings": baseline}, f, indent=2)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def compare_findings(findings: List[dict], baseline: Dict[str, str]) -> Tuple[List[dict], List[dict], List[dict]]:
|
|
36
|
+
"""
|
|
37
|
+
Compare current findings against baseline.
|
|
38
|
+
Returns: (new, changed, removed)
|
|
39
|
+
- new: findings not in baseline
|
|
40
|
+
- changed: findings whose severity changed
|
|
41
|
+
- removed: baseline entries not in current findings (as list of dict with 'id' and 'severity')
|
|
42
|
+
"""
|
|
43
|
+
current_ids = set()
|
|
44
|
+
new = []
|
|
45
|
+
changed = []
|
|
46
|
+
|
|
47
|
+
for f in findings:
|
|
48
|
+
fid = compute_finding_id(f)
|
|
49
|
+
current_ids.add(fid)
|
|
50
|
+
if fid not in baseline:
|
|
51
|
+
new.append(f)
|
|
52
|
+
elif baseline[fid] != f['severity']:
|
|
53
|
+
changed.append(f)
|
|
54
|
+
|
|
55
|
+
removed = []
|
|
56
|
+
for fid, sev in baseline.items():
|
|
57
|
+
if fid not in current_ids:
|
|
58
|
+
removed.append({'id': fid, 'severity': sev})
|
|
59
|
+
|
|
60
|
+
return new, changed, removed
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""CLI entry point for AgentSec."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import click
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from .scanner import Scanner
|
|
7
|
+
from .report import print_summary
|
|
8
|
+
from .baseline import load_baseline, save_baseline, compare_findings, compute_finding_id
|
|
9
|
+
from .owasp import format_owasp
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.group()
|
|
13
|
+
def cli():
|
|
14
|
+
"""AgentSec — security scanner for AI agent configs."""
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@cli.command()
|
|
19
|
+
@click.argument("path", default=".", type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True))
|
|
20
|
+
@click.option("--format", "-f", default="terminal", help="Output format: terminal, json, markdown, sarif")
|
|
21
|
+
@click.option("--severity", default="all", help="Minimum severity: critical, high, medium, low, all")
|
|
22
|
+
@click.option("--fail-on", type=click.Choice(["critical", "high", "medium", "low"], case_sensitive=False), help="Exit with code 1 if any finding is at least this severity")
|
|
23
|
+
@click.option("--include-hidden", is_flag=True, help="Include hidden files and directories")
|
|
24
|
+
@click.option("--baseline", type=click.Path(exists=True, dir_okay=False, resolve_path=True), help="Path to baseline JSON file (lockfile). Compare findings against it.")
|
|
25
|
+
@click.option("--update-baseline", type=click.Path(dir_okay=False, resolve_path=True), help="Save current findings as baseline JSON file and exit.")
|
|
26
|
+
@click.option("--show-owasp", is_flag=True, default=False, help="Show OWASP Top 10 for LLM mapping IDs for each finding")
|
|
27
|
+
def scan(path, format, severity, include_hidden, fail_on=None, baseline=None, update_baseline=None, show_owasp=False):
|
|
28
|
+
"""Scan a directory for security risks in AI agent configurations."""
|
|
29
|
+
if format == "terminal":
|
|
30
|
+
click.echo(f" Scanning {path}...")
|
|
31
|
+
scanner = Scanner(Path(path), include_hidden=include_hidden, min_severity=severity)
|
|
32
|
+
findings = scanner.scan()
|
|
33
|
+
|
|
34
|
+
# If update-baseline is provided, save baseline and exit
|
|
35
|
+
if update_baseline:
|
|
36
|
+
save_baseline(update_baseline, findings)
|
|
37
|
+
if format == "terminal":
|
|
38
|
+
click.echo(f" Baseline saved to {update_baseline}")
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
# Load baseline if provided
|
|
42
|
+
if baseline:
|
|
43
|
+
baseline_findings = load_baseline(baseline)
|
|
44
|
+
new, changed, removed = compare_findings(findings, baseline_findings)
|
|
45
|
+
if format == "terminal":
|
|
46
|
+
click.echo(f"\n Baseline comparison against {baseline}:")
|
|
47
|
+
click.echo(f" New findings: {len(new)}")
|
|
48
|
+
click.echo(f" Changed severity: {len(changed)}")
|
|
49
|
+
click.echo(f" Removed findings: {len(removed)}")
|
|
50
|
+
if new:
|
|
51
|
+
click.echo("\n New findings:")
|
|
52
|
+
for f in new:
|
|
53
|
+
click.echo(f" [{f['severity']}] {f['rule']} ({f['file']})")
|
|
54
|
+
if changed:
|
|
55
|
+
click.echo("\n Changed findings:")
|
|
56
|
+
for f in changed:
|
|
57
|
+
old_sev = baseline_findings.get(compute_finding_id(f), 'unknown')
|
|
58
|
+
click.echo(f" [{old_sev} -> {f['severity']}] {f['rule']} ({f['file']})")
|
|
59
|
+
if removed:
|
|
60
|
+
click.echo("\n Removed findings (baseline only):")
|
|
61
|
+
for r in removed:
|
|
62
|
+
click.echo(f" [{r['severity']}] id: {r['id']}")
|
|
63
|
+
print_summary(findings, format, show_owasp=show_owasp)
|
|
64
|
+
if new or changed:
|
|
65
|
+
sys.exit(1)
|
|
66
|
+
else:
|
|
67
|
+
# Add OWASP tags to terminal output header if show_owasp
|
|
68
|
+
if format == "terminal" and show_owasp:
|
|
69
|
+
click.echo(" OWASP mapping enabled (LLM = OWASP Top 10 for LLM, AG = OWASP Agentic Security)\n")
|
|
70
|
+
print_summary(findings, format, show_owasp=show_owasp)
|
|
71
|
+
|
|
72
|
+
# Existing fail-on logic
|
|
73
|
+
if fail_on:
|
|
74
|
+
severity_levels = {"low": 0, "medium": 1, "high": 2, "critical": 3}
|
|
75
|
+
min_fail = severity_levels.get(fail_on.lower(), -1)
|
|
76
|
+
if min_fail >= 0:
|
|
77
|
+
for f in findings:
|
|
78
|
+
if severity_levels.get(f["severity"].lower(), -1) >= min_fail:
|
|
79
|
+
click.echo(f"Failing due to {f['severity']} finding: {f['rule']}")
|
|
80
|
+
sys.exit(1)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if __name__ == "__main__":
|
|
84
|
+
cli()
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""OWASP Top 10 for LLM Applications — rule mapping for AgentSec.
|
|
2
|
+
|
|
3
|
+
Maps each AgentSec rule to the relevant OWASP categories.
|
|
4
|
+
|
|
5
|
+
OWASP Top 10 for LLM Applications (2025):
|
|
6
|
+
https://genai.owasp.org/
|
|
7
|
+
|
|
8
|
+
LLM01 — Prompt Injection
|
|
9
|
+
LLM02 — Sensitive Information Disclosure
|
|
10
|
+
LLM03 — Supply Chain Vulnerabilities
|
|
11
|
+
LLM04 — Data Leakage via External Services
|
|
12
|
+
LLM05 — Insecure Output Handling
|
|
13
|
+
LLM06 — Excessive Agency / Unrestricted Autonomy
|
|
14
|
+
LLM07 — Insecure Plugin / Extension Design
|
|
15
|
+
LLM08 — Excessive Permissions / Overprivileged Access
|
|
16
|
+
LLM09 — Over-reliance / Insufficient Oversight
|
|
17
|
+
LLM10 — Model Theft / Intellectual Property Loss
|
|
18
|
+
|
|
19
|
+
OWASP Agentic Security Top 10 (2025):
|
|
20
|
+
AG01 — Insecure Agent-to-Agent Communication
|
|
21
|
+
AG02 — Unauthorized Tool Access
|
|
22
|
+
AG03 — Agent Impersonation
|
|
23
|
+
AG04 — Task Delegation Abuse
|
|
24
|
+
AG05 — Memory / Prompt Leakage
|
|
25
|
+
AG06 — Inconsistent Authorization
|
|
26
|
+
AG07 — Output Validation Failure
|
|
27
|
+
AG08 — Agent Workflow Manipulation
|
|
28
|
+
AG09 — Inadequate Audit Trail
|
|
29
|
+
AG10 — Privilege Escalation
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
# Mapping: OWASP ID -> (short name, description)
|
|
33
|
+
OWASP_CATEGORIES = {
|
|
34
|
+
"LLM01": ("Prompt Injection", "Attacker injects malicious instructions via user prompts or indirect inputs"),
|
|
35
|
+
"LLM02": ("Sensitive Information Disclosure", "LLM or agent exposes sensitive data (secrets, PII, internal info)"),
|
|
36
|
+
"LLM03": ("Supply Chain Vulnerabilities", "Compromised dependencies, packages, or third-party components"),
|
|
37
|
+
"LLM04": ("Data Leakage via External Services", "Sensitive data exfiltrated through network calls, APIs, or integrations"),
|
|
38
|
+
"LLM05": ("Insecure Output Handling", "Agent output is not validated before being used in downstream operations"),
|
|
39
|
+
"LLM06": ("Excessive Agency", "Agent has more autonomy than necessary — can perform actions without oversight"),
|
|
40
|
+
"LLM07": ("Insecure Plugin/Extension Design", "Plugins or extensions have weak security boundaries"),
|
|
41
|
+
"LLM08": ("Excessive Permissions", "Agent or tool has overly broad filesystem/network/OS permissions"),
|
|
42
|
+
"LLM09": ("Over-reliance / Insufficient Oversight", "No guardrails, audit, or policy enforcement for agent actions"),
|
|
43
|
+
"LLM10": ("Model Theft / IP Loss", "Risk of proprietary model weights or IP extraction"),
|
|
44
|
+
# Agentic Security
|
|
45
|
+
"AG01": ("Insecure Agent-to-Agent Communication", "Agents communicate without proper authentication or encryption"),
|
|
46
|
+
"AG02": ("Unauthorized Tool Access", "Agent can invoke tools or capabilities without proper authorization gates"),
|
|
47
|
+
"AG03": ("Agent Impersonation", "Attacker impersonates a legitimate agent to gain access"),
|
|
48
|
+
"AG04": ("Task Delegation Abuse", "Malicious tasks can be delegated to sub-agents without validation"),
|
|
49
|
+
"AG05": ("Memory/Prompt Leakage", "Agent memory or prompt context can leak across sessions or users"),
|
|
50
|
+
"AG06": ("Inconsistent Authorization", "Authorization policies are missing, incomplete, or not enforced"),
|
|
51
|
+
"AG07": ("Output Validation Failure", "Agent output is not validated for safety or correctness"),
|
|
52
|
+
"AG08": ("Agent Workflow Manipulation", "Attacker manipulates the agent's workflow or decision chain"),
|
|
53
|
+
"AG09": ("Inadequate Audit Trail", "Agent actions are not logged or traceable"),
|
|
54
|
+
"AG10": ("Privilege Escalation", "Agent can escalate its own privileges beyond intended scope"),
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Rule-to-OWASP mapping: rule_name -> list of (owasp_id, category_name)
|
|
58
|
+
# Covers all 41 rules from base.py + additional.py (deduplicated)
|
|
59
|
+
RULE_OWASP_MAP = {
|
|
60
|
+
# === Base rules ===
|
|
61
|
+
"MCP shell execution": [
|
|
62
|
+
("LLM06", "Excessive Agency"),
|
|
63
|
+
("AG02", "Unauthorized Tool Access"),
|
|
64
|
+
],
|
|
65
|
+
"MCP filesystem write access": [
|
|
66
|
+
("LLM08", "Excessive Permissions"),
|
|
67
|
+
],
|
|
68
|
+
"Secret exposure": [
|
|
69
|
+
("LLM02", "Sensitive Information Disclosure"),
|
|
70
|
+
],
|
|
71
|
+
"Broad path access": [
|
|
72
|
+
("LLM08", "Excessive Permissions"),
|
|
73
|
+
],
|
|
74
|
+
"Prompt injection risk": [
|
|
75
|
+
("LLM01", "Prompt Injection"),
|
|
76
|
+
],
|
|
77
|
+
"Sensitive file reference": [
|
|
78
|
+
("LLM02", "Sensitive Information Disclosure"),
|
|
79
|
+
],
|
|
80
|
+
"Excessive autonomy": [
|
|
81
|
+
("LLM06", "Excessive Agency"),
|
|
82
|
+
],
|
|
83
|
+
"Unpinned dependency": [
|
|
84
|
+
("LLM03", "Supply Chain Vulnerabilities"),
|
|
85
|
+
],
|
|
86
|
+
"Remote script install": [
|
|
87
|
+
("LLM03", "Supply Chain Vulnerabilities"),
|
|
88
|
+
],
|
|
89
|
+
"Docker socket access": [
|
|
90
|
+
("LLM08", "Excessive Permissions"),
|
|
91
|
+
("AG02", "Unauthorized Tool Access"),
|
|
92
|
+
],
|
|
93
|
+
# === Additional rules ===
|
|
94
|
+
"Network + filesystem access": [
|
|
95
|
+
("LLM04", "Data Leakage via External Services"),
|
|
96
|
+
("AG05", "Memory/Prompt Leakage"),
|
|
97
|
+
],
|
|
98
|
+
"Suspicious tool description": [
|
|
99
|
+
("LLM01", "Prompt Injection"),
|
|
100
|
+
("AG10", "Privilege Escalation"),
|
|
101
|
+
],
|
|
102
|
+
"GitHub token exposure": [
|
|
103
|
+
("LLM02", "Sensitive Information Disclosure"),
|
|
104
|
+
("AG02", "Unauthorized Tool Access"),
|
|
105
|
+
],
|
|
106
|
+
"Communication tool write permission": [
|
|
107
|
+
("LLM04", "Data Leakage via External Services"),
|
|
108
|
+
],
|
|
109
|
+
"Database write/delete permission": [
|
|
110
|
+
("LLM08", "Excessive Permissions"),
|
|
111
|
+
],
|
|
112
|
+
"Excessive autonomy instruction": [
|
|
113
|
+
("LLM06", "Excessive Agency"),
|
|
114
|
+
],
|
|
115
|
+
"Prompt injection in markdown": [
|
|
116
|
+
("LLM01", "Prompt Injection"),
|
|
117
|
+
],
|
|
118
|
+
"MCP OAuth broad scopes": [
|
|
119
|
+
("LLM07", "Insecure Plugin/Extension Design"),
|
|
120
|
+
("AG06", "Inconsistent Authorization"),
|
|
121
|
+
],
|
|
122
|
+
"Web + filesystem access": [
|
|
123
|
+
("LLM04", "Data Leakage via External Services"),
|
|
124
|
+
("LLM08", "Excessive Permissions"),
|
|
125
|
+
],
|
|
126
|
+
"Read repo + network": [
|
|
127
|
+
("LLM04", "Data Leakage via External Services"),
|
|
128
|
+
("AG05", "Memory/Prompt Leakage"),
|
|
129
|
+
],
|
|
130
|
+
"Unknown/untrusted source": [
|
|
131
|
+
("LLM03", "Supply Chain Vulnerabilities"),
|
|
132
|
+
],
|
|
133
|
+
"No policy file": [
|
|
134
|
+
("LLM09", "Over-reliance / Insufficient Oversight"),
|
|
135
|
+
("AG06", "Inconsistent Authorization"),
|
|
136
|
+
],
|
|
137
|
+
"Cursor agent config with dangerous permissions": [
|
|
138
|
+
("LLM08", "Excessive Permissions"),
|
|
139
|
+
("AG02", "Unauthorized Tool Access"),
|
|
140
|
+
],
|
|
141
|
+
"Claude Desktop config with MCP server risks": [
|
|
142
|
+
("LLM07", "Insecure Plugin/Extension Design"),
|
|
143
|
+
],
|
|
144
|
+
"Codex/Cline agent with unrestricted tools": [
|
|
145
|
+
("LLM08", "Excessive Permissions"),
|
|
146
|
+
("AG02", "Unauthorized Tool Access"),
|
|
147
|
+
],
|
|
148
|
+
"Environment variable exposure": [
|
|
149
|
+
("LLM02", "Sensitive Information Disclosure"),
|
|
150
|
+
],
|
|
151
|
+
"Vulnerable dependency pattern": [
|
|
152
|
+
("LLM03", "Supply Chain Vulnerabilities"),
|
|
153
|
+
],
|
|
154
|
+
"Insecure default command": [
|
|
155
|
+
("LLM07", "Insecure Plugin/Extension Design"),
|
|
156
|
+
],
|
|
157
|
+
"Read-only file system in MCP server": [
|
|
158
|
+
("LLM08", "Excessive Permissions"),
|
|
159
|
+
],
|
|
160
|
+
"Missing input validation": [
|
|
161
|
+
("LLM05", "Insecure Output Handling"),
|
|
162
|
+
("AG07", "Output Validation Failure"),
|
|
163
|
+
],
|
|
164
|
+
"Package manager execution": [
|
|
165
|
+
("LLM03", "Supply Chain Vulnerabilities"),
|
|
166
|
+
],
|
|
167
|
+
"Container privileged mode": [
|
|
168
|
+
("LLM08", "Excessive Permissions"),
|
|
169
|
+
("AG02", "Unauthorized Tool Access"),
|
|
170
|
+
],
|
|
171
|
+
"Host mount exposure": [
|
|
172
|
+
("LLM08", "Excessive Permissions"),
|
|
173
|
+
],
|
|
174
|
+
"Browser automation with local file access": [
|
|
175
|
+
("LLM04", "Data Leakage via External Services"),
|
|
176
|
+
("AG05", "Memory/Prompt Leakage"),
|
|
177
|
+
],
|
|
178
|
+
"Dynamic code execution": [
|
|
179
|
+
("LLM06", "Excessive Agency"),
|
|
180
|
+
("AG10", "Privilege Escalation"),
|
|
181
|
+
],
|
|
182
|
+
"Wildcard tool allowlist": [
|
|
183
|
+
("LLM08", "Excessive Permissions"),
|
|
184
|
+
],
|
|
185
|
+
"Telemetry or analytics endpoint": [
|
|
186
|
+
("LLM04", "Data Leakage via External Services"),
|
|
187
|
+
],
|
|
188
|
+
"Credential helper access": [
|
|
189
|
+
("LLM02", "Sensitive Information Disclosure"),
|
|
190
|
+
("AG02", "Unauthorized Tool Access"),
|
|
191
|
+
],
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def get_owasp(rule_name: str) -> list:
|
|
196
|
+
"""Return OWASP mappings for a rule name. Returns [(owasp_id, category_name), ...] or empty list."""
|
|
197
|
+
return RULE_OWASP_MAP.get(rule_name, [])
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def get_owasp_ids(rule_name: str) -> str:
|
|
201
|
+
"""Return OWASP IDs as a comma-separated string (e.g. 'LLM06, AG02')."""
|
|
202
|
+
mappings = get_owasp(rule_name)
|
|
203
|
+
return ", ".join(owasp_id for owasp_id, _ in mappings)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def format_owasp(rule_name: str) -> str:
|
|
207
|
+
"""Format OWASP info for terminal output: '[LLM06, AG02]' or empty string."""
|
|
208
|
+
mappings = get_owasp(rule_name)
|
|
209
|
+
if not mappings:
|
|
210
|
+
return ""
|
|
211
|
+
ids = ", ".join(f"{owasp_id}" for owasp_id, _ in mappings)
|
|
212
|
+
return f"[{ids}]"
|