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.
- package/README.md +44 -4
- package/dist/cli/index.js +110 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +12 -6
- package/dist/cli/init.js.map +1 -1
- package/dist/prompts/orchestration.md +160 -2
- package/dist/prompts/runtime-verify.md +353 -0
- package/package.json +1 -1
- package/src/cli/index.ts +127 -0
- package/src/cli/init.ts +12 -6
- package/src/prompts/orchestration.md +160 -2
- package/src/prompts/runtime-verify.md +353 -0
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
**Ultrathink** - Analyze deeply, consider edge cases, trace data flows completely.
|
|
4
4
|
|
|
5
|
-
Execute this
|
|
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
|