coverme-scanner 1.2.0 → 1.3.0

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.
@@ -2,11 +2,11 @@
2
2
 
3
3
  **Ultrathink** - Analyze deeply, consider edge cases, trace data flows completely.
4
4
 
5
- Execute this 21-agent security scan with cross-validation.
5
+ Execute this 22-agent security scan with cross-validation.
6
6
 
7
7
  ---
8
8
 
9
- ## PHASE 0: PROJECT DISCOVERY
9
+ ## PHASE 0: PROJECT DISCOVERY & RUNTIME CHECK
10
10
 
11
11
  Before scanning, understand what you're scanning:
12
12
 
@@ -34,6 +34,21 @@ Create a **Project Overview** to include in the report:
34
34
 
35
35
  This context helps readers understand the security findings in context.
36
36
 
37
+ ### Step 3: Check for Runtime Verification (SSH)
38
+
39
+ ```bash
40
+ cat .coverme/runtime.json 2>/dev/null || echo "NO_RUNTIME_CONFIG"
41
+ ```
42
+
43
+ **IF runtime.json exists with environments:**
44
+ - Runtime verification is ENABLED
45
+ - Store the SSH details for use in AGENT 22 (Runtime Verification)
46
+ - The agent will SSH to compare actual runtime vs code expectations
47
+
48
+ **IF NO runtime.json:**
49
+ - Runtime verification is DISABLED (skip AGENT 22)
50
+ - This is normal - many projects don't need runtime verification
51
+
37
52
  ---
38
53
 
39
54
  ## CRITICAL OUTPUT FORMAT
@@ -1215,6 +1230,149 @@ After all other agents complete, generate an executive summary.
1215
1230
 
1216
1231
  ---
1217
1232
 
1233
+ ### AGENT 22: Runtime Verification Scanner (ID prefix: RUNTIME)
1234
+
1235
+ **CONDITIONAL AGENT** - Only runs if SSH is configured in Phase 0.
1236
+
1237
+ Check if runtime.json was found in Phase 0:
1238
+ - If NO runtime.json → Skip this agent entirely
1239
+ - If runtime.json exists → Proceed with runtime verification
1240
+
1241
+ **PURPOSE**: Find dangerous mismatches between code configuration and actual runtime environment.
1242
+
1243
+ **Example of what this catches:**
1244
+ > The code says `USER appuser` in Dockerfile, but the container actually runs as `root`.
1245
+ > This is why a DuckDB file read vulnerability could access /etc/passwd!
1246
+
1247
+ ---
1248
+
1249
+ **STEP 1: Gather Code Expectations**
1250
+
1251
+ Analyze configuration files to understand EXPECTED runtime state:
1252
+
1253
+ ```bash
1254
+ # Dockerfile
1255
+ grep -E "^USER|^EXPOSE|^ENV" Dockerfile 2>/dev/null
1256
+
1257
+ # Docker Compose
1258
+ grep -E "user:|ports:|environment:" docker-compose*.yml 2>/dev/null
1259
+
1260
+ # Kubernetes
1261
+ grep -E "runAsUser|runAsNonRoot|readOnlyRootFilesystem|securityContext" k8s/*.yaml helm/**/values.yaml 2>/dev/null
1262
+
1263
+ # PM2
1264
+ grep -E "user|uid|cwd" ecosystem.config.js pm2.config.js 2>/dev/null
1265
+
1266
+ # Systemd
1267
+ grep -E "^User=|^Group=" *.service 2>/dev/null
1268
+ ```
1269
+
1270
+ Build expected state:
1271
+ ```json
1272
+ {
1273
+ "expected": {
1274
+ "user": "appuser",
1275
+ "uid": 1000,
1276
+ "runAsRoot": false,
1277
+ "readOnlyFs": true,
1278
+ "ports": [3000],
1279
+ "source": "Dockerfile:15 - USER appuser"
1280
+ }
1281
+ }
1282
+ ```
1283
+
1284
+ ---
1285
+
1286
+ **STEP 2: SSH and Check Actual Runtime**
1287
+
1288
+ Use the SSH configuration from runtime.json:
1289
+
1290
+ ```bash
1291
+ # Build SSH command (from runtime.json)
1292
+ # host: user@server.com, port: 22, keyPath: ~/.ssh/id_rsa
1293
+
1294
+ # Check who is running the process
1295
+ ssh user@server.com "ps -eo user,pid,cmd | grep -E 'node|python|java|pm2' | head -5"
1296
+
1297
+ # Check Docker container user
1298
+ ssh user@server.com "docker ps -q | head -1 | xargs -I {} docker exec {} id"
1299
+
1300
+ # Check Kubernetes pod user
1301
+ ssh user@server.com "kubectl get pods -o name | head -1 | xargs -I {} kubectl exec {} -- id"
1302
+
1303
+ # Check file permissions
1304
+ ssh user@server.com "ls -la /app/ 2>/dev/null | head -10"
1305
+
1306
+ # Check listening ports
1307
+ ssh user@server.com "ss -tlnp | grep -E ':3000|:8080|:6379'"
1308
+ ```
1309
+
1310
+ Build actual state:
1311
+ ```json
1312
+ {
1313
+ "actual": {
1314
+ "user": "root",
1315
+ "uid": 0,
1316
+ "runAsRoot": true,
1317
+ "readOnlyFs": false,
1318
+ "ports": [3000, 6379],
1319
+ "evidence": "uid=0(root) gid=0(root)"
1320
+ }
1321
+ }
1322
+ ```
1323
+
1324
+ ---
1325
+
1326
+ **STEP 3: Compare and Generate Findings**
1327
+
1328
+ | Mismatch | Severity | ID |
1329
+ |----------|----------|-----|
1330
+ | Code says non-root, runs as root | CRITICAL | RUNTIME-001 |
1331
+ | Dockerfile USER ignored | CRITICAL | RUNTIME-002 |
1332
+ | K8s runAsNonRoot:true but runs as root | HIGH | RUNTIME-003 |
1333
+ | ReadOnlyRootFilesystem not enforced | HIGH | RUNTIME-004 |
1334
+ | Unexpected ports exposed | MEDIUM | RUNTIME-005 |
1335
+ | File permissions too open (777) | MEDIUM | RUNTIME-006 |
1336
+ | Environment variables mismatch | LOW | RUNTIME-007 |
1337
+
1338
+ **Output Format:**
1339
+ ```json
1340
+ {
1341
+ "id": "RUNTIME-001",
1342
+ "title": "Container running as root despite USER directive",
1343
+ "severity": "critical",
1344
+ "category": "runtime-mismatch",
1345
+ "fixOwner": "devops",
1346
+ "fixType": "infrastructure",
1347
+
1348
+ "expected": {
1349
+ "value": "appuser (uid 1000)",
1350
+ "source": "Dockerfile:15",
1351
+ "code": "USER appuser"
1352
+ },
1353
+
1354
+ "actual": {
1355
+ "value": "root (uid 0)",
1356
+ "command": "docker exec container id",
1357
+ "evidence": "uid=0(root) gid=0(root)"
1358
+ },
1359
+
1360
+ "description": "The Dockerfile specifies USER appuser, but the container runs as root. This likely means docker-compose or K8s overrides the user.",
1361
+
1362
+ "impact": "Running as root means any code vulnerability can access ALL system files. The DuckDB read_text() issue could read /etc/shadow, SSH keys, or any file on the system.",
1363
+
1364
+ "recommendation": "1. Remove user: root from docker-compose.yml\n2. Add securityContext.runAsNonRoot: true to K8s deployment\n3. Verify no --user root in docker run commands",
1365
+
1366
+ "verification": {
1367
+ "codeFile": "Dockerfile:15",
1368
+ "sshCommand": "docker exec container_name id",
1369
+ "sshOutput": "uid=0(root) gid=0(root)"
1370
+ }
1371
+ }
1372
+ ```
1373
+
1374
+ ---
1375
+
1218
1376
  ## PHASE 2: DUPLICATE & EXISTING SOLUTIONS CHECK
1219
1377
 
1220
1378
  ### AGENT 21: Duplicate & Existing Solutions Scanner (ID prefix: DUP)
@@ -0,0 +1,353 @@
1
+ # CoverMe Runtime Verification Agent
2
+
3
+ Compare your **actual runtime environment** against **code configuration** to find dangerous mismatches.
4
+
5
+ ## Arguments
6
+
7
+ $ARGUMENTS - Environment name (from `coverme verify list`)
8
+
9
+ ---
10
+
11
+ ## CRITICAL: This agent requires SSH access
12
+
13
+ Before running, ensure:
14
+ 1. SSH environment is configured: `coverme verify setup --host user@server --name production`
15
+ 2. You have SSH key or password ready
16
+ 3. The environment name is passed as argument
17
+
18
+ ---
19
+
20
+ ## PHASE 1: Load Environment Configuration
21
+
22
+ ```bash
23
+ cat .coverme/runtime.json 2>/dev/null || echo "NO_RUNTIME_CONFIG"
24
+ ```
25
+
26
+ If no config found, output:
27
+ ```
28
+ ERROR: No runtime environments configured.
29
+
30
+ To set up runtime verification:
31
+ coverme verify setup --host user@server.com --name production
32
+
33
+ Then run:
34
+ /coverme-verify production
35
+ ```
36
+
37
+ Extract the environment details:
38
+ - `host`: SSH host (user@server)
39
+ - `port`: SSH port
40
+ - `keyPath`: Optional SSH key path
41
+
42
+ ---
43
+
44
+ ## PHASE 2: Gather Code Expectations
45
+
46
+ Before SSH, analyze the codebase to understand EXPECTED runtime configuration:
47
+
48
+ ### Check Dockerfile/Docker Compose
49
+ ```bash
50
+ cat Dockerfile 2>/dev/null
51
+ cat docker-compose*.yml 2>/dev/null
52
+ ```
53
+
54
+ Look for:
55
+ - `USER` directive (what user should container run as?)
56
+ - `EXPOSE` ports
57
+ - Environment variables
58
+ - Volume mounts
59
+
60
+ ### Check Kubernetes/Helm
61
+ ```bash
62
+ cat k8s/*.yaml helm/**/values.yaml 2>/dev/null
63
+ ```
64
+
65
+ Look for:
66
+ - `securityContext.runAsUser`
67
+ - `securityContext.runAsNonRoot`
68
+ - `securityContext.readOnlyRootFilesystem`
69
+ - Resource limits
70
+ - Service accounts
71
+
72
+ ### Check PM2/Process Manager
73
+ ```bash
74
+ cat ecosystem.config.js pm2.config.js 2>/dev/null
75
+ ```
76
+
77
+ Look for:
78
+ - `user` field
79
+ - `cwd` working directory
80
+ - Environment variables
81
+
82
+ ### Check Systemd
83
+ ```bash
84
+ cat *.service 2>/dev/null
85
+ ```
86
+
87
+ Look for:
88
+ - `User=` directive
89
+ - `Group=` directive
90
+ - `WorkingDirectory=`
91
+
92
+ ### Build Expected State Object
93
+ ```json
94
+ {
95
+ "expected": {
96
+ "user": "appuser",
97
+ "uid": 1000,
98
+ "workDir": "/app",
99
+ "ports": [3000, 8080],
100
+ "env": ["NODE_ENV=production"],
101
+ "readOnlyFs": true,
102
+ "source": "Dockerfile line 15: USER appuser"
103
+ }
104
+ }
105
+ ```
106
+
107
+ ---
108
+
109
+ ## PHASE 3: SSH and Gather Runtime State
110
+
111
+ **IMPORTANT**: Use the Bash tool with SSH commands. The user has pre-configured SSH access.
112
+
113
+ Build SSH command prefix:
114
+ ```
115
+ SSH_CMD="ssh -o StrictHostKeyChecking=no -p {port} {keyPath ? '-i ' + keyPath : ''} {host}"
116
+ ```
117
+
118
+ ### Check Running Processes
119
+
120
+ ```bash
121
+ # Find Node.js/Python/Java processes
122
+ $SSH_CMD "ps aux | grep -E 'node|python|java|pm2' | grep -v grep"
123
+
124
+ # Get current user
125
+ $SSH_CMD "whoami"
126
+
127
+ # Get user running the app
128
+ $SSH_CMD "ps -eo user,pid,cmd | grep -E 'node|python|java' | head -5"
129
+ ```
130
+
131
+ ### Check Docker Containers (if Docker)
132
+
133
+ ```bash
134
+ # List running containers
135
+ $SSH_CMD "docker ps --format '{{.Names}}\t{{.Image}}\t{{.Status}}'"
136
+
137
+ # Get user inside container
138
+ $SSH_CMD "docker exec {container_name} whoami"
139
+
140
+ # Get container user ID
141
+ $SSH_CMD "docker exec {container_name} id"
142
+
143
+ # Check container security
144
+ $SSH_CMD "docker inspect {container_name} --format '{{.Config.User}} {{.HostConfig.ReadonlyRootfs}} {{.HostConfig.Privileged}}'"
145
+ ```
146
+
147
+ ### Check Kubernetes Pods (if K8s)
148
+
149
+ ```bash
150
+ # Get pods
151
+ $SSH_CMD "kubectl get pods -o wide"
152
+
153
+ # Get security context
154
+ $SSH_CMD "kubectl get pod {pod_name} -o jsonpath='{.spec.securityContext}'"
155
+
156
+ # Get container user
157
+ $SSH_CMD "kubectl exec {pod_name} -- id"
158
+ ```
159
+
160
+ ### Check PM2 Processes
161
+
162
+ ```bash
163
+ # PM2 status
164
+ $SSH_CMD "pm2 list"
165
+
166
+ # PM2 process details
167
+ $SSH_CMD "pm2 show 0 | grep -E 'user|uid|gid|cwd'"
168
+ ```
169
+
170
+ ### Check System Permissions
171
+
172
+ ```bash
173
+ # Check file permissions on app directory
174
+ $SSH_CMD "ls -la /app/ 2>/dev/null || ls -la /home/*/app/ 2>/dev/null"
175
+
176
+ # Check who owns the app files
177
+ $SSH_CMD "stat -c '%U:%G %a %n' /app/* 2>/dev/null | head -10"
178
+
179
+ # Check sensitive files
180
+ $SSH_CMD "ls -la /app/.env* /app/config/*.json 2>/dev/null"
181
+ ```
182
+
183
+ ### Check Network
184
+
185
+ ```bash
186
+ # Check listening ports
187
+ $SSH_CMD "netstat -tlnp 2>/dev/null || ss -tlnp"
188
+
189
+ # Check firewall rules
190
+ $SSH_CMD "iptables -L -n 2>/dev/null || ufw status 2>/dev/null"
191
+ ```
192
+
193
+ ### Build Actual State Object
194
+ ```json
195
+ {
196
+ "actual": {
197
+ "user": "root",
198
+ "uid": 0,
199
+ "workDir": "/app",
200
+ "ports": [3000, 8080, 6379],
201
+ "env": ["NODE_ENV=production"],
202
+ "readOnlyFs": false,
203
+ "privileged": false,
204
+ "containerUser": "root",
205
+ "processUser": "root"
206
+ }
207
+ }
208
+ ```
209
+
210
+ ---
211
+
212
+ ## PHASE 4: Compare and Generate Findings
213
+
214
+ Compare expected vs actual for EACH field:
215
+
216
+ ### Critical Mismatches (RUNTIME prefix)
217
+
218
+ | Finding | Severity | When to Report |
219
+ |---------|----------|----------------|
220
+ | RUNTIME-001 | CRITICAL | Code says `USER appuser`, runtime runs as `root` |
221
+ | RUNTIME-002 | CRITICAL | Dockerfile has `USER`, but container runs as root |
222
+ | RUNTIME-003 | HIGH | `runAsNonRoot: true` in K8s, but pod runs as root |
223
+ | RUNTIME-004 | HIGH | ReadOnlyRootFilesystem expected, but writable |
224
+ | RUNTIME-005 | HIGH | Privileged container not expected |
225
+ | RUNTIME-006 | MEDIUM | Port exposed that shouldn't be (e.g., Redis 6379) |
226
+ | RUNTIME-007 | MEDIUM | Environment variables missing or different |
227
+ | RUNTIME-008 | MEDIUM | File permissions too open (777, world-writable) |
228
+ | RUNTIME-009 | LOW | Working directory mismatch |
229
+ | RUNTIME-010 | INFO | Extra processes running not in config |
230
+
231
+ ### Output Format
232
+
233
+ ```json
234
+ {
235
+ "id": "RUNTIME-001",
236
+ "title": "Container running as root despite USER directive",
237
+ "severity": "critical",
238
+ "category": "runtime-mismatch",
239
+ "fixOwner": "devops",
240
+ "fixType": "infrastructure",
241
+
242
+ "expected": {
243
+ "value": "appuser (uid 1000)",
244
+ "source": "Dockerfile:15 - USER appuser",
245
+ "code": "USER appuser"
246
+ },
247
+
248
+ "actual": {
249
+ "value": "root (uid 0)",
250
+ "source": "docker exec container_name id",
251
+ "evidence": "uid=0(root) gid=0(root) groups=0(root)"
252
+ },
253
+
254
+ "description": "The Dockerfile specifies USER appuser, but the container is actually running as root. This happened because the docker run command included --user root or the orchestration layer overrode the USER directive.",
255
+
256
+ "impact": "Running as root inside the container means any code execution vulnerability (like the DuckDB file read issue) can access ALL files on the system, not just application files. Attackers can read /etc/shadow, modify system configs, and potentially escape the container.",
257
+
258
+ "recommendation": "1. Check docker-compose.yml for user: root override\n2. Check K8s deployment for securityContext.runAsUser: 0\n3. Ensure no --user root in docker run commands\n4. Add securityContext.runAsNonRoot: true to K8s",
259
+
260
+ "commands": {
261
+ "verify": "docker exec container_name id",
262
+ "fix_docker": "docker run --user 1000:1000 ...",
263
+ "fix_k8s": "kubectl patch deployment app -p '{\"spec\":{\"template\":{\"spec\":{\"securityContext\":{\"runAsNonRoot\":true}}}}}'"
264
+ }
265
+ }
266
+ ```
267
+
268
+ ---
269
+
270
+ ## PHASE 5: Generate Verification Report
271
+
272
+ Save to `.coverme/runtime-verify.json`:
273
+
274
+ ```json
275
+ {
276
+ "environment": "production",
277
+ "host": "user@server.com",
278
+ "verifiedAt": "2024-01-15T10:30:00Z",
279
+
280
+ "summary": {
281
+ "total": 5,
282
+ "critical": 1,
283
+ "high": 2,
284
+ "medium": 1,
285
+ "low": 1,
286
+ "matches": 8
287
+ },
288
+
289
+ "mismatches": [
290
+ { "...finding objects..." }
291
+ ],
292
+
293
+ "matches": [
294
+ {
295
+ "check": "NODE_ENV",
296
+ "expected": "production",
297
+ "actual": "production",
298
+ "status": "match"
299
+ }
300
+ ],
301
+
302
+ "rawData": {
303
+ "expected": { "...from code analysis..." },
304
+ "actual": { "...from SSH commands..." }
305
+ }
306
+ }
307
+ ```
308
+
309
+ ---
310
+
311
+ ## PHASE 6: Display Results
312
+
313
+ Print summary:
314
+
315
+ ```
316
+ Runtime Verification: production (user@server.com)
317
+ =================================================
318
+
319
+ CRITICAL MISMATCH: Container running as root!
320
+
321
+ Expected: USER appuser (from Dockerfile:15)
322
+ Actual: root (uid=0)
323
+
324
+ This is why your DuckDB file read vulnerability was exploitable!
325
+ The code assumed it would run as appuser with limited permissions,
326
+ but in production it runs as root with access to everything.
327
+
328
+ Fix: Add to your deployment:
329
+ securityContext:
330
+ runAsNonRoot: true
331
+ runAsUser: 1000
332
+
333
+ --------------------------------------------------
334
+
335
+ Summary:
336
+ Matches: 8 configurations match
337
+ Mismatches: 5 found
338
+ - 1 CRITICAL (user mismatch)
339
+ - 2 HIGH (permissions)
340
+ - 1 MEDIUM (ports)
341
+ - 1 LOW (working directory)
342
+
343
+ Full report: .coverme/runtime-verify.json
344
+ ```
345
+
346
+ ---
347
+
348
+ ## Security Notes
349
+
350
+ - SSH credentials are NOT stored by CoverMe
351
+ - Commands are read-only (no modifications to remote system)
352
+ - All SSH output is included in the report for audit
353
+ - Consider using a dedicated read-only SSH user for verification