mohuclaw 1.0.1
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/LICENSE +21 -0
- package/README.md +64 -0
- package/bin/mohu-tui.js +73 -0
- package/bin/mohu-webui.js +67 -0
- package/dist/tui/tui.js +38733 -0
- package/dist/webui/index.html +1551 -0
- package/dist/webui/server.js +876 -0
- package/ioc/c2-ips.txt +25 -0
- package/ioc/file-hashes.txt +13 -0
- package/ioc/malicious-domains.txt +46 -0
- package/ioc/malicious-hashes.txt +5 -0
- package/ioc/malicious-publishers.txt +34 -0
- package/ioc/malicious-skill-patterns.txt +87 -0
- package/package.json +46 -0
- package/scripts/check/access_control.sh +183 -0
- package/scripts/check/credential_storage.sh +222 -0
- package/scripts/check/execution_sandbox.sh +502 -0
- package/scripts/check/memory_poisoning.sh +334 -0
- package/scripts/check/network_exposure.sh +479 -0
- package/scripts/check/resource_cost.sh +182 -0
- package/scripts/check/supply_chain.sh +553 -0
- package/scripts/repair/access_control/_common.sh +249 -0
- package/scripts/repair/access_control/check_1.sh +28 -0
- package/scripts/repair/access_control/check_2.sh +27 -0
- package/scripts/repair/access_control/check_3.sh +23 -0
- package/scripts/repair/access_control/check_4.sh +23 -0
- package/scripts/repair/access_control/check_5.sh +20 -0
- package/scripts/repair/credential_storage/_common.sh +277 -0
- package/scripts/repair/credential_storage/check_1.sh +47 -0
- package/scripts/repair/credential_storage/check_2.sh +35 -0
- package/scripts/repair/credential_storage/check_3.sh +53 -0
- package/scripts/repair/credential_storage/logs/security-scan.log +15 -0
- package/scripts/repair/execution_sandbox/_common.sh +302 -0
- package/scripts/repair/execution_sandbox/check_1.sh +67 -0
- package/scripts/repair/execution_sandbox/check_10.sh +23 -0
- package/scripts/repair/execution_sandbox/check_11.sh +34 -0
- package/scripts/repair/execution_sandbox/check_12.sh +38 -0
- package/scripts/repair/execution_sandbox/check_13.sh +29 -0
- package/scripts/repair/execution_sandbox/check_2.sh +46 -0
- package/scripts/repair/execution_sandbox/check_3.sh +37 -0
- package/scripts/repair/execution_sandbox/check_4.sh +23 -0
- package/scripts/repair/execution_sandbox/check_5.sh +28 -0
- package/scripts/repair/execution_sandbox/check_6.sh +17 -0
- package/scripts/repair/execution_sandbox/check_7.sh +17 -0
- package/scripts/repair/execution_sandbox/check_8.sh +17 -0
- package/scripts/repair/execution_sandbox/check_9.sh +17 -0
- package/scripts/repair/execution_sandbox/logs/security-scan.log +10 -0
- package/scripts/repair/memory_poisoning/_common.sh +336 -0
- package/scripts/repair/memory_poisoning/check_1.sh +51 -0
- package/scripts/repair/memory_poisoning/check_2.sh +26 -0
- package/scripts/repair/memory_poisoning/check_3.sh +24 -0
- package/scripts/repair/memory_poisoning/check_4.sh +27 -0
- package/scripts/repair/memory_poisoning/check_5.sh +20 -0
- package/scripts/repair/network_exposure/_common.sh +330 -0
- package/scripts/repair/network_exposure/check_1.sh +86 -0
- package/scripts/repair/network_exposure/check_10.sh +16 -0
- package/scripts/repair/network_exposure/check_11.sh +31 -0
- package/scripts/repair/network_exposure/check_12.sh +24 -0
- package/scripts/repair/network_exposure/check_2.sh +26 -0
- package/scripts/repair/network_exposure/check_3.sh +43 -0
- package/scripts/repair/network_exposure/check_4.sh +23 -0
- package/scripts/repair/network_exposure/check_5.sh +16 -0
- package/scripts/repair/network_exposure/check_6.sh +98 -0
- package/scripts/repair/network_exposure/check_7.sh +35 -0
- package/scripts/repair/network_exposure/check_8.sh +19 -0
- package/scripts/repair/network_exposure/check_9.sh +19 -0
- package/scripts/repair/resource_cost/_common.sh +303 -0
- package/scripts/repair/resource_cost/check_1.sh +16 -0
- package/scripts/repair/resource_cost/check_2.sh +16 -0
- package/scripts/repair/resource_cost/check_3.sh +23 -0
- package/scripts/repair/supply_chain/_common.sh +222 -0
- package/scripts/repair/supply_chain/check_1.sh +95 -0
- package/scripts/repair/supply_chain/check_10.sh +60 -0
- package/scripts/repair/supply_chain/check_11.sh +63 -0
- package/scripts/repair/supply_chain/check_12.sh +36 -0
- package/scripts/repair/supply_chain/check_13.sh +44 -0
- package/scripts/repair/supply_chain/check_14.sh +33 -0
- package/scripts/repair/supply_chain/check_15.sh +33 -0
- package/scripts/repair/supply_chain/check_16.sh +34 -0
- package/scripts/repair/supply_chain/check_17.sh +61 -0
- package/scripts/repair/supply_chain/check_18.sh +62 -0
- package/scripts/repair/supply_chain/check_2.sh +93 -0
- package/scripts/repair/supply_chain/check_3.sh +78 -0
- package/scripts/repair/supply_chain/check_4.sh +72 -0
- package/scripts/repair/supply_chain/check_5.sh +73 -0
- package/scripts/repair/supply_chain/check_6.sh +81 -0
- package/scripts/repair/supply_chain/check_7.sh +52 -0
- package/scripts/repair/supply_chain/check_8.sh +71 -0
- package/scripts/repair/supply_chain/check_9.sh +78 -0
- package/scripts/repair/supply_chain/logs/security-scan.log +77 -0
- package/scripts/scan.sh +228 -0
- package/webui/index.html +1551 -0
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
# shellcheck shell=bash
|
|
2
|
+
|
|
3
|
+
# ------------------------------------------------------------
|
|
4
|
+
# Shared helpers
|
|
5
|
+
# ------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
OPENCLAW_PRESENT=false
|
|
8
|
+
if command -v openclaw >/dev/null 2>&1; then
|
|
9
|
+
OPENCLAW_PRESENT=true
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
OC_VERSION="unknown"
|
|
13
|
+
PARSED_OC_VERSION=""
|
|
14
|
+
OC_MAJOR=""
|
|
15
|
+
OC_MINOR=""
|
|
16
|
+
OC_PATCH=""
|
|
17
|
+
|
|
18
|
+
if [ "$OPENCLAW_PRESENT" = true ]; then
|
|
19
|
+
OC_VERSION="$(run_with_timeout 5 openclaw --version 2>/dev/null || echo "unknown")"
|
|
20
|
+
PARSED_OC_VERSION="$(echo "$OC_VERSION" | grep -oE '[0-9]{4}\.[0-9]+\.[0-9]+' | head -1 || true)"
|
|
21
|
+
if [ -n "$PARSED_OC_VERSION" ]; then
|
|
22
|
+
OC_MAJOR="$(echo "$PARSED_OC_VERSION" | cut -d'.' -f1)"
|
|
23
|
+
OC_MINOR="$(echo "$PARSED_OC_VERSION" | cut -d'.' -f2)"
|
|
24
|
+
OC_PATCH="$(echo "$PARSED_OC_VERSION" | cut -d'.' -f3)"
|
|
25
|
+
fi
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
get_oc_config() {
|
|
29
|
+
# Usage: get_oc_config <key> <default>
|
|
30
|
+
local key="$1"
|
|
31
|
+
local default_val="${2:-}"
|
|
32
|
+
if [ "$OPENCLAW_PRESENT" = true ]; then
|
|
33
|
+
run_with_timeout 10 openclaw config get "$key" 2>/dev/null || echo "$default_val"
|
|
34
|
+
else
|
|
35
|
+
echo "$default_val"
|
|
36
|
+
fi
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
version_ge() {
|
|
40
|
+
# Usage: version_ge <major> <minor> <patch>
|
|
41
|
+
local t_major="$1"
|
|
42
|
+
local t_minor="$2"
|
|
43
|
+
local t_patch="$3"
|
|
44
|
+
|
|
45
|
+
if [ -z "$PARSED_OC_VERSION" ]; then
|
|
46
|
+
return 1
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
if [ "$OC_MAJOR" -gt "$t_major" ] 2>/dev/null; then
|
|
50
|
+
return 0
|
|
51
|
+
fi
|
|
52
|
+
if [ "$OC_MAJOR" -lt "$t_major" ] 2>/dev/null; then
|
|
53
|
+
return 1
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
if [ "$OC_MINOR" -gt "$t_minor" ] 2>/dev/null; then
|
|
57
|
+
return 0
|
|
58
|
+
fi
|
|
59
|
+
if [ "$OC_MINOR" -lt "$t_minor" ] 2>/dev/null; then
|
|
60
|
+
return 1
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
if [ "$OC_PATCH" -ge "$t_patch" ] 2>/dev/null; then
|
|
64
|
+
return 0
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
return 1
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
version_lt() {
|
|
71
|
+
# Usage: version_lt <major> <minor> <patch>
|
|
72
|
+
if [ -z "$PARSED_OC_VERSION" ]; then
|
|
73
|
+
return 1
|
|
74
|
+
fi
|
|
75
|
+
if version_ge "$1" "$2" "$3"; then
|
|
76
|
+
return 1
|
|
77
|
+
fi
|
|
78
|
+
return 0
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
get_perm() {
|
|
82
|
+
local target="$1"
|
|
83
|
+
|
|
84
|
+
[ -e "$target" ] || {
|
|
85
|
+
echo "unknown"
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
case "$(uname -s)" in
|
|
90
|
+
Darwin|FreeBSD|OpenBSD|NetBSD)
|
|
91
|
+
stat -f '%Lp' "$target" 2>/dev/null || echo "unknown"
|
|
92
|
+
;;
|
|
93
|
+
Linux)
|
|
94
|
+
stat -c '%a' "$target" 2>/dev/null || echo "unknown"
|
|
95
|
+
;;
|
|
96
|
+
*)
|
|
97
|
+
stat -c '%a' "$target" 2>/dev/null || stat -f '%Lp' "$target" 2>/dev/null || echo "unknown"
|
|
98
|
+
;;
|
|
99
|
+
esac
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# ============================================================
|
|
103
|
+
# CHECK 1 (origin 18): Tool Policy / Elevated Tools Audit
|
|
104
|
+
# ============================================================
|
|
105
|
+
header 1 "Auditing tool policies and elevated access..."
|
|
106
|
+
|
|
107
|
+
TOOL_ISSUES=0
|
|
108
|
+
|
|
109
|
+
if [ "$OPENCLAW_PRESENT" = true ]; then
|
|
110
|
+
ELEVATED="$(get_oc_config "tools.elevated.enabled" "")"
|
|
111
|
+
if [ "$ELEVATED" = "true" ]; then
|
|
112
|
+
ELEVATED_ALLOW="$(get_oc_config "tools.elevated.allowFrom" "")"
|
|
113
|
+
if echo "$ELEVATED_ALLOW" | grep -qE '"\*"|\*' 2>/dev/null; then
|
|
114
|
+
result_critical "Elevated tools enabled with wildcard allowFrom"
|
|
115
|
+
TOOL_ISSUES=$((TOOL_ISSUES + 1))
|
|
116
|
+
else
|
|
117
|
+
log " INFO: Elevated tools enabled with restricted allowFrom"
|
|
118
|
+
fi
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
DENY_LIST="$(get_oc_config "tools.deny" "")"
|
|
122
|
+
if [ -z "$DENY_LIST" ] || [ "$DENY_LIST" = "[]" ] || [ "$DENY_LIST" = "null" ]; then
|
|
123
|
+
result_warn "No tools in deny list (consider blocking high-risk tools such as exec/process/browser)"
|
|
124
|
+
TOOL_ISSUES=$((TOOL_ISSUES + 1))
|
|
125
|
+
fi
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
if [ "$TOOL_ISSUES" -eq 0 ]; then
|
|
129
|
+
result_clean "Tool policies acceptable"
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
# ============================================================
|
|
133
|
+
# CHECK 2 (origin 26): Exec-Approvals Configuration
|
|
134
|
+
# ============================================================
|
|
135
|
+
header 2 "Auditing exec-approvals configuration..."
|
|
136
|
+
|
|
137
|
+
EXEC_ISSUES=0
|
|
138
|
+
EXEC_FILE="$OPENCLAW_DIR/exec-approvals.json"
|
|
139
|
+
|
|
140
|
+
if [ -f "$EXEC_FILE" ]; then
|
|
141
|
+
UNSAFE_EXEC="$(grep -iE '"security"\s*:\s*"allow"|"ask"\s*:\s*"off"|"allowlist"\s*:\s*\[\s*\]' "$EXEC_FILE" 2>/dev/null || true)"
|
|
142
|
+
if [ -n "$UNSAFE_EXEC" ]; then
|
|
143
|
+
result_critical "Exec-approvals has unsafe configuration"
|
|
144
|
+
log "$UNSAFE_EXEC"
|
|
145
|
+
EXEC_ISSUES=$((EXEC_ISSUES + 1))
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
EXEC_PERMS="$(get_perm "$EXEC_FILE")"
|
|
149
|
+
if [ "$EXEC_PERMS" != "600" ] && [ "$EXEC_PERMS" != "unknown" ]; then
|
|
150
|
+
result_warn "exec-approvals.json has permissions $EXEC_PERMS (recommended: 600)"
|
|
151
|
+
EXEC_ISSUES=$((EXEC_ISSUES + 1))
|
|
152
|
+
fi
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
if [ "$EXEC_ISSUES" -eq 0 ]; then
|
|
156
|
+
result_clean "Exec-approvals configuration acceptable"
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
# ============================================================
|
|
160
|
+
# CHECK 3 (origin 27 + EXEC-005/006/007): Docker Container Security
|
|
161
|
+
# ============================================================
|
|
162
|
+
header 3 "Auditing Docker container security..."
|
|
163
|
+
|
|
164
|
+
DOCKER_ISSUES=0
|
|
165
|
+
|
|
166
|
+
if command -v docker >/dev/null 2>&1; then
|
|
167
|
+
OC_CONTAINERS="$(docker ps --format '{{.Names}} {{.Image}}' 2>/dev/null | grep -iE 'openclaw|clawdbot|moltbot' || true)"
|
|
168
|
+
if [ -n "$OC_CONTAINERS" ]; then
|
|
169
|
+
while IFS= read -r container_line; do
|
|
170
|
+
[ -z "$container_line" ] && continue
|
|
171
|
+
CNAME="$(echo "$container_line" | awk '{print $1}')"
|
|
172
|
+
|
|
173
|
+
CUSER="$(docker inspect --format '{{.Config.User}}' "$CNAME" 2>/dev/null || echo "")"
|
|
174
|
+
if [ -z "$CUSER" ] || [ "$CUSER" = "root" ] || [ "$CUSER" = "0" ]; then
|
|
175
|
+
result_warn "Container '$CNAME' running as root"
|
|
176
|
+
DOCKER_ISSUES=$((DOCKER_ISSUES + 1))
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
DSOCK="$(docker inspect --format '{{range .Mounts}}{{.Source}} {{end}}' "$CNAME" 2>/dev/null | grep 'docker.sock' || true)"
|
|
180
|
+
if [ -n "$DSOCK" ]; then
|
|
181
|
+
result_critical "Container '$CNAME' has Docker socket mounted (container escape risk)"
|
|
182
|
+
DOCKER_ISSUES=$((DOCKER_ISSUES + 1))
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
PRIV="$(docker inspect --format '{{.HostConfig.Privileged}}' "$CNAME" 2>/dev/null || echo "")"
|
|
186
|
+
if [ "$PRIV" = "true" ]; then
|
|
187
|
+
result_critical "Container '$CNAME' is running in privileged mode"
|
|
188
|
+
DOCKER_ISSUES=$((DOCKER_ISSUES + 1))
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
# EXEC-005: cap_drop should include ALL
|
|
192
|
+
CAP_DROP_ALL="$(docker inspect --format '{{range .HostConfig.CapDrop}}{{println .}}{{end}}' "$CNAME" 2>/dev/null || true)"
|
|
193
|
+
if ! echo "$CAP_DROP_ALL" | grep -qx 'ALL'; then
|
|
194
|
+
result_warn "Container '$CNAME' retains Linux capabilities (cap_drop does not include ALL)"
|
|
195
|
+
DOCKER_ISSUES=$((DOCKER_ISSUES + 1))
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
# EXEC-006: no-new-privileges:true should be enabled
|
|
199
|
+
SECURITY_OPTS="$(docker inspect --format '{{range .HostConfig.SecurityOpt}}{{println .}}{{end}}' "$CNAME" 2>/dev/null || true)"
|
|
200
|
+
if ! echo "$SECURITY_OPTS" | grep -qx 'no-new-privileges:true'; then
|
|
201
|
+
result_warn "Container '$CNAME' allows privilege escalation (security_opt missing no-new-privileges:true)"
|
|
202
|
+
DOCKER_ISSUES=$((DOCKER_ISSUES + 1))
|
|
203
|
+
fi
|
|
204
|
+
|
|
205
|
+
# EXEC-007: network_mode should not be host
|
|
206
|
+
NETWORK_MODE="$(docker inspect --format '{{.HostConfig.NetworkMode}}' "$CNAME" 2>/dev/null || echo "")"
|
|
207
|
+
if [ "$NETWORK_MODE" = "host" ]; then
|
|
208
|
+
result_critical "Container '$CNAME' uses host network mode (bypasses network isolation)"
|
|
209
|
+
DOCKER_ISSUES=$((DOCKER_ISSUES + 1))
|
|
210
|
+
fi
|
|
211
|
+
done <<EOF
|
|
212
|
+
$OC_CONTAINERS
|
|
213
|
+
EOF
|
|
214
|
+
else
|
|
215
|
+
log " No OpenClaw-related Docker containers detected"
|
|
216
|
+
fi
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
if [ "$DOCKER_ISSUES" -eq 0 ]; then
|
|
220
|
+
result_clean "Docker security acceptable"
|
|
221
|
+
fi
|
|
222
|
+
|
|
223
|
+
# ============================================================
|
|
224
|
+
# CHECK 4 (origin 35): Exec safeBins Validation Bypass
|
|
225
|
+
# ============================================================
|
|
226
|
+
header 4 "Checking exec safeBins bypass (CVE-2026-28363)..."
|
|
227
|
+
|
|
228
|
+
SAFEBINS_ISSUES=0
|
|
229
|
+
|
|
230
|
+
if [ "$OPENCLAW_PRESENT" = true ]; then
|
|
231
|
+
if version_lt 2026 2 23; then
|
|
232
|
+
result_critical "OpenClaw v$OC_VERSION vulnerable to CVE-2026-28363 (safeBins bypass via sort --compress-prog). Update to v2026.2.23+"
|
|
233
|
+
SAFEBINS_ISSUES=$((SAFEBINS_ISSUES + 1))
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
SAFE_BINS="$(get_oc_config "tools.exec.safeBins" "")"
|
|
237
|
+
if echo "$SAFE_BINS" | grep -q '"sort"' 2>/dev/null; then
|
|
238
|
+
log " INFO: 'sort' is in safeBins list (ensure v2026.2.23+)"
|
|
239
|
+
fi
|
|
240
|
+
fi
|
|
241
|
+
|
|
242
|
+
if [ "$SAFEBINS_ISSUES" -eq 0 ]; then
|
|
243
|
+
result_clean "Exec safeBins validation acceptable"
|
|
244
|
+
fi
|
|
245
|
+
|
|
246
|
+
# ============================================================
|
|
247
|
+
# CHECK 5 (origin 37): PATH Hijacking / Command Hijacking
|
|
248
|
+
# ============================================================
|
|
249
|
+
header 5 "Checking PATH hijacking risk (GHSA-jqpq)..."
|
|
250
|
+
|
|
251
|
+
PATH_ISSUES=0
|
|
252
|
+
|
|
253
|
+
IFS=':' read -r -a PATH_DIRS <<< "${PATH:-}"
|
|
254
|
+
|
|
255
|
+
for PDIR in "${PATH_DIRS[@]}"; do
|
|
256
|
+
[ -z "$PDIR" ] && continue
|
|
257
|
+
if [ -d "$PDIR" ] && [ -w "$PDIR" ]; then
|
|
258
|
+
case "$PDIR" in
|
|
259
|
+
"$HOME/bin"|"$HOME/.local/bin"|"$HOME/.cargo/bin"|"$HOME/go/bin")
|
|
260
|
+
continue
|
|
261
|
+
;;
|
|
262
|
+
esac
|
|
263
|
+
|
|
264
|
+
for CMD in node python3 bash curl git ssh openclaw; do
|
|
265
|
+
if [ -f "$PDIR/$CMD" ] && [ -x "$PDIR/$CMD" ]; then
|
|
266
|
+
SYS_BIN="$(which -a "$CMD" 2>/dev/null | tail -1 || true)"
|
|
267
|
+
if [ -n "$SYS_BIN" ] && [ "$PDIR/$CMD" != "$SYS_BIN" ]; then
|
|
268
|
+
result_critical "PATH hijack: $PDIR/$CMD shadows system $SYS_BIN (GHSA-jqpq)"
|
|
269
|
+
PATH_ISSUES=$((PATH_ISSUES + 1))
|
|
270
|
+
fi
|
|
271
|
+
fi
|
|
272
|
+
done
|
|
273
|
+
fi
|
|
274
|
+
done
|
|
275
|
+
|
|
276
|
+
if [ -d "$WORKSPACE_DIR" ]; then
|
|
277
|
+
PLANTED="$(find "$WORKSPACE_DIR" -maxdepth 3 -type f \( -name "node" -o -name "python3" -o -name "bash" -o -name "curl" -o -name "git" \) 2>/dev/null | head -5 || true)"
|
|
278
|
+
if [ -n "$PLANTED" ]; then
|
|
279
|
+
while IFS= read -r pbin; do
|
|
280
|
+
[ -z "$pbin" ] && continue
|
|
281
|
+
if [ -x "$pbin" ]; then
|
|
282
|
+
result_critical "Planted executable in workspace: $pbin (PATH hijack vector)"
|
|
283
|
+
PATH_ISSUES=$((PATH_ISSUES + 1))
|
|
284
|
+
fi
|
|
285
|
+
done <<EOF
|
|
286
|
+
$PLANTED
|
|
287
|
+
EOF
|
|
288
|
+
fi
|
|
289
|
+
fi
|
|
290
|
+
|
|
291
|
+
if [ "$PATH_ISSUES" -eq 0 ]; then
|
|
292
|
+
result_clean "No PATH hijacking indicators found"
|
|
293
|
+
fi
|
|
294
|
+
|
|
295
|
+
# ============================================================
|
|
296
|
+
# CHECK 6 (origin 43): Exec-Approvals Shell Expansion Bypass
|
|
297
|
+
# ============================================================
|
|
298
|
+
header 6 "Checking exec-approvals shell expansion bypass (CVE-2026-28463)..."
|
|
299
|
+
|
|
300
|
+
SHEXP_ISSUES=0
|
|
301
|
+
|
|
302
|
+
if [ "$OPENCLAW_PRESENT" = true ]; then
|
|
303
|
+
if version_lt 2026 2 14; then
|
|
304
|
+
result_critical "Exec-approvals shell expansion bypass (CVE-2026-28463). Update to v2026.2.14+"
|
|
305
|
+
SHEXP_ISSUES=$((SHEXP_ISSUES + 1))
|
|
306
|
+
fi
|
|
307
|
+
|
|
308
|
+
SAFE_BINS="$(get_oc_config "tools.exec.safeBins" "")"
|
|
309
|
+
for RBIN in head tail grep cat; do
|
|
310
|
+
if echo "$SAFE_BINS" | grep -q "\"$RBIN\"" 2>/dev/null; then
|
|
311
|
+
log " INFO: '$RBIN' in safeBins - ensure v2026.2.14+"
|
|
312
|
+
fi
|
|
313
|
+
done
|
|
314
|
+
fi
|
|
315
|
+
|
|
316
|
+
if [ "$SHEXP_ISSUES" -eq 0 ]; then
|
|
317
|
+
result_clean "Exec-approvals shell expansion handling acceptable"
|
|
318
|
+
fi
|
|
319
|
+
|
|
320
|
+
# ============================================================
|
|
321
|
+
# CHECK 7 (origin 44): Approval Field Injection / Exec Gating Bypass
|
|
322
|
+
# ============================================================
|
|
323
|
+
header 7 "Checking approval field injection bypass (CVE-2026-28466)..."
|
|
324
|
+
|
|
325
|
+
AFI_ISSUES=0
|
|
326
|
+
|
|
327
|
+
if [ "$OPENCLAW_PRESENT" = true ]; then
|
|
328
|
+
if version_lt 2026 2 14; then
|
|
329
|
+
result_critical "Approval field injection bypass (CVE-2026-28466). Update to v2026.2.14+"
|
|
330
|
+
AFI_ISSUES=$((AFI_ISSUES + 1))
|
|
331
|
+
fi
|
|
332
|
+
fi
|
|
333
|
+
|
|
334
|
+
if [ "$AFI_ISSUES" -eq 0 ]; then
|
|
335
|
+
result_clean "Approval field sanitization acceptable"
|
|
336
|
+
fi
|
|
337
|
+
|
|
338
|
+
# ============================================================
|
|
339
|
+
# CHECK 8 (origin 42): Browser Control API Path Traversal
|
|
340
|
+
# ============================================================
|
|
341
|
+
header 8 "Checking browser control path traversal (CVE-2026-28462)..."
|
|
342
|
+
|
|
343
|
+
BCTRL_ISSUES=0
|
|
344
|
+
|
|
345
|
+
if [ "$OPENCLAW_PRESENT" = true ]; then
|
|
346
|
+
if version_lt 2026 2 13; then
|
|
347
|
+
result_critical "Browser control API path traversal (CVE-2026-28462). Update to v2026.2.13+"
|
|
348
|
+
BCTRL_ISSUES=$((BCTRL_ISSUES + 1))
|
|
349
|
+
fi
|
|
350
|
+
fi
|
|
351
|
+
|
|
352
|
+
if [ "$BCTRL_ISSUES" -eq 0 ]; then
|
|
353
|
+
result_clean "Browser control path handling acceptable"
|
|
354
|
+
fi
|
|
355
|
+
|
|
356
|
+
# ============================================================
|
|
357
|
+
# CHECK 9 (origin 47): TAR Archive Path Traversal
|
|
358
|
+
# ============================================================
|
|
359
|
+
header 9 "Checking TAR archive path traversal (CVE-2026-28453)..."
|
|
360
|
+
|
|
361
|
+
TAR_ISSUES=0
|
|
362
|
+
|
|
363
|
+
if [ "$OPENCLAW_PRESENT" = true ]; then
|
|
364
|
+
if version_lt 2026 2 14; then
|
|
365
|
+
result_critical "TAR archive path traversal (CVE-2026-28453). Update to v2026.2.14+"
|
|
366
|
+
TAR_ISSUES=$((TAR_ISSUES + 1))
|
|
367
|
+
fi
|
|
368
|
+
fi
|
|
369
|
+
|
|
370
|
+
if [ "$TAR_ISSUES" -eq 0 ]; then
|
|
371
|
+
result_clean "TAR archive handling acceptable"
|
|
372
|
+
fi
|
|
373
|
+
|
|
374
|
+
# ============================================================
|
|
375
|
+
# CHECK 10 (origin 19): Sandbox Configuration
|
|
376
|
+
# ============================================================
|
|
377
|
+
header 10 "Checking sandbox configuration..."
|
|
378
|
+
|
|
379
|
+
SANDBOX_ISSUES=0
|
|
380
|
+
|
|
381
|
+
if [ "$OPENCLAW_PRESENT" = true ]; then
|
|
382
|
+
SANDBOX_MODE="$(get_oc_config "sandbox.mode" "")"
|
|
383
|
+
if [ "$SANDBOX_MODE" = "off" ] || [ "$SANDBOX_MODE" = "none" ]; then
|
|
384
|
+
result_warn "Sandbox mode is disabled (recommended: mode='all')"
|
|
385
|
+
SANDBOX_ISSUES=$((SANDBOX_ISSUES + 1))
|
|
386
|
+
elif [ -n "$SANDBOX_MODE" ]; then
|
|
387
|
+
log " Sandbox mode: $SANDBOX_MODE"
|
|
388
|
+
fi
|
|
389
|
+
|
|
390
|
+
WORKSPACE_ACCESS="$(get_oc_config "sandbox.workspaceAccess" "")"
|
|
391
|
+
if [ "$WORKSPACE_ACCESS" = "rw" ]; then
|
|
392
|
+
log " INFO: Sandbox workspace access is read-write"
|
|
393
|
+
fi
|
|
394
|
+
fi
|
|
395
|
+
|
|
396
|
+
if [ "$SANDBOX_ISSUES" -eq 0 ]; then
|
|
397
|
+
result_clean "Sandbox configuration acceptable"
|
|
398
|
+
fi
|
|
399
|
+
|
|
400
|
+
# ============================================================
|
|
401
|
+
# CHECK 11 (origin 28): Node.js Version / Known CVEs
|
|
402
|
+
# ============================================================
|
|
403
|
+
header 11 "Checking Node.js version for known CVEs..."
|
|
404
|
+
|
|
405
|
+
NODE_ISSUES=0
|
|
406
|
+
|
|
407
|
+
if command -v node >/dev/null 2>&1; then
|
|
408
|
+
NODE_VER="$(node --version 2>/dev/null | sed 's/^v//')"
|
|
409
|
+
NODE_MAJOR="$(echo "$NODE_VER" | cut -d. -f1)"
|
|
410
|
+
NODE_MINOR="$(echo "$NODE_VER" | cut -d. -f2)"
|
|
411
|
+
NODE_PATCH="$(echo "$NODE_VER" | cut -d. -f3)"
|
|
412
|
+
log " Node.js version: v$NODE_VER"
|
|
413
|
+
|
|
414
|
+
if [ -n "$NODE_MAJOR" ] && [ "$NODE_MAJOR" -eq 22 ] 2>/dev/null && [ -n "$NODE_MINOR" ] && [ "$NODE_MINOR" -lt 12 ] 2>/dev/null; then
|
|
415
|
+
result_warn "Node.js v$NODE_VER is vulnerable to CVE-2026-21636 (permission model bypass). Upgrade to 22.12.0+"
|
|
416
|
+
NODE_ISSUES=$((NODE_ISSUES + 1))
|
|
417
|
+
elif [ -n "$NODE_MAJOR" ] && [ "$NODE_MAJOR" -lt 22 ] 2>/dev/null; then
|
|
418
|
+
result_warn "Node.js v$NODE_VER is below recommended v22 LTS"
|
|
419
|
+
NODE_ISSUES=$((NODE_ISSUES + 1))
|
|
420
|
+
fi
|
|
421
|
+
else
|
|
422
|
+
log " Node.js not found"
|
|
423
|
+
fi
|
|
424
|
+
|
|
425
|
+
if [ "$NODE_ISSUES" -eq 0 ]; then
|
|
426
|
+
result_clean "Node.js version acceptable"
|
|
427
|
+
fi
|
|
428
|
+
|
|
429
|
+
# ============================================================
|
|
430
|
+
# CHECK 12 (origin 24): Log Redaction Audit
|
|
431
|
+
# ============================================================
|
|
432
|
+
header 12 "Checking log redaction settings..."
|
|
433
|
+
|
|
434
|
+
LOG_ISSUES=0
|
|
435
|
+
|
|
436
|
+
if [ "$OPENCLAW_PRESENT" = true ]; then
|
|
437
|
+
REDACT="$(get_oc_config "logging.redactSensitive" "")"
|
|
438
|
+
if [ "$REDACT" = "off" ] || [ "$REDACT" = "false" ] || [ "$REDACT" = "none" ]; then
|
|
439
|
+
result_warn "Log redaction is disabled (sensitive data may leak to logs)"
|
|
440
|
+
LOG_ISSUES=$((LOG_ISSUES + 1))
|
|
441
|
+
elif [ -n "$REDACT" ]; then
|
|
442
|
+
log " Log redaction: $REDACT"
|
|
443
|
+
fi
|
|
444
|
+
|
|
445
|
+
GW_LOG="/tmp/openclaw"
|
|
446
|
+
if [ -d "$GW_LOG" ]; then
|
|
447
|
+
GW_LOG_PERMS="$(get_perm "$GW_LOG")"
|
|
448
|
+
if [ "$GW_LOG_PERMS" != "700" ] && [ "$GW_LOG_PERMS" != "750" ] && [ "$GW_LOG_PERMS" != "unknown" ]; then
|
|
449
|
+
result_warn "Gateway log dir /tmp/openclaw has permissions $GW_LOG_PERMS (recommended: 700)"
|
|
450
|
+
LOG_ISSUES=$((LOG_ISSUES + 1))
|
|
451
|
+
fi
|
|
452
|
+
fi
|
|
453
|
+
fi
|
|
454
|
+
|
|
455
|
+
if [ "$LOG_ISSUES" -eq 0 ]; then
|
|
456
|
+
result_clean "Log redaction settings acceptable"
|
|
457
|
+
fi
|
|
458
|
+
|
|
459
|
+
# ============================================================
|
|
460
|
+
# CHECK 13 (origin 22): Persistence Mechanism Scan
|
|
461
|
+
# ============================================================
|
|
462
|
+
header 13 "Scanning for unauthorized persistence mechanisms..."
|
|
463
|
+
|
|
464
|
+
PERSIST_ISSUES=0
|
|
465
|
+
|
|
466
|
+
if [ -d "$HOME/Library/LaunchAgents" ]; then
|
|
467
|
+
SUSPICIOUS_AGENTS="$(find "$HOME/Library/LaunchAgents" -name "*.plist" -exec grep -liE 'openclaw|clawdbot|moltbot' {} \; 2>/dev/null || true)"
|
|
468
|
+
if [ -n "$SUSPICIOUS_AGENTS" ]; then
|
|
469
|
+
log " LaunchAgents referencing OpenClaw-related strings:"
|
|
470
|
+
log "$SUSPICIOUS_AGENTS"
|
|
471
|
+
|
|
472
|
+
while IFS= read -r agent; do
|
|
473
|
+
[ -z "$agent" ] && continue
|
|
474
|
+
if ! grep -q "com.openclaw.security-dashboard" "$agent" 2>/dev/null; then
|
|
475
|
+
AGENT_LABEL="$(grep -A1 '<key>Label</key>' "$agent" 2>/dev/null | tail -1 | sed 's/.*<string>\(.*\)<\/string>.*/\1/' || true)"
|
|
476
|
+
result_warn "Unknown LaunchAgent: ${AGENT_LABEL:-unknown} ($(basename "$agent"))"
|
|
477
|
+
PERSIST_ISSUES=$((PERSIST_ISSUES + 1))
|
|
478
|
+
fi
|
|
479
|
+
done <<EOF
|
|
480
|
+
$SUSPICIOUS_AGENTS
|
|
481
|
+
EOF
|
|
482
|
+
fi
|
|
483
|
+
fi
|
|
484
|
+
|
|
485
|
+
CRON_ENTRIES="$(crontab -l 2>/dev/null | grep -ivE "${SELF_DIR_NAME}|#" | grep -iE 'openclaw|clawdbot|moltbot|curl.*\|.*sh|wget.*\|.*bash' || true)"
|
|
486
|
+
if [ -n "$CRON_ENTRIES" ]; then
|
|
487
|
+
result_warn "Suspicious cron entries found"
|
|
488
|
+
log "$CRON_ENTRIES"
|
|
489
|
+
PERSIST_ISSUES=$((PERSIST_ISSUES + 1))
|
|
490
|
+
fi
|
|
491
|
+
|
|
492
|
+
if command -v systemctl >/dev/null 2>&1; then
|
|
493
|
+
SYS_SERVICES="$(systemctl --user list-units --type=service --all 2>/dev/null | grep -iE 'openclaw|clawdbot|moltbot' | grep -v "$SELF_DIR_NAME" || true)"
|
|
494
|
+
if [ -n "$SYS_SERVICES" ]; then
|
|
495
|
+
log " User systemd services referencing OpenClaw-related strings:"
|
|
496
|
+
log "$SYS_SERVICES"
|
|
497
|
+
fi
|
|
498
|
+
fi
|
|
499
|
+
|
|
500
|
+
if [ "$PERSIST_ISSUES" -eq 0 ]; then
|
|
501
|
+
result_clean "No unauthorized persistence mechanisms"
|
|
502
|
+
fi
|