grim-mcp 0.2.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.
- grim-mcp-0.2.0/LICENSE +21 -0
- grim-mcp-0.2.0/MANIFEST.in +6 -0
- grim-mcp-0.2.0/PKG-INFO +553 -0
- grim-mcp-0.2.0/README.md +507 -0
- grim-mcp-0.2.0/docs/validation.md +71 -0
- grim-mcp-0.2.0/pyproject.toml +48 -0
- grim-mcp-0.2.0/setup.cfg +4 -0
- grim-mcp-0.2.0/src/grim/__init__.py +3 -0
- grim-mcp-0.2.0/src/grim/__main__.py +211 -0
- grim-mcp-0.2.0/src/grim/core/__init__.py +0 -0
- grim-mcp-0.2.0/src/grim/core/attack.py +78 -0
- grim-mcp-0.2.0/src/grim/core/detector.py +159 -0
- grim-mcp-0.2.0/src/grim/core/findings.py +111 -0
- grim-mcp-0.2.0/src/grim/core/ledger.py +161 -0
- grim-mcp-0.2.0/src/grim/core/planner.py +79 -0
- grim-mcp-0.2.0/src/grim/core/report.py +93 -0
- grim-mcp-0.2.0/src/grim/engines/__init__.py +0 -0
- grim-mcp-0.2.0/src/grim/engines/codepatterns.py +612 -0
- grim-mcp-0.2.0/src/grim/engines/deps.py +358 -0
- grim-mcp-0.2.0/src/grim/engines/diffscan.py +235 -0
- grim-mcp-0.2.0/src/grim/engines/exposure.py +496 -0
- grim-mcp-0.2.0/src/grim/engines/flow.py +262 -0
- grim-mcp-0.2.0/src/grim/engines/secrets.py +393 -0
- grim-mcp-0.2.0/src/grim/feeds/__init__.py +1 -0
- grim-mcp-0.2.0/src/grim/feeds/iocs.py +213 -0
- grim-mcp-0.2.0/src/grim/mcp/__init__.py +0 -0
- grim-mcp-0.2.0/src/grim/mcp/server.py +99 -0
- grim-mcp-0.2.0/src/grim/sbom.py +144 -0
- grim-mcp-0.2.0/src/grim/tools.py +384 -0
- grim-mcp-0.2.0/src/grim_mcp.egg-info/PKG-INFO +553 -0
- grim-mcp-0.2.0/src/grim_mcp.egg-info/SOURCES.txt +33 -0
- grim-mcp-0.2.0/src/grim_mcp.egg-info/dependency_links.txt +1 -0
- grim-mcp-0.2.0/src/grim_mcp.egg-info/entry_points.txt +2 -0
- grim-mcp-0.2.0/src/grim_mcp.egg-info/requires.txt +4 -0
- grim-mcp-0.2.0/src/grim_mcp.egg-info/top_level.txt +1 -0
grim-mcp-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AbduljabbarBXR
|
|
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.
|
grim-mcp-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: grim-mcp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Security audit MCP server: finds code, dependency, exposure, secrets, and active-compromise gaps across any stack
|
|
5
|
+
Author: AbduljabbarBXR
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 AbduljabbarBXR
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/AbduljabbarBXR/grim
|
|
29
|
+
Project-URL: Repository, https://github.com/AbduljabbarBXR/grim
|
|
30
|
+
Project-URL: Issues, https://github.com/AbduljabbarBXR/grim/issues
|
|
31
|
+
Keywords: security,mcp,model-context-protocol,audit,malware,sast,osv,sbom,mitre-attack
|
|
32
|
+
Classifier: Development Status :: 4 - Beta
|
|
33
|
+
Classifier: Intended Audience :: Developers
|
|
34
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
35
|
+
Classifier: Operating System :: OS Independent
|
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
41
|
+
Classifier: Topic :: Security
|
|
42
|
+
Requires-Python: >=3.10
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
Provides-Extra: dev
|
|
45
|
+
License-File: LICENSE
|
|
46
|
+
|
|
47
|
+
# GRIM
|
|
48
|
+
|
|
49
|
+
**Security audit MCP server — finds the gaps, exposures, and active compromise that other scanners miss.**
|
|
50
|
+
|
|
51
|
+
> Works on any stack. For AI-built apps and existing complex codebases. Point it at a folder,
|
|
52
|
+
> a repo, or an authorized live URL, and get a prioritized, evidence-backed report with fixes.
|
|
53
|
+
|
|
54
|
+
**Status: v0.2.0.** v1 validated against a real compromise; v2 adds the planner, SBOM,
|
|
55
|
+
MITRE ATT&CK tagging, an IoC hash feed, a persistent findings ledger, nested-archive
|
|
56
|
+
scanning, a delta cache, and parallel scanning. Zero runtime dependencies (Python stdlib
|
|
57
|
+
only), runs on Linux/macOS/Windows and Termux. 117 tests passing across Python 3.10–3.13.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Quickstart
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# from the repo root (no install needed)
|
|
65
|
+
PYTHONPATH=src python3 -m grim list
|
|
66
|
+
PYTHONPATH=src python3 -m grim scan /path/to/app # one-shot audit (md report)
|
|
67
|
+
PYTHONPATH=src python3 -m grim scan backup.tar.gz --format json --out report.json
|
|
68
|
+
PYTHONPATH=src python3 -m grim tool audit_exposure --path /path/to/backup.tar.gz
|
|
69
|
+
PYTHONPATH=src python3 -m grim diff old.tar.gz new.tar.gz # drift / active compromise
|
|
70
|
+
PYTHONPATH=src python3 -m grim mcp # MCP server on stdio
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Optional real install: `pip install -e .` (then `grim ...` works anywhere).
|
|
74
|
+
|
|
75
|
+
### Develop and test
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
python3 tests/run_all.py # runs every tests/test_*.py, no pytest needed
|
|
79
|
+
ruff check src tests # optional lint (pip install ruff)
|
|
80
|
+
python -m build # sdist + wheel
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
CI runs the full suite on Python 3.10, 3.11, 3.12, and 3.13, plus `ruff` and a
|
|
84
|
+
build/install smoke test (`.github/workflows/ci.yml`).
|
|
85
|
+
|
|
86
|
+
### Install from npm (MCP launcher)
|
|
87
|
+
|
|
88
|
+
`grim-mcp` bundles the Python engine and runs it as a stdio MCP server, so no manual
|
|
89
|
+
path setup is needed (requires Node 18+ and Python 3.10+):
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npx -y grim-mcp # MCP server on stdio
|
|
93
|
+
npx -y grim-mcp version
|
|
94
|
+
npx -y grim-mcp list
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Also listed on the official MCP registry (`io.github.AbduljabbarBXR/grim-mcp`) and Smithery
|
|
98
|
+
(`abdijabarboxer2009/grim-mcp`).
|
|
99
|
+
|
|
100
|
+
### Use as MCP server in opencode
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"$schema": "https://opencode.ai/config.json",
|
|
105
|
+
"mcp": {
|
|
106
|
+
"grim": {
|
|
107
|
+
"type": "local",
|
|
108
|
+
"command": ["npx", "-y", "grim-mcp"],
|
|
109
|
+
"enabled": true
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Or point directly at a local checkout:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"$schema": "https://opencode.ai/config.json",
|
|
120
|
+
"mcp": {
|
|
121
|
+
"grim": {
|
|
122
|
+
"type": "local",
|
|
123
|
+
"command": ["python3", "-m", "grim", "mcp"],
|
|
124
|
+
"enabled": true,
|
|
125
|
+
"environment": { "PYTHONPATH": "/path/to/grim/src" }
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Then the agent can call `audit_exposure`, `scan_code`, `diff_artifacts`, `scan`, and friends
|
|
132
|
+
directly while building or reviewing any app.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Table of Contents
|
|
137
|
+
|
|
138
|
+
1. [Why GRIM exists](#1-why-grim-exists)
|
|
139
|
+
2. [Design principles](#2-design-principles)
|
|
140
|
+
3. [What GRIM is / is not](#3-what-grim-is--is-not)
|
|
141
|
+
4. [Architecture](#4-architecture)
|
|
142
|
+
5. [The four gap classes](#5-the-four-gap-classes)
|
|
143
|
+
6. [MCP tool surface](#6-mcp-tool-surface)
|
|
144
|
+
7. [Finding schema](#7-finding-schema)
|
|
145
|
+
8. [Engines and auto-updating feeds](#8-engines-and-auto-updating-feeds)
|
|
146
|
+
9. [Safety and authorization model](#9-safety-and-authorization-model)
|
|
147
|
+
10. [Roadmap](#10-roadmap)
|
|
148
|
+
11. [Case study: what GRIM would have caught](#11-case-study-what-grim-would-have-caught)
|
|
149
|
+
12. [Repo layout](#12-repo-layout)
|
|
150
|
+
13. [Tech stack](#13-tech-stack)
|
|
151
|
+
14. [Non-goals and honest limitations](#14-non-goals-and-honest-limitations)
|
|
152
|
+
15. [Open questions](#15-open-questions)
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 1. Why GRIM exists
|
|
157
|
+
|
|
158
|
+
AI coding agents can build and modify whole applications in hours. Security tooling did not
|
|
159
|
+
adapt to that workflow:
|
|
160
|
+
|
|
161
|
+
- **Scanners exist. Orchestration does not.** Semgrep, Trivy, gitleaks, nuclei, ClamAV, YARA,
|
|
162
|
+
OSV — all open source, all excellent, all separate. Nothing runs the right set per stack,
|
|
163
|
+
normalizes the output, ranks it, explains it in plain language, and hands the AI agent a fix.
|
|
164
|
+
- **Signature engines are blind to app-logic flaws.** A server malware scanner cleans a webshell
|
|
165
|
+
but cannot see the vulnerable upload handler that keeps writing new ones.
|
|
166
|
+
- **File-policy failures are invisible to antivirus.** Executable files landing in public upload
|
|
167
|
+
directories is a *policy* problem, not a virus signature problem, until it is too late.
|
|
168
|
+
- **Nobody watches drift.** The single highest-signal security check for "is this system
|
|
169
|
+
compromised right now?" is: *what changed since the last known-good state?* Almost nobody
|
|
170
|
+
runs it.
|
|
171
|
+
|
|
172
|
+
One real incident (see [case study](#11-case-study-what-grim-would-have-caught)) took three
|
|
173
|
+
months of undetected access, credential harvesting, and executed native binaries before a
|
|
174
|
+
manual audit found it in under an hour. Every artifact was findable by existing engines.
|
|
175
|
+
No single tool was looking.
|
|
176
|
+
|
|
177
|
+
**GRIM is the looker.**
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 2. Design principles
|
|
182
|
+
|
|
183
|
+
1. **Orchestrator, not reinvention.** Wrap mature engines. The value is selection, coverage,
|
|
184
|
+
normalization, prioritization, and remediation — not another regex engine.
|
|
185
|
+
2. **Evidence over alarms.** Every finding carries: file/URL, timestamp, engine, raw evidence,
|
|
186
|
+
and reproducible command. No "something is wrong somewhere".
|
|
187
|
+
3. **Read-only by default.** File scanning never modifies. Live checks are passive unless an
|
|
188
|
+
explicit authorization scope enables active probes.
|
|
189
|
+
4. **AI-native output.** Every tool returns machine-stable JSON plus an optional human narrative.
|
|
190
|
+
Findings include remediation text an AI agent (or a junior dev) can apply directly.
|
|
191
|
+
5. **Feed-driven freshness.** New malware coverage arrives by syncing upstream feeds
|
|
192
|
+
(OSV, Semgrep registry, nuclei templates, ClamAV, YARA repos) — not by shipping app updates.
|
|
193
|
+
6. **Any stack.** Next.js, Astro, plain HTML, Laravel/PHP, Node/Express, Python/Django, Go,
|
|
194
|
+
static sites, container images. Detection first, then stack-specific engines.
|
|
195
|
+
7. **Low-resource capable.** Runs on a laptop, a small VPS, or an Android/Termux device for
|
|
196
|
+
offline audits of downloaded backups and clones.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## 3. What GRIM is / is not
|
|
201
|
+
|
|
202
|
+
| GRIM is | GRIM is not |
|
|
203
|
+
|---|---|
|
|
204
|
+
| An MCP server exposing security tools to AI agents | An antivirus product |
|
|
205
|
+
| A security *orchestrator* over proven engines | A replacement for Imunify360/ClamAV |
|
|
206
|
+
| Code, dependency, exposure, and drift analysis | A guarantee of 100% coverage |
|
|
207
|
+
| Passive live checks + authorized active checks | An exploitation framework |
|
|
208
|
+
| A findings normalizer with fix guidance | A compliance certification tool |
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 4. Architecture
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
216
|
+
│ AI agent (opencode / Claude / Cursor / any MCP client) │
|
|
217
|
+
└──────────────────────────────┬───────────────────────────────┘
|
|
218
|
+
│ MCP (stdio / http)
|
|
219
|
+
┌──────────────────────────────▼───────────────────────────────┐
|
|
220
|
+
│ GRIM CORE │
|
|
221
|
+
│ detector → planner → runner → normalizer → ranker → report │
|
|
222
|
+
│ policy (scope, safety) feeds (auto-update) │
|
|
223
|
+
└───────┬───────────────────────────────────────────────┬──────┘
|
|
224
|
+
│ │
|
|
225
|
+
┌───────▼──────────┐ ┌──────────────────┐ ┌─────────▼──────┐
|
|
226
|
+
│ Static engines │ │ Exposure/Dir │ │ Live engines │
|
|
227
|
+
│ semgrep, gitleaks│ │ file-policy, │ │ nuclei, header │
|
|
228
|
+
│ trivy, phpstan, │ │ backup-diff, │ │ checks, TLS │
|
|
229
|
+
│ bandit, osv.dev │ │ watch/baseline │ │ fingerprint │
|
|
230
|
+
└──────────────────┘ └──────────────────┘ └────────────────┘
|
|
231
|
+
│ │ │
|
|
232
|
+
┌───────▼───────────────────────▼──────────────────────▼───────┐
|
|
233
|
+
│ Malware engines │
|
|
234
|
+
│ ClamAV (freshclam) · YARA (community repos) │
|
|
235
|
+
└──────────────────────────────────────────────────────────────┘
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**Core modules**
|
|
239
|
+
|
|
240
|
+
- `detector` — identifies stack(s) from manifests and file patterns; selects engine plan.
|
|
241
|
+
- `planner` — decides which tools run given target type (repo/folder/live), depth, and policy.
|
|
242
|
+
- `runner` — executes engines with timeouts, resource caps, parallelism, caching of results.
|
|
243
|
+
- `normalizer` — maps every engine output into the GRIM finding schema (`§7`).
|
|
244
|
+
- `ranker` — severity × confidence × exploitability scoring; dedupes cross-engine findings.
|
|
245
|
+
- `report` — renders Markdown/JSON/SARIF; attaches remediation guidance.
|
|
246
|
+
- `feeds` — updates rule/signature/template feeds (`§8`).
|
|
247
|
+
- `policy` — authorization scope enforcement and safety gates (`§9`).
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## 5. The four gap classes
|
|
252
|
+
|
|
253
|
+
GRIM's coverage model. Every tool belongs to one or more:
|
|
254
|
+
|
|
255
|
+
1. **Code-level gaps** — unvalidated inputs, unsafe uploads, missing authorization,
|
|
256
|
+
injection sinks, dangerous patterns. *(classic SAST)*
|
|
257
|
+
2. **Dependency gaps** — known CVEs in package ecosystems.
|
|
258
|
+
3. **Exposure gaps** — secrets in code, `.env`/`.git`/backups/web-executable files reachable,
|
|
259
|
+
misconfigured headers/cookies, writable public directories.
|
|
260
|
+
4. **Active compromise** — file drift since baseline, known malware signatures, polyglot
|
|
261
|
+
webshells, unexpected executables, live indicators (suspicious paths responding 200).
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## 6. MCP tool surface
|
|
266
|
+
|
|
267
|
+
### v1 (MVP — ship first)
|
|
268
|
+
|
|
269
|
+
| Tool | Purpose | Inputs | Engines |
|
|
270
|
+
|---|---|---|---|
|
|
271
|
+
| `detect_stack` | Identify stack(s) and produce an audit plan | `path` | built-in detectors |
|
|
272
|
+
| `audit_deps` | Known CVEs in dependencies | `path` | OSV.dev API, ecosystem lockfiles |
|
|
273
|
+
| `scan_secrets` | Leaked keys, tokens, `.env` in tree | `path`, `config?` | gitleaks, trufflehog, heuristics |
|
|
274
|
+
| `scan_code` | Injection, upload, authz, unsafe pattern findings | `path`, `ruleset?` | Semgrep (+registry), PHPStan/Psalm, Bandit, eslint-security |
|
|
275
|
+
| `audit_exposure` | Web-exposed dangerous files in a tree/backup | `path` (dir, tar, zip) | built-in file-policy engine |
|
|
276
|
+
| `report` | Unified prioritized report + fixes | `findings`, `format` | ranker + renderer |
|
|
277
|
+
|
|
278
|
+
### v2 planned (not yet shipped)
|
|
279
|
+
|
|
280
|
+
| Tool | Purpose | Inputs | Engines |
|
|
281
|
+
|---|---|---|---|
|
|
282
|
+
| `inventory_endpoints` | Every route: method, auth middleware, input surface, risk rank | `path` | framework parsers (Laravel, Next/Astro, Express, Django, Go) |
|
|
283
|
+
| `check_live` | Passive (default) / authorized active live checks | `url`, `scope` | nuclei (passive templates), custom HTTP/TLS/header checks |
|
|
284
|
+
| `malware_scan` | Known malware, webshells, polyglots, ELF-in-webdir | `path` | ClamAV, YARA (community rules) |
|
|
285
|
+
| `watch` | Baseline + drift detection between runs | `path`, `baseline` | hash manifests + semantic diff |
|
|
286
|
+
|
|
287
|
+
### v3
|
|
288
|
+
|
|
289
|
+
- `update_feeds` — force-sync all rule/signature feeds and report versions
|
|
290
|
+
- `ci_scan` — non-interactive mode for pipelines with exit codes
|
|
291
|
+
- `fix_plan` — turn findings into patch suggestions / PR-ready diffs
|
|
292
|
+
- Node agent mode — long-running watchdog for live servers without root (PHP/shell cron
|
|
293
|
+
companion that reports into GRIM)
|
|
294
|
+
|
|
295
|
+
### v2 shipped (0.2.0)
|
|
296
|
+
|
|
297
|
+
| Tool / feature | Purpose |
|
|
298
|
+
|---|---|
|
|
299
|
+
| `plan` | Ordered, explainable audit plan derived from the detected stack |
|
|
300
|
+
| `sbom` | CycloneDX 1.5 / SPDX 2.3 bill of materials for resolved dependencies |
|
|
301
|
+
| `scan_iocs` | Match file hashes against a known-bad IoC store (+ EICAR) |
|
|
302
|
+
| `update_feeds` | Sync the IoC store from a remote JSON feed |
|
|
303
|
+
| `ledger` | Persistent findings ledger: new / known / reopened / resolved across audits |
|
|
304
|
+
| MITRE ATT&CK | Every finding auto-tagged with technique IDs (e.g. `T1505.003`) |
|
|
305
|
+
| Nested archives | `deep=true` extracts zip/tar inside zip/tar with traversal and size guards |
|
|
306
|
+
| Delta cache | SHA-256 keyed per-file result cache; unchanged files are not re-scanned |
|
|
307
|
+
| Parallel scanning | Thread-pool SAST across files (`workers`) |
|
|
308
|
+
|
|
309
|
+
### Example call
|
|
310
|
+
|
|
311
|
+
```json
|
|
312
|
+
{
|
|
313
|
+
"tool": "audit_exposure",
|
|
314
|
+
"arguments": {
|
|
315
|
+
"path": "/audits/backup_2026_09_17.tar.gz",
|
|
316
|
+
"checks": ["web-executable", "dotfiles", "exposed-config", "backup-files", "elf-in-public"]
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
```json
|
|
322
|
+
{
|
|
323
|
+
"findings": [
|
|
324
|
+
{
|
|
325
|
+
"id": "GRIM-EXPOS-0007",
|
|
326
|
+
"severity": "critical",
|
|
327
|
+
"category": "CWE-434",
|
|
328
|
+
"owasp": "A04:2021",
|
|
329
|
+
"title": "PHP file present in public upload directory",
|
|
330
|
+
"location": { "file": "app/public/fileholder/img/9f23630a-....php" },
|
|
331
|
+
"evidence": "520 bytes; contains password-gated command form; folder is web-served",
|
|
332
|
+
"remediation": "Remove file; block PHP execution in upload dirs; fix upload validation server-side",
|
|
333
|
+
"confidence": 0.99,
|
|
334
|
+
"engine": "grim-filepolicy@0.1"
|
|
335
|
+
}
|
|
336
|
+
]
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## 7. Finding schema
|
|
343
|
+
|
|
344
|
+
All engines normalize to this object:
|
|
345
|
+
|
|
346
|
+
```json
|
|
347
|
+
{
|
|
348
|
+
"id": "GRIM-<CLASS>-<NNNN>",
|
|
349
|
+
"severity": "critical | high | medium | low | info",
|
|
350
|
+
"confidence": 0.0,
|
|
351
|
+
"category": "CWE-xxx",
|
|
352
|
+
"owasp": "A01:2021 | ...",
|
|
353
|
+
"title": "short human title",
|
|
354
|
+
"description": "what it is and why it matters",
|
|
355
|
+
"location": {
|
|
356
|
+
"file": "relative/path",
|
|
357
|
+
"line": 0,
|
|
358
|
+
"url": "https://... (live findings)",
|
|
359
|
+
"artifact": "backup.tar.gz (when extracted)"
|
|
360
|
+
},
|
|
361
|
+
"evidence": "raw snippet / header / hash / timestamp — minimal and safe",
|
|
362
|
+
"remediation": "actionable fix, code-level when possible",
|
|
363
|
+
"references": ["https://..."],
|
|
364
|
+
"engine": "name@version",
|
|
365
|
+
"first_seen": "ISO-8601",
|
|
366
|
+
"tags": ["upload", "rce", "active-compromise"]
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Design rules:
|
|
371
|
+
- **No false certainty**: `confidence` always present; ranker sorts by severity × confidence.
|
|
372
|
+
- **Dedupe**: same location + category from multiple engines merges into one finding.
|
|
373
|
+
- **Safe evidence**: truncate secrets; never include full key material in reports.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## 8. Engines and auto-updating feeds
|
|
378
|
+
|
|
379
|
+
| Domain | Engine | Feed / update path | Freshness |
|
|
380
|
+
|---|---|---|---|
|
|
381
|
+
| Dependencies (all ecosystems) | OSV.dev API | live API | real-time |
|
|
382
|
+
| SAST (multi-language) | Semgrep OSS | registry rules (`--config auto` + pinned sets) | continuous |
|
|
383
|
+
| Secrets | gitleaks / trufflehog | built-in rules + custom GRIM patterns | per release |
|
|
384
|
+
| PHP | PHPStan, Psalm, Enlightn | composer install | per release |
|
|
385
|
+
| JS/TS | eslint-plugin-security, `npm audit` | npm | continuous |
|
|
386
|
+
| Python | Bandit, `pip-audit` | PyPI advisories | continuous |
|
|
387
|
+
| Filesystem/containers | Trivy | built-in DB (auto-download) | daily |
|
|
388
|
+
| Live checks | nuclei | templates repo | daily |
|
|
389
|
+
| Malware signatures | ClamAV | `freshclam` | hours |
|
|
390
|
+
| Malware heuristics | YARA (Neo23x0/signature-base, Elastic, etc.) | git pull | days |
|
|
391
|
+
| File policy + drift | GRIM built-in | GRIM rules file (remote-syncable) | versioned |
|
|
392
|
+
|
|
393
|
+
**The auto-update answer:** new malware is caught by *feeds*, not by GRIM releases.
|
|
394
|
+
GRIM ships the pipeline; ClamAV/YARA/nuclei/Semgrep/OSV ship the ever-fresh detection data.
|
|
395
|
+
GRIM's own heuristic rules (upload-dir policy, dangerous patterns) are a single versioned
|
|
396
|
+
rules file that can be hosted remotely and pulled by every installation.
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## 9. Safety and authorization model
|
|
401
|
+
|
|
402
|
+
- **Default mode is read-only and local.** File scans never write; no network unless a live
|
|
403
|
+
tool is invoked.
|
|
404
|
+
- **Live checks require a scope file** (`grim.scope.yaml`):
|
|
405
|
+
|
|
406
|
+
```yaml
|
|
407
|
+
authorization:
|
|
408
|
+
declared_by: "owner or authorized party"
|
|
409
|
+
reference: "contract/ticket id"
|
|
410
|
+
targets:
|
|
411
|
+
- host: "example.com"
|
|
412
|
+
mode: passive # passive | active
|
|
413
|
+
max_requests_per_minute: 30
|
|
414
|
+
paths_allowlist: ["/", "/api/health"]
|
|
415
|
+
deny:
|
|
416
|
+
- "*/wp-admin/*"
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
- **No exploitation payloads, ever.** Active mode = safe probes (exposure checks, header/TLS
|
|
420
|
+
analysis), not weaponized attacks.
|
|
421
|
+
- **Rate-limited, allowlisted, auditable.** Every live request logged with timestamp + target.
|
|
422
|
+
- **Backups treated as evidence**: extraction is isolated and never modifies source archives.
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## 10. Roadmap
|
|
427
|
+
|
|
428
|
+
**Phase 0 — spec and fixtures (this document)**
|
|
429
|
+
- Two sanitized real-world corpora: a compromised app export and a clean baseline
|
|
430
|
+
- Golden output files for regression tests
|
|
431
|
+
|
|
432
|
+
**Phase 1 — v1 tools (MVP)**
|
|
433
|
+
- `detect_stack`, `audit_deps`, `scan_secrets`, `scan_code`, `audit_exposure`, `report`
|
|
434
|
+
- CLI mode + MCP server mode
|
|
435
|
+
- Finding schema + Markdown/JSON renderers
|
|
436
|
+
- Termux-friendly (no root dependencies for v1 tools)
|
|
437
|
+
|
|
438
|
+
**Phase 2 — v2 tools**
|
|
439
|
+
- Shipped in 0.2.0: `plan`, `sbom` (CycloneDX/SPDX), `scan_iocs` + `update_feeds`,
|
|
440
|
+
`ledger`, MITRE ATT&CK tagging, nested-archive scanning, delta cache, parallel scanning
|
|
441
|
+
- Multi-language SAST + lockfile coverage: Go, Rust, Java, Kotlin, C#, Ruby, Dart
|
|
442
|
+
- Still planned: `inventory_endpoints`, `check_live` (passive first), `malware_scan`
|
|
443
|
+
- SARIF export for CI
|
|
444
|
+
|
|
445
|
+
**Phase 3 — platform**
|
|
446
|
+
- Shipped: stdlib test runner, CI matrix (3.10–3.13) with lint + build/install smoke,
|
|
447
|
+
PyPI-ready metadata (classifiers, urls, LICENSE, MANIFEST)
|
|
448
|
+
- Still planned: `ci_scan` with fail thresholds, `fix_plan` PR generation, remote rules sync
|
|
449
|
+
- Optional node agent (PHP/shell, no root) for shared hosting drift alerts
|
|
450
|
+
- Dashboard/report hosting (optional paid tier)
|
|
451
|
+
|
|
452
|
+
**Success metric:** for the case-study corpus, GRIM v2 must detect 100% of the artifacts found
|
|
453
|
+
manually, with zero critical false positives on the clean baseline.
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
## 11. Case study: what GRIM would have caught
|
|
458
|
+
|
|
459
|
+
Sanitized summary of a real incident that motivated this project:
|
|
460
|
+
|
|
461
|
+
- A Laravel-based web app stored uploads in a public directory and validated file types using a
|
|
462
|
+
**client-supplied allowlist** (`mimes:'.$request->mimes`) — an arbitrary-file-upload leading
|
|
463
|
+
to remote code execution.
|
|
464
|
+
- An attacker used it to write UUID-named `.php` webshells into the public upload folder.
|
|
465
|
+
- Over ~3 months: credential-harvesting scripts read every `.env` on the account, a downloader
|
|
466
|
+
installed a remote C2 client, and later two 8 MB ELF binaries were downloaded and executed.
|
|
467
|
+
- The host's malware scanner eventually cleaned **one** file and left ~20 other artifacts,
|
|
468
|
+
including the stager and the executables, publicly reachable.
|
|
469
|
+
- A manual audit found everything in under an hour by: diffing two backup file listings,
|
|
470
|
+
inspecting suspicious files, and reading a stray `error_log`.
|
|
471
|
+
|
|
472
|
+
**GRIM coverage mapping:**
|
|
473
|
+
|
|
474
|
+
| Artifact | Tool that catches it |
|
|
475
|
+
|---|---|
|
|
476
|
+
| `mimes:'.$request->mimes` pattern | `scan_code` (Semgrep taint) |
|
|
477
|
+
| `.php` shells in public upload dir | `audit_exposure`, `malware_scan` |
|
|
478
|
+
| `.sh` stager, `ELF` binaries in web dir | `audit_exposure` (web-executable, elf-in-public) |
|
|
479
|
+
| New UUID `.php` files vs old backup | `watch` / backup diff |
|
|
480
|
+
| Credential-harvesting script pattern | `malware_scan` (YARA), `scan_code` |
|
|
481
|
+
| Publicly reachable shells | `check_live` (authorized exposure probes) |
|
|
482
|
+
| Exposed `.env` DB keys risk | `scan_secrets`, `audit_exposure` |
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
## 12. Repo layout
|
|
487
|
+
|
|
488
|
+
```
|
|
489
|
+
grim/
|
|
490
|
+
├── README.md
|
|
491
|
+
├── LICENSE
|
|
492
|
+
├── pyproject.toml
|
|
493
|
+
├── .github/workflows/ci.yml # test matrix + lint + build
|
|
494
|
+
├── docs/
|
|
495
|
+
│ └── validation.md # real-world case study
|
|
496
|
+
├── src/grim/
|
|
497
|
+
│ ├── __main__.py # CLI (scan, tool, diff, plan, sbom, ledger, iocs, mcp)
|
|
498
|
+
│ ├── tools.py # MCP tool registry
|
|
499
|
+
│ ├── sbom.py # CycloneDX 1.5 / SPDX 2.3
|
|
500
|
+
│ ├── core/ # detector, planner, findings, ledger, attack, report
|
|
501
|
+
│ ├── engines/ # exposure, secrets, codepatterns, flow, deps, diffscan
|
|
502
|
+
│ ├── feeds/ # IoC store + remote feed sync
|
|
503
|
+
│ └── mcp/ # dependency-free stdio MCP server
|
|
504
|
+
├── npm/ # grim-mcp npm launcher (bundles the Python engine)
|
|
505
|
+
└── tests/
|
|
506
|
+
├── run_all.py # stdlib test runner (CI entry point)
|
|
507
|
+
├── test_tools.py # v1 regression
|
|
508
|
+
├── test_p0_v2.py # drift, flow
|
|
509
|
+
├── test_p1_v2.py # multi-language SAST, lockfiles, secrets
|
|
510
|
+
├── test_p2_v2.py # ledger, sbom, ioc, planner, cache, nested archives
|
|
511
|
+
└── test_p3_cli.py # CLI, MCP, security edge cases
|
|
512
|
+
```
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
## 13. Tech stack
|
|
518
|
+
|
|
519
|
+
- **Language:** Python 3.11+ (MCP SDK maturity, scanner ecosystem, Termux support)
|
|
520
|
+
- **MCP:** official `mcp` Python SDK; stdio transport first, HTTP later
|
|
521
|
+
- **Packaging:** `uv`/`pipx` installable; single `grim` entry point; `grim mcp` server mode
|
|
522
|
+
- **Engines:** invoked as subprocesses with structured output (`--json` where available);
|
|
523
|
+
adapters isolate version quirks
|
|
524
|
+
- **Rules:** YAML for GRIM heuristics; Semgrep YAML for custom code rules
|
|
525
|
+
- **Storage:** local cache dir for engine results + baselines (JSON/SQLite)
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## 14. Non-goals and honest limitations
|
|
530
|
+
|
|
531
|
+
- **Not 100% coverage.** The promise is: *the four gap classes, with fast drift detection* —
|
|
532
|
+
not "finds every possible exploit".
|
|
533
|
+
- **No proof of exploitation.** Detecting a live exploit in progress without server access is
|
|
534
|
+
probabilistic; GRIM reports indicators with confidence levels.
|
|
535
|
+
- **No noisy scanners by default.** Aggressive/active scanning is opt-in and authorized.
|
|
536
|
+
- **Won't replace host-level security.** Imunify/ClamAV remain; GRIM adds the code- and
|
|
537
|
+
policy-level layers they lack.
|
|
538
|
+
- **Shared-hosting reality:** some engines need a CLI environment (Termux/VPS/laptop); the v1
|
|
539
|
+
toolset is designed to run against downloaded copies so live hosting is never required.
|
|
540
|
+
|
|
541
|
+
---
|
|
542
|
+
|
|
543
|
+
## 15. Open questions
|
|
544
|
+
|
|
545
|
+
- Rule hosting: GitHub raw vs. dedicated CDN for `rules/` sync?
|
|
546
|
+
- Baseline storage: per-project local vs. optional encrypted remote for `watch` across machines?
|
|
547
|
+
- Licensing model: open-core (v1 free, v2+ paid) vs. service-first (audits) while building?
|
|
548
|
+
- First vertical to prove out: freelance client audits, AI-IDE integrations, or hosting partners?
|
|
549
|
+
- Node agent language for shared hosting: PHP-only (most compatible) or shell + PHP fallback?
|
|
550
|
+
|
|
551
|
+
---
|
|
552
|
+
|
|
553
|
+
*GRIM — because the things that get you are the things nobody was looking at.*
|