whitzard-claw 1.0.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.
Files changed (91) hide show
  1. package/README.md +89 -0
  2. package/bin/whitzard-tui.js +73 -0
  3. package/bin/whitzard-webui.js +67 -0
  4. package/dist/tui/tui.js +38733 -0
  5. package/dist/webui/index.html +1235 -0
  6. package/dist/webui/server.js +876 -0
  7. package/ioc/c2-ips.txt +25 -0
  8. package/ioc/file-hashes.txt +13 -0
  9. package/ioc/malicious-domains.txt +46 -0
  10. package/ioc/malicious-hashes.txt +5 -0
  11. package/ioc/malicious-publishers.txt +34 -0
  12. package/ioc/malicious-skill-patterns.txt +87 -0
  13. package/package.json +50 -0
  14. package/scripts/check/access_control.sh +183 -0
  15. package/scripts/check/credential_storage.sh +222 -0
  16. package/scripts/check/execution_sandbox.sh +502 -0
  17. package/scripts/check/memory_poisoning.sh +334 -0
  18. package/scripts/check/network_exposure.sh +479 -0
  19. package/scripts/check/resource_cost.sh +182 -0
  20. package/scripts/check/supply_chain.sh +553 -0
  21. package/scripts/repair/access_control/_common.sh +249 -0
  22. package/scripts/repair/access_control/check_1.sh +28 -0
  23. package/scripts/repair/access_control/check_2.sh +27 -0
  24. package/scripts/repair/access_control/check_3.sh +23 -0
  25. package/scripts/repair/access_control/check_4.sh +23 -0
  26. package/scripts/repair/access_control/check_5.sh +20 -0
  27. package/scripts/repair/credential_storage/_common.sh +277 -0
  28. package/scripts/repair/credential_storage/check_1.sh +47 -0
  29. package/scripts/repair/credential_storage/check_2.sh +35 -0
  30. package/scripts/repair/credential_storage/check_3.sh +53 -0
  31. package/scripts/repair/credential_storage/logs/security-scan.log +15 -0
  32. package/scripts/repair/execution_sandbox/_common.sh +302 -0
  33. package/scripts/repair/execution_sandbox/check_1.sh +67 -0
  34. package/scripts/repair/execution_sandbox/check_10.sh +23 -0
  35. package/scripts/repair/execution_sandbox/check_11.sh +34 -0
  36. package/scripts/repair/execution_sandbox/check_12.sh +38 -0
  37. package/scripts/repair/execution_sandbox/check_13.sh +29 -0
  38. package/scripts/repair/execution_sandbox/check_2.sh +46 -0
  39. package/scripts/repair/execution_sandbox/check_3.sh +37 -0
  40. package/scripts/repair/execution_sandbox/check_4.sh +23 -0
  41. package/scripts/repair/execution_sandbox/check_5.sh +28 -0
  42. package/scripts/repair/execution_sandbox/check_6.sh +17 -0
  43. package/scripts/repair/execution_sandbox/check_7.sh +17 -0
  44. package/scripts/repair/execution_sandbox/check_8.sh +17 -0
  45. package/scripts/repair/execution_sandbox/check_9.sh +17 -0
  46. package/scripts/repair/execution_sandbox/logs/security-scan.log +10 -0
  47. package/scripts/repair/memory_poisoning/_common.sh +336 -0
  48. package/scripts/repair/memory_poisoning/check_1.sh +51 -0
  49. package/scripts/repair/memory_poisoning/check_2.sh +26 -0
  50. package/scripts/repair/memory_poisoning/check_3.sh +24 -0
  51. package/scripts/repair/memory_poisoning/check_4.sh +27 -0
  52. package/scripts/repair/memory_poisoning/check_5.sh +20 -0
  53. package/scripts/repair/network_exposure/_common.sh +330 -0
  54. package/scripts/repair/network_exposure/check_1.sh +86 -0
  55. package/scripts/repair/network_exposure/check_10.sh +16 -0
  56. package/scripts/repair/network_exposure/check_11.sh +31 -0
  57. package/scripts/repair/network_exposure/check_12.sh +24 -0
  58. package/scripts/repair/network_exposure/check_2.sh +26 -0
  59. package/scripts/repair/network_exposure/check_3.sh +43 -0
  60. package/scripts/repair/network_exposure/check_4.sh +23 -0
  61. package/scripts/repair/network_exposure/check_5.sh +16 -0
  62. package/scripts/repair/network_exposure/check_6.sh +98 -0
  63. package/scripts/repair/network_exposure/check_7.sh +35 -0
  64. package/scripts/repair/network_exposure/check_8.sh +19 -0
  65. package/scripts/repair/network_exposure/check_9.sh +19 -0
  66. package/scripts/repair/resource_cost/_common.sh +303 -0
  67. package/scripts/repair/resource_cost/check_1.sh +16 -0
  68. package/scripts/repair/resource_cost/check_2.sh +16 -0
  69. package/scripts/repair/resource_cost/check_3.sh +23 -0
  70. package/scripts/repair/supply_chain/_common.sh +222 -0
  71. package/scripts/repair/supply_chain/check_1.sh +95 -0
  72. package/scripts/repair/supply_chain/check_10.sh +60 -0
  73. package/scripts/repair/supply_chain/check_11.sh +63 -0
  74. package/scripts/repair/supply_chain/check_12.sh +36 -0
  75. package/scripts/repair/supply_chain/check_13.sh +44 -0
  76. package/scripts/repair/supply_chain/check_14.sh +33 -0
  77. package/scripts/repair/supply_chain/check_15.sh +33 -0
  78. package/scripts/repair/supply_chain/check_16.sh +34 -0
  79. package/scripts/repair/supply_chain/check_17.sh +61 -0
  80. package/scripts/repair/supply_chain/check_18.sh +62 -0
  81. package/scripts/repair/supply_chain/check_2.sh +93 -0
  82. package/scripts/repair/supply_chain/check_3.sh +78 -0
  83. package/scripts/repair/supply_chain/check_4.sh +72 -0
  84. package/scripts/repair/supply_chain/check_5.sh +73 -0
  85. package/scripts/repair/supply_chain/check_6.sh +81 -0
  86. package/scripts/repair/supply_chain/check_7.sh +52 -0
  87. package/scripts/repair/supply_chain/check_8.sh +71 -0
  88. package/scripts/repair/supply_chain/check_9.sh +78 -0
  89. package/scripts/repair/supply_chain/logs/security-scan.log +77 -0
  90. package/scripts/scan.sh +228 -0
  91. package/webui/index.html +1235 -0
@@ -0,0 +1,334 @@
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
+ has_pcre_grep() {
13
+ echo 'test' | grep -P 't.st' >/dev/null 2>&1
14
+ }
15
+
16
+ get_oc_config() {
17
+ # Usage: get_oc_config <key> <default>
18
+ local key="$1"
19
+ local default_val="${2:-}"
20
+ if [ "$OPENCLAW_PRESENT" = true ]; then
21
+ run_with_timeout 10 openclaw config get "$key" 2>/dev/null || echo "$default_val"
22
+ else
23
+ echo "$default_val"
24
+ fi
25
+ }
26
+
27
+ normalize_config_val() {
28
+ local val="${1:-}"
29
+ case "$val" in
30
+ null|undefined|"")
31
+ echo ""
32
+ ;;
33
+ *)
34
+ echo "$val"
35
+ ;;
36
+ esac
37
+ }
38
+
39
+ get_perm() {
40
+ local target="$1"
41
+
42
+ [ -e "$target" ] || {
43
+ echo "unknown"
44
+ return
45
+ }
46
+
47
+ case "$(uname -s)" in
48
+ Darwin|FreeBSD|OpenBSD|NetBSD)
49
+ stat -f '%Lp' "$target" 2>/dev/null || echo "unknown"
50
+ ;;
51
+ Linux)
52
+ stat -c '%a' "$target" 2>/dev/null || echo "unknown"
53
+ ;;
54
+ *)
55
+ stat -c '%a' "$target" 2>/dev/null || stat -f '%Lp' "$target" 2>/dev/null || echo "unknown"
56
+ ;;
57
+ esac
58
+ }
59
+
60
+ list_memory_files() {
61
+ # Enumerate workspace-level and agent-level memory files
62
+ local f
63
+ for f in \
64
+ "$WORKSPACE_DIR/SOUL.md" \
65
+ "$WORKSPACE_DIR/MEMORY.md" \
66
+ "$WORKSPACE_DIR/IDENTITY.md"
67
+ do
68
+ [ -f "$f" ] && echo "$f"
69
+ done
70
+
71
+ if [ -d "$OPENCLAW_DIR/agents" ]; then
72
+ find "$OPENCLAW_DIR/agents" -mindepth 2 -maxdepth 2 -type f \
73
+ \( -name 'soul.md' -o -name 'SOUL.md' -o -name 'MEMORY.md' -o -name 'IDENTITY.md' \) \
74
+ 2>/dev/null || true
75
+ fi
76
+ }
77
+
78
+ extract_allowed_domains() {
79
+ # Build allowlist from config + defaults
80
+ # Defaults follow the TS snippet.
81
+ local cfg raw_domains
82
+ cfg="$(normalize_config_val "$(get_oc_config "secureclaw.network.egressAllowlist" "")")"
83
+
84
+ {
85
+ echo "api.anthropic.com"
86
+ echo "api.openai.com"
87
+ echo "generativelanguage.googleapis.com"
88
+ if [ -n "$cfg" ]; then
89
+ echo "$cfg" | grep -oE '[A-Za-z0-9.-]+\.[A-Za-z]{2,}' || true
90
+ fi
91
+ } | awk 'NF' | sort -u
92
+ }
93
+
94
+ url_host_allowed() {
95
+ # Usage: url_host_allowed <hostname>
96
+ local host="${1:-}"
97
+ local allowed
98
+ [ -n "$host" ] || return 1
99
+
100
+ while IFS= read -r allowed; do
101
+ [ -z "$allowed" ] && continue
102
+ if [ "$host" = "$allowed" ]; then
103
+ return 0
104
+ fi
105
+ case "$host" in
106
+ *."$allowed")
107
+ return 0
108
+ ;;
109
+ esac
110
+ done <<EOF
111
+ $ALLOWED_DOMAINS
112
+ EOF
113
+
114
+ return 1
115
+ }
116
+
117
+ # Precompute allowed domains once
118
+ ALLOWED_DOMAINS="$(extract_allowed_domains)"
119
+
120
+ # ============================================================
121
+ # CHECK 1 (origin 10 + expanded patterns): Memory Poisoning Detection
122
+ # ============================================================
123
+ header 1 "Checking memory files for poisoning indicators..."
124
+
125
+ MEMORY_POISON=0
126
+ MEMORY_FILES_FOUND=0
127
+
128
+ while IFS= read -r memfile; do
129
+ [ -z "$memfile" ] && continue
130
+ [ -f "$memfile" ] || continue
131
+
132
+ MEMORY_FILES_FOUND=$((MEMORY_FILES_FOUND + 1))
133
+
134
+ # Expanded but still relatively conservative injection/exfiltration patterns
135
+ POISON_HITS="$(grep -inE \
136
+ 'ignore[[:space:]]+previous[[:space:]]+instructions|ignore[[:space:]]+all[[:space:]]+previous|disregard[[:space:]]+(all[[:space:]]+)?previous|override.*instruction|new[[:space:]]+system[[:space:]]+prompt|system[[:space:]]+prompt|forget.*previous|you[[:space:]]+are[[:space:]]+now|act[[:space:]]+as[[:space:]]+if|pretend[[:space:]]+to[[:space:]]+be|from[[:space:]]+now[[:space:]]+on.*ignore|forward[[:space:]]+to|send[[:space:]]+to|exfiltrat(e|ion)|leak[[:space:]]+(the[[:space:]]+)?(secret|token|credential|key)|reveal[[:space:]]+(the[[:space:]]+)?(system[[:space:]]+prompt|secret|token|credential|key)|post[[:space:]]+to[[:space:]]+https?://|upload[[:space:]]+to[[:space:]]+https?://' \
137
+ "$memfile" 2>/dev/null || true)"
138
+
139
+ if [ -n "$POISON_HITS" ]; then
140
+ result_critical "Memory poisoning indicators detected in $(basename "$memfile")"
141
+ log "$POISON_HITS"
142
+ MEMORY_POISON=$((MEMORY_POISON + 1))
143
+ fi
144
+ done <<EOF
145
+ $(list_memory_files)
146
+ EOF
147
+
148
+ if [ "$MEMORY_POISON" -eq 0 ]; then
149
+ if [ "$MEMORY_FILES_FOUND" -eq 0 ]; then
150
+ result_clean "No memory files found; poisoning check skipped"
151
+ else
152
+ result_clean "No poisoning indicators found in memory files"
153
+ fi
154
+ fi
155
+
156
+ # ============================================================
157
+ # CHECK 2 (origin 40): Log Poisoning / WebSocket Header Injection
158
+ # ============================================================
159
+ header 2 "Checking log poisoning / WebSocket header injection..."
160
+
161
+ LOG_POISON_ISSUES=0
162
+ LOG_FILES_PRESENT=0
163
+
164
+ if [ -d "$LOG_DIR" ]; then
165
+ LOG_FILES_PRESENT="$(find "$LOG_DIR" -type f ! -name '*.gz' 2>/dev/null | wc -l | tr -d ' ')"
166
+
167
+ # ---- Check 1: ANSI escape sequences
168
+ ANSI_HITS=""
169
+ if has_pcre_grep; then
170
+ ANSI_HITS="$(grep -rlP '\x1b\[' "$LOG_DIR" --exclude='*.gz' 2>/dev/null | head -5 || true)"
171
+ else
172
+ ANSI_HITS="$(LC_ALL=C grep -rl "$(printf '\033[')" "$LOG_DIR" --exclude='*.gz' 2>/dev/null | head -5 || true)"
173
+ fi
174
+
175
+ if [ -n "$ANSI_HITS" ]; then
176
+ ANSI_COUNT="$(echo "$ANSI_HITS" | wc -l | tr -d ' ')"
177
+ result_warn "ANSI escape sequences found in $ANSI_COUNT log file(s) - possible log poisoning"
178
+ log "$ANSI_HITS"
179
+ LOG_POISON_ISSUES=$((LOG_POISON_ISSUES + 1))
180
+ fi
181
+
182
+ # ---- Check 2: nested/fake log-entry patterns
183
+ FAKE_ENTRIES="$(grep -rlE '\]\s*(INFO|WARN|WARNING|ERROR|CRITICAL)\s*:.*\[20[0-9]{2}-' "$LOG_DIR" --exclude='*.gz' 2>/dev/null | head -3 || true)"
184
+ if [ -n "$FAKE_ENTRIES" ]; then
185
+ while IFS= read -r ffile; do
186
+ [ -z "$ffile" ] && continue
187
+ NESTED="$(grep -cE '^\[.*\]\s*(INFO|WARN|WARNING|ERROR|CRITICAL).*\[20[0-9]{2}-[0-9]{2}' "$ffile" 2>/dev/null || echo "0")"
188
+ if [ "${NESTED:-0}" -gt 0 ] 2>/dev/null; then
189
+ result_warn "Possible injected log entries in $(basename "$ffile") - log poisoning indicator"
190
+ log "$ffile"
191
+ LOG_POISON_ISSUES=$((LOG_POISON_ISSUES + 1))
192
+ break
193
+ fi
194
+ done <<EOF
195
+ $FAKE_ENTRIES
196
+ EOF
197
+ fi
198
+
199
+ # ---- Check 3: control characters in logs
200
+ CTRL_HITS=""
201
+ if has_pcre_grep; then
202
+ CTRL_HITS="$(grep -rlP '[\x00-\x08\x0e-\x1f]' "$LOG_DIR" --exclude='*.gz' 2>/dev/null | head -3 || true)"
203
+ else
204
+ if command -v perl >/dev/null 2>&1; then
205
+ CTRL_HITS="$(
206
+ find "$LOG_DIR" -type f ! -name '*.gz' 2>/dev/null | while IFS= read -r f; do
207
+ perl -ne 'if (/[\x00-\x08\x0e-\x1f]/) { print "$ARGV\n"; exit 0 }' "$f" 2>/dev/null
208
+ done | head -3 || true
209
+ )"
210
+ fi
211
+ fi
212
+
213
+ if [ -n "$CTRL_HITS" ]; then
214
+ result_warn "Control characters found in logs - possible WebSocket header injection"
215
+ log "$CTRL_HITS"
216
+ LOG_POISON_ISSUES=$((LOG_POISON_ISSUES + 1))
217
+ fi
218
+ fi
219
+
220
+
221
+ if [ "$LOG_POISON_ISSUES" -eq 0 ]; then
222
+ if [ "${LOG_FILES_PRESENT:-0}" -eq 0 ] 2>/dev/null; then
223
+ result_clean "No log files found; log-poisoning check skipped"
224
+ else
225
+ result_clean "No log poisoning indicators found"
226
+ fi
227
+ fi
228
+
229
+ # ============================================================
230
+ # CHECK 3 (new / MEM-003): Base64 encoded blocks in memory
231
+ # ============================================================
232
+ header 3 "Checking memory files for long base64-encoded blocks..."
233
+
234
+ BASE64_ISSUES=0
235
+ BASE64_FILES_FOUND=0
236
+
237
+ while IFS= read -r memfile; do
238
+ [ -z "$memfile" ] && continue
239
+ [ -f "$memfile" ] || continue
240
+
241
+ BASE64_FILES_FOUND=$((BASE64_FILES_FOUND + 1))
242
+
243
+ # Conservative heuristic: very long base64-like block
244
+ if grep -Eq '(^|[^A-Za-z0-9+/=])[A-Za-z0-9+/]{160,}={0,2}([^A-Za-z0-9+/=]|$)' "$memfile" 2>/dev/null; then
245
+ result_warn "Long base64-like content found in $(basename "$memfile") - may hide malicious instructions"
246
+ log " Evidence: $memfile"
247
+ BASE64_ISSUES=$((BASE64_ISSUES + 1))
248
+ fi
249
+ done <<EOF
250
+ $(list_memory_files)
251
+ EOF
252
+
253
+ if [ "$BASE64_ISSUES" -eq 0 ]; then
254
+ if [ "$BASE64_FILES_FOUND" -eq 0 ]; then
255
+ result_clean "No memory files found; base64 check skipped"
256
+ else
257
+ result_clean "No suspicious base64 blocks found in memory files"
258
+ fi
259
+ fi
260
+
261
+ # ============================================================
262
+ # CHECK 4 (new / MEM-004): Non-whitelisted URLs in memory
263
+ # ============================================================
264
+ header 4 "Checking memory files for unexpected external URLs..."
265
+
266
+ URL_ISSUES=0
267
+ URL_FILES_FOUND=0
268
+
269
+ while IFS= read -r memfile; do
270
+ [ -z "$memfile" ] && continue
271
+ [ -f "$memfile" ] || continue
272
+
273
+ URL_FILES_FOUND=$((URL_FILES_FOUND + 1))
274
+
275
+ URLS="$(grep -oE 'https?://[^[:space:]"'"'"'<>)]+' "$memfile" 2>/dev/null | sort -u || true)"
276
+ if [ -n "$URLS" ]; then
277
+ while IFS= read -r url; do
278
+ [ -z "$url" ] && continue
279
+
280
+ # Extract hostname conservatively
281
+ URL_HOST="$(printf "%s\n" "$url" | sed -E 's#^https?://([^/:?#]+).*$#\1#' | tr '[:upper:]' '[:lower:]')"
282
+ [ -z "$URL_HOST" ] && continue
283
+
284
+ if ! url_host_allowed "$URL_HOST"; then
285
+ result_warn "Unexpected URL in $(basename "$memfile") points to non-whitelisted domain '$URL_HOST'"
286
+ log " Evidence: $memfile -> $url"
287
+ URL_ISSUES=$((URL_ISSUES + 1))
288
+ fi
289
+ done <<EOF
290
+ $URLS
291
+ EOF
292
+ fi
293
+ done <<EOF
294
+ $(list_memory_files)
295
+ EOF
296
+
297
+ if [ "$URL_ISSUES" -eq 0 ]; then
298
+ if [ "$URL_FILES_FOUND" -eq 0 ]; then
299
+ result_clean "No memory files found; URL allowlist check skipped"
300
+ else
301
+ result_clean "No unexpected external URLs found in memory files"
302
+ fi
303
+ fi
304
+
305
+ # ============================================================
306
+ # CHECK 5 (new / MEM-005): Memory file permissions
307
+ # ============================================================
308
+ header 5 "Checking memory file permissions..."
309
+
310
+ MEM_PERM_ISSUES=0
311
+ MEM_PERM_FILES_FOUND=0
312
+
313
+ while IFS= read -r memfile; do
314
+ [ -z "$memfile" ] && continue
315
+ [ -f "$memfile" ] || continue
316
+
317
+ MEM_PERM_FILES_FOUND=$((MEM_PERM_FILES_FOUND + 1))
318
+ MPERMS="$(get_perm "$memfile")"
319
+
320
+ if [ "$MPERMS" != "600" ] && [ "$MPERMS" != "unknown" ]; then
321
+ result_warn "Memory file $(basename "$memfile") has permissions $MPERMS (recommended: 600)"
322
+ MEM_PERM_ISSUES=$((MEM_PERM_ISSUES + 1))
323
+ fi
324
+ done <<EOF
325
+ $(list_memory_files)
326
+ EOF
327
+
328
+ if [ "$MEM_PERM_ISSUES" -eq 0 ]; then
329
+ if [ "$MEM_PERM_FILES_FOUND" -eq 0 ]; then
330
+ result_clean "No memory files found; permission check skipped"
331
+ else
332
+ result_clean "Memory file permissions acceptable"
333
+ fi
334
+ fi