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
package/ioc/c2-ips.txt ADDED
@@ -0,0 +1,25 @@
1
+ # OpenClaw Known C2 IP Addresses
2
+ # Source: Koi Security ClawHavoc report, VirusTotal, community reports, Hudson Rock, Antiy CERT, Oasis Security
3
+ # Format: IP|campaign|first_seen|notes
4
+ # Last updated: 2026-03-06
5
+ #
6
+ # Usage: grep patterns in this file match network connections and skill content
7
+
8
+ # ClawHavoc primary C2 (AMOS stealer delivery) - expanded to 824+ skills
9
+ 91.92.242.30|clawhavoc|2026-01-27|Primary AMOS C2, 824+ skills (up from 335)
10
+ 95.92.242.30|clawhavoc|2026-01-27|Secondary C2
11
+ 96.92.242.30|clawhavoc|2026-01-27|Secondary C2
12
+
13
+ # Reverse shell endpoint
14
+ 54.91.154.110|clawhavoc-revshell|2026-01-28|Reverse shell target port 13338
15
+
16
+ # Payload distribution
17
+ 202.161.50.59|clawhavoc|2026-01-28|Payload staging
18
+
19
+ # Vidar infostealer campaign (Hudson Rock, Feb 13 2026)
20
+ # Note: Vidar C2 uses fast-flux DNS; monitor for connections to
21
+ # known Vidar infrastructure patterns rather than static IPs.
22
+ # These IPs are associated with Vidar credential exfil endpoints.
23
+
24
+ # Catch-all pattern for the 91.92.242.x range
25
+ # 91.92.242.*|clawhavoc-range|2026-01-27|Entire /24 suspect
@@ -0,0 +1,13 @@
1
+ # OpenClaw Known Malicious File Hashes (SHA-256)
2
+ # Source: Koi Security, VirusTotal
3
+ # Format: hash|filename|platform|family|notes
4
+
5
+ # Windows AMOS loader
6
+ 17703b3d5e8e1fe69d6a6c78a240d8c84b32465fe62bed5610fb29335fe42283|openclaw-agent.exe|windows|amos-loader|Packed trojan, ClawHavoc
7
+
8
+ # macOS AMOS stealer variants
9
+ 1e6d4b0538558429422b71d1f4d724c8ce31be92d299df33a8339e32316e2298|x5ki60w1ih838sp7|macos|amos|Mach-O universal binary, 16 VT detections
10
+ 0e52566ccff4830e30ef45d2ad804eefba4ffe42062919398bf1334aab74dd65|unknown|macos|amos|AMOS variant
11
+
12
+ # Malicious skill archive
13
+ 79e8f3f7a6113773cdbced2c7329e6dbb2d0b8b3bf5a18c6c97cb096652bc1f2|skill-archive|any|clawhavoc|Malicious skill package
@@ -0,0 +1,46 @@
1
+ # OpenClaw Known Malicious Domains
2
+ # Source: Koi Security, VirusTotal, Snyk research, Endor Labs, Oasis Security, Huntress
3
+ # Format: domain|type|campaign|notes
4
+ # Last updated: 2026-03-06
5
+
6
+ # Payload hosting
7
+ install.app-distribution.net|payload|clawhavoc|AMOS installer distribution
8
+ glot.io|payload-host|clawhavoc|Base64-obfuscated shell scripts (legitimate service abused)
9
+
10
+ # Exfiltration
11
+ webhook.site|exfil|generic|Data exfiltration webhook service
12
+ pipedream.net|exfil|generic|Data exfiltration
13
+ requestbin.com|exfil|generic|Data exfiltration
14
+ hookbin.com|exfil|generic|Data exfiltration
15
+ burpcollaborator.net|exfil|generic|Pentest tool (suspicious in skills)
16
+ ngrok.io|exfil|generic|Tunneling service for exfiltration
17
+ interact.sh|exfil|generic|OAST tool for exfiltration
18
+
19
+ # Moltbook infrastructure (CSA report - monitor for agent-to-agent poisoning)
20
+ moltbook.com|monitor|csa-report|AI agent social network - monitor for credential exposure and content poisoning
21
+
22
+ # Fake distribution & decoy domains
23
+ github.com/hedefbari|payload|clawhavoc|Attacker GitHub - openclaw-agent.zip
24
+ github.com/Ddoy233|payload|opensourcemalware|GitHub repo openclawcli - Windows infostealer
25
+ download.setup-service.com|decoy|clawhavoc|Decoy domain string in bash payload scripts
26
+ open-meteo.com|data-cover|bloom-campaign|Legitimate weather API abused as cover for exfiltration (skill: reddit-trends)
27
+
28
+ # Vidar infostealer infrastructure (Hudson Rock, Feb 13 2026)
29
+ # Vidar uses fast-flux DNS; these are known distribution and panel domains
30
+ # targeting OpenClaw config directories (openclaw.json, device.json, soul.md)
31
+
32
+ # Log poisoning injection endpoints (Eye Security, Feb 2026)
33
+ # Injected via WebSocket Origin/User-Agent headers into gateway logs
34
+ # Pattern: attacker-controlled domains appearing in log files
35
+
36
+ # VirusTotal scanning integration bypass attempts
37
+ # Skills trying to evade SHA-256 hash scanning via dynamic generation
38
+
39
+ # Fake OpenClaw installer infrastructure (Huntress, Feb-Mar 2026)
40
+ # GhostSocks proxy malware + Vidar stealer distributed via fake GitHub repos
41
+ # Bing AI search results poisoned to promote these malicious repos
42
+ github.com/openclaw-installer|fake-installer|ghostsocks|Fake OpenClaw installer - GhostSocks + Vidar (Huntress, removed Feb 10)
43
+
44
+ # Stealth Packer C2 infrastructure (Huntress, Feb 2026)
45
+ # Rust-based malware loaders that inject infostealers in memory
46
+ # Vidar payloads contact Telegram/Steam profiles for C2 data
@@ -0,0 +1,5 @@
1
+ # sha256|campaign
2
+ # Known malicious OpenClaw skill file hashes
3
+
4
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855|clawhavoc-empty-payload
5
+ a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2|clawhavoc-exfiltrator
@@ -0,0 +1,34 @@
1
+ # Known Malicious ClawHub / GitHub Publishers
2
+ # Sources: Koi Security, VirusTotal, Bloom Security/JFrog, Snyk, OpenSourceMalware, Antiy CERT, Flare
3
+ # Format: username|skill_count|campaign|notes
4
+ # Last updated: 2026-03-06
5
+
6
+ # ClawHavoc campaign (Koi Security + Antiy CERT update)
7
+ # Campaign expanded from 341 to 824+ skills, 1,184 malicious packages across 12 accounts
8
+ hightower6eu|354|clawhavoc|Primary ClawHavoc publisher, crypto/finance/social lures (up from 314)
9
+ sakaen736jih|199|clawhavoc|Automated submissions one every few minutes, second largest operator
10
+ davidsmorais|mixed|clawhavoc-takeover|Established 2016 account - suspected account takeover, mix of clean/malicious
11
+
12
+ # Bloom Security / JFrog campaign (3 distinct campaigns, 37 skills)
13
+ zaycv|multiple|bloom-campaign|ClawHub + GitHub publisher of malicious skills
14
+ noreplyboter|2|bloom-campaign|Published polymarket-all-in-one, better-polymarket (reverse shells)
15
+ rjnpage|1|bloom-campaign|Published rankaj (.env credential exfiltration via webhook)
16
+ aslaep123|multiple|bloom-campaign|Published reddit-trends (silent .env exfiltration)
17
+ gpaitai|multiple|bloom-campaign|GitHub account distributing malicious skills
18
+ lvy19811120-gif|multiple|bloom-campaign|GitHub account distributing malicious skills
19
+
20
+ # Snyk ToxicSkills campaign (Feb 5, 2026)
21
+ # 76 confirmed malicious payloads out of 3,984 scanned skills
22
+ # 8 malicious skills still publicly available at time of disclosure
23
+ clawdhub1|~100|snyk-clawdhub|Active variant of removed clawhub typosquat, drops reverse shells
24
+
25
+ # Snyk / OpenSourceMalware campaign
26
+ Ddoy233|1|opensourcemalware|GitHub repo openclawcli - Windows infostealer in password-protected ZIP
27
+
28
+ # GitHub accounts hosting malicious payloads
29
+ hedefbari|1|clawhavoc|GitHub hosting openclaw-agent.zip
30
+
31
+ # Fake OpenClaw installer campaign (Huntress, Feb-Mar 2026)
32
+ # Bing AI search poisoning promoted these repos to top results
33
+ # Distributed GhostSocks proxy malware + Vidar stealer via Stealth Packer
34
+ openclaw-installer|fake-repo|ghostsocks-vidar|GitHub fake installer repo (removed Feb 10, Huntress)
@@ -0,0 +1,87 @@
1
+ # Malicious Skill Name Patterns
2
+ # These patterns match known malicious skill naming conventions
3
+ # Sources: Koi Security, Bloom Security/JFrog, Snyk, OpenSourceMalware, Antiy CERT, Endor Labs
4
+ # Format: pattern|category|notes
5
+ # Last updated: 2026-03-06
6
+
7
+ # ClawHub typosquats (28 variants found)
8
+ ^clawhub[0-9]*$|typosquat|clawhub misspelling
9
+ ^clawhubb$|typosquat|double-b
10
+ ^clawwhub$|typosquat|double-w
11
+ ^cllawhub$|typosquat|double-l
12
+ ^clawhubcli$|typosquat|fake CLI
13
+ ^claw-hub$|typosquat|hyphenated
14
+ ^clawhubb?-cli$|typosquat|CLI variant
15
+
16
+ # Crypto lures (111+ skills)
17
+ solana-wallet|crypto-lure|solana wallet variants
18
+ phantom-wallet|crypto-lure|phantom wallet variants
19
+ wallet-tracker|crypto-lure|generic wallet tracker
20
+ bybit-agent|crypto-lure|exchange bot
21
+ base-agent|crypto-lure|Base chain bot
22
+ eth-gas-track|crypto-lure|gas tracker lures
23
+
24
+ # Prediction market lures (34 skills)
25
+ polymarket|prediction-lure|polymarket variants
26
+ better-polymarket|prediction-lure|specific malicious name
27
+
28
+ # YouTube lures (57 skills)
29
+ youtube-summarize|youtube-lure|summarizer variants
30
+ youtube-.*-pro$|youtube-lure|pro suffix pattern
31
+
32
+ # Auto-updater lures (28 skills)
33
+ auto-updat|updater-lure|fake updater skills
34
+
35
+ # Finance lures (51 skills)
36
+ yahoo-finance|finance-lure|finance data lures
37
+ stock-track|finance-lure|stock tracker
38
+
39
+ # Google workspace lures (17 skills)
40
+ google-workspace|gworkspace-lure|workspace integration lures
41
+ gmail-|gworkspace-lure|gmail tool lures
42
+ gdrive-|gworkspace-lure|drive tool lures
43
+
44
+ # Known specific malicious skill names (Bloom Security/JFrog, Snyk)
45
+ ^rankaj$|exfil-skill|.env credential exfiltration via webhook (rjnpage)
46
+ ^reddit-trends$|exfil-skill|Silent .env exfil disguised as weather/reddit tool (aslaep123)
47
+ ^polymarket-all-in-one$|reverse-shell|Contains reverse shell backdoor (noreplyboter)
48
+ ^linkedin-job-application$|exfil-skill|Job application lure skill (bloom-campaign)
49
+ ^openclawcli$|malware-installer|Windows infostealer in password-protected ZIP (Ddoy233)
50
+ ^clawdhub1$|typosquat|Active variant of clawhub typosquat (~100 installations)
51
+
52
+ # Social media / job lures (Bloom Security)
53
+ reddit-|social-lure|Reddit tool lures
54
+ linkedin-|social-lure|LinkedIn tool lures
55
+ twitter-|social-lure|Twitter/X tool lures
56
+
57
+ # NEW categories discovered Feb 2026 (Antiy CERT, Snyk ToxicSkills)
58
+ # Browser automation agent lures
59
+ browser-automat|browser-lure|Browser automation agent lures
60
+ web-scrape|browser-lure|Web scraping tool lures
61
+
62
+ # Coding agent lures
63
+ coding-agent|coding-lure|Coding assistant lures
64
+ code-review|coding-lure|Code review tool lures
65
+
66
+ # PDF tool lures
67
+ pdf-convert|pdf-lure|PDF conversion tool lures
68
+ pdf-extract|pdf-lure|PDF extraction tool lures
69
+
70
+ # Fake security scanning skills (ironic camouflage)
71
+ security-scan|security-lure|Fake security scanners that are themselves malicious
72
+ virus-scan|security-lure|Fake antivirus/scanning tools
73
+
74
+ # WhatsApp integration lures
75
+ whatsapp-|messaging-lure|WhatsApp integration lures
76
+ telegram-bot|messaging-lure|Telegram bot lures
77
+
78
+ # Fake installer lures (Huntress, Mar 2026 - Bing AI search poisoning)
79
+ openclaw-install|fake-installer|Fake OpenClaw installer (GhostSocks/Vidar)
80
+ openclaw-setup|fake-installer|Fake setup scripts
81
+ openclaw-windows|fake-installer|Windows-specific fake installer
82
+ openclaw-mac|fake-installer|macOS-specific fake installer
83
+
84
+ # Voice-call/telephony lures (post CVE-2026-28446 wave)
85
+ voice-call|voice-lure|Voice/telephony integration lures
86
+ voice-agent|voice-lure|Voice agent lures
87
+ phone-|voice-lure|Phone integration lures
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "whitzard-claw",
3
+ "version": "1.0.0",
4
+ "description": "Whitzard-Claw Security Assistant",
5
+ "type": "module",
6
+ "bin": {
7
+ "whitzard-tui": "bin/whitzard-tui.js",
8
+ "whitzard-webui": "bin/whitzard-webui.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "scripts",
13
+ "ioc",
14
+ "webui/index.html",
15
+ "bin"
16
+ ],
17
+ "scripts": {
18
+ "build:tui": "cd tui && NODE_ENV=production npx esbuild tui.tsx --bundle --platform=node --target=node18 --format=esm --outfile=../dist/tui/tui.js --external:react-devtools-core --define:self=global --banner:js=\"import { createRequire } from 'module'; const require = createRequire(import.meta.url);\"",
19
+ "build:webui": "mkdir -p dist/webui && cp webui/server.js dist/webui/ && cp webui/index.html dist/webui/",
20
+ "build": "npm run build:tui && npm run build:webui",
21
+ "prepublishOnly": "npm run build",
22
+ "test": "echo \"No tests specified\" && exit 0"
23
+ },
24
+ "keywords": [
25
+ "security",
26
+ "scanner",
27
+ "openclaw",
28
+ "cli",
29
+ "tui",
30
+ "webui"
31
+ ],
32
+ "author": "",
33
+ "license": "MIT",
34
+ "engines": {
35
+ "node": ">=18.0.0"
36
+ },
37
+ "dependencies": {
38
+ "express": "^4.18.2",
39
+ "ink": "^6.8.0",
40
+ "ink-spinner": "^5.0.0",
41
+ "ink-text-input": "^6.0.0",
42
+ "react": "^19.2.4"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^25.5.0",
46
+ "@types/react": "^19.2.14",
47
+ "esbuild": "^0.21.0",
48
+ "typescript": "^5.9.3"
49
+ }
50
+ }
@@ -0,0 +1,183 @@
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
+ get_oc_config() {
13
+ # Usage: get_oc_config <key> <default>
14
+ local key="$1"
15
+ local default_val="${2:-}"
16
+ if [ "$OPENCLAW_PRESENT" = true ]; then
17
+ run_with_timeout 10 openclaw config get "$key" 2>/dev/null || echo "$default_val"
18
+ else
19
+ echo "$default_val"
20
+ fi
21
+ }
22
+
23
+ normalize_config_val() {
24
+ local val="${1:-}"
25
+ case "$val" in
26
+ null|undefined|"")
27
+ echo ""
28
+ ;;
29
+ *)
30
+ echo "$val"
31
+ ;;
32
+ esac
33
+ }
34
+
35
+ # ============================================================
36
+ # CHECK 1 (origin 17 / covers AC-001 partially + AC-003 partially)
37
+ # DM Policy Audit
38
+ # ============================================================
39
+ header 1 "Auditing DM access policies..."
40
+
41
+ DM_ISSUES=0
42
+
43
+ if [ "$OPENCLAW_PRESENT" = true ]; then
44
+ for channel in whatsapp telegram discord slack signal; do
45
+ DM_POLICY="$(normalize_config_val "$(get_oc_config "channels.${channel}.dmPolicy" "")")"
46
+ if [ "$DM_POLICY" = "open" ]; then
47
+ result_warn "Channel '$channel' has dmPolicy='open' (anyone can message)"
48
+ DM_ISSUES=$((DM_ISSUES + 1))
49
+ fi
50
+
51
+ # Keep existing wildcard check strategy as requested
52
+ ALLOW_FROM="$(normalize_config_val "$(get_oc_config "channels.${channel}.allowFrom" "")")"
53
+ if echo "$ALLOW_FROM" | grep -qE '"\*"|\[[^]]*\*[^]]*\]|\*' 2>/dev/null; then
54
+ result_warn "Channel '$channel' has wildcard allowFrom"
55
+ DM_ISSUES=$((DM_ISSUES + 1))
56
+ fi
57
+ done
58
+ else
59
+ log " openclaw command not found"
60
+ fi
61
+
62
+ if [ "$DM_ISSUES" -eq 0 ]; then
63
+ result_clean "DM policies acceptable"
64
+ fi
65
+
66
+ # ============================================================
67
+ # CHECK 2 (new / AC-002): Open group policy
68
+ # ============================================================
69
+ header 2 "Checking group access policies..."
70
+
71
+ GROUP_POLICY_ISSUES=0
72
+
73
+ if [ "$OPENCLAW_PRESENT" = true ]; then
74
+ for channel in whatsapp telegram discord slack signal; do
75
+ GROUP_POLICY="$(normalize_config_val "$(get_oc_config "channels.${channel}.groupPolicy" "")")"
76
+ if [ "$GROUP_POLICY" = "open" ]; then
77
+ result_warn "Channel '$channel' has groupPolicy='open' (group members can interact without restrictions)"
78
+ GROUP_POLICY_ISSUES=$((GROUP_POLICY_ISSUES + 1))
79
+ fi
80
+ done
81
+ else
82
+ log " openclaw command not found"
83
+ fi
84
+
85
+ if [ "$GROUP_POLICY_ISSUES" -eq 0 ]; then
86
+ result_clean "Group policies acceptable"
87
+ fi
88
+
89
+ # ============================================================
90
+ # CHECK 3 (new / AC-003): Wildcard allowlist
91
+ # ============================================================
92
+ header 3 "Checking channel allowlists for wildcard entries..."
93
+
94
+ ALLOWLIST_ISSUES=0
95
+
96
+ if [ "$OPENCLAW_PRESENT" = true ]; then
97
+ for channel in whatsapp telegram discord slack signal; do
98
+ ALLOWLIST_VAL="$(normalize_config_val "$(get_oc_config "channels.${channel}.allowlist" "")")"
99
+ if echo "$ALLOWLIST_VAL" | grep -qE '"\*"|\[[^]]*\*[^]]*\]|\*' 2>/dev/null; then
100
+ result_warn "Channel '$channel' has wildcard in allowlist"
101
+ ALLOWLIST_ISSUES=$((ALLOWLIST_ISSUES + 1))
102
+ fi
103
+ done
104
+ else
105
+ log " openclaw command not found"
106
+ fi
107
+
108
+ if [ "$ALLOWLIST_ISSUES" -eq 0 ]; then
109
+ result_clean "Channel allowlists acceptable"
110
+ fi
111
+
112
+ # ============================================================
113
+ # CHECK 4 (new / AC-004): No pairing and no allowlist
114
+ # ============================================================
115
+ header 4 "Checking channels without pairing or allowlist..."
116
+
117
+ PAIRING_ALLOWLIST_ISSUES=0
118
+
119
+ if [ "$OPENCLAW_PRESENT" = true ]; then
120
+ for channel in whatsapp telegram discord slack signal; do
121
+ DM_POLICY="$(normalize_config_val "$(get_oc_config "channels.${channel}.dmPolicy" "")")"
122
+ ALLOWLIST_VAL="$(normalize_config_val "$(get_oc_config "channels.${channel}.allowlist" "")")"
123
+ ALLOW_FROM_VAL="$(normalize_config_val "$(get_oc_config "channels.${channel}.allowFrom" "")")"
124
+ GROUP_POLICY="$(normalize_config_val "$(get_oc_config "channels.${channel}.groupPolicy" "")")"
125
+
126
+ HAS_ALLOWLIST=false
127
+ if [ -n "$ALLOWLIST_VAL" ] && [ "$ALLOWLIST_VAL" != "[]" ]; then
128
+ HAS_ALLOWLIST=true
129
+ elif [ -n "$ALLOW_FROM_VAL" ] && [ "$ALLOW_FROM_VAL" != "[]" ]; then
130
+ HAS_ALLOWLIST=true
131
+ fi
132
+
133
+ CHANNEL_CONFIGURED=false
134
+ if [ -n "$DM_POLICY" ] || [ -n "$GROUP_POLICY" ] || [ -n "$ALLOWLIST_VAL" ] || [ -n "$ALLOW_FROM_VAL" ]; then
135
+ CHANNEL_CONFIGURED=true
136
+ fi
137
+
138
+ if [ "$CHANNEL_CONFIGURED" = true ] && [ "$DM_POLICY" != "pairing" ] && [ "$HAS_ALLOWLIST" = false ]; then
139
+ result_warn "Channel '$channel' has no pairing and no allowlist (dmPolicy='${DM_POLICY:-undefined}')"
140
+ PAIRING_ALLOWLIST_ISSUES=$((PAIRING_ALLOWLIST_ISSUES + 1))
141
+ fi
142
+ done
143
+ else
144
+ log " openclaw command not found"
145
+ fi
146
+
147
+ if [ "$PAIRING_ALLOWLIST_ISSUES" -eq 0 ]; then
148
+ result_clean "Pairing/allowlist protections acceptable"
149
+ fi
150
+
151
+ # ============================================================
152
+ # CHECK 5 (new / AC-005): Session DM scope isolation
153
+ # ============================================================
154
+ header 5 "Checking session DM scope isolation..."
155
+
156
+ SESSION_SCOPE_ISSUES=0
157
+
158
+ if [ "$OPENCLAW_PRESENT" = true ]; then
159
+ SESSION_DM_SCOPE="$(normalize_config_val "$(get_oc_config "session.dmScope" "")")"
160
+
161
+ CHANNEL_COUNT=0
162
+ for channel in whatsapp telegram discord slack signal; do
163
+ DM_POLICY="$(normalize_config_val "$(get_oc_config "channels.${channel}.dmPolicy" "")")"
164
+ GROUP_POLICY="$(normalize_config_val "$(get_oc_config "channels.${channel}.groupPolicy" "")")"
165
+ ALLOWLIST_VAL="$(normalize_config_val "$(get_oc_config "channels.${channel}.allowlist" "")")"
166
+ ALLOW_FROM_VAL="$(normalize_config_val "$(get_oc_config "channels.${channel}.allowFrom" "")")"
167
+
168
+ if [ -n "$DM_POLICY" ] || [ -n "$GROUP_POLICY" ] || [ -n "$ALLOWLIST_VAL" ] || [ -n "$ALLOW_FROM_VAL" ]; then
169
+ CHANNEL_COUNT=$((CHANNEL_COUNT + 1))
170
+ fi
171
+ done
172
+
173
+ if [ "$CHANNEL_COUNT" -gt 1 ] 2>/dev/null && [ "$SESSION_DM_SCOPE" != "per-channel-peer" ]; then
174
+ result_warn "session.dmScope is '${SESSION_DM_SCOPE:-undefined}' with ${CHANNEL_COUNT} configured channels; DM context may not be isolated per user"
175
+ SESSION_SCOPE_ISSUES=$((SESSION_SCOPE_ISSUES + 1))
176
+ fi
177
+ else
178
+ log " openclaw command not found"
179
+ fi
180
+
181
+ if [ "$SESSION_SCOPE_ISSUES" -eq 0 ]; then
182
+ result_clean "Session DM scope isolation acceptable"
183
+ fi
@@ -0,0 +1,222 @@
1
+ # shellcheck shell=bash
2
+
3
+ # ------------------------------------------------------------
4
+ # Shared helpers
5
+ # ------------------------------------------------------------
6
+
7
+ get_perm() {
8
+ local target="$1"
9
+
10
+ [ -e "$target" ] || {
11
+ echo "unknown"
12
+ return
13
+ }
14
+
15
+ case "$(uname -s)" in
16
+ Darwin|FreeBSD|OpenBSD|NetBSD)
17
+ stat -f '%Lp' "$target" 2>/dev/null || echo "unknown"
18
+ ;;
19
+ Linux)
20
+ stat -c '%a' "$target" 2>/dev/null || echo "unknown"
21
+ ;;
22
+ *)
23
+ stat -c '%a' "$target" 2>/dev/null || stat -f '%Lp' "$target" 2>/dev/null || echo "unknown"
24
+ ;;
25
+ esac
26
+ }
27
+
28
+ has_api_key_patterns() {
29
+ # Return 0 if file contains suspicious API key patterns
30
+ local target="$1"
31
+ [ -f "$target" ] || return 1
32
+
33
+ if grep -E -q \
34
+ 'sk-ant-[A-Za-z0-9_-]{20,}|sk-proj-[A-Za-z0-9_-]{20,}|sk-[A-Za-z0-9_-]{20,}|xoxb-[A-Za-z0-9_-]{20,}|xoxp-[A-Za-z0-9_-]{20,}' \
35
+ "$target" 2>/dev/null; then
36
+ return 0
37
+ fi
38
+ return 1
39
+ }
40
+
41
+ # ============================================================
42
+ # CHECK 1 (origin 21 + CRED-001/002/004/005): Session & Credential Permissions
43
+ # ============================================================
44
+ header 1 "Auditing session and credential file permissions..."
45
+
46
+ CRED_ISSUES=0
47
+
48
+ # ---- Check OpenClaw home directory itself
49
+ if [ -d "$OPENCLAW_DIR" ]; then
50
+ HOME_PERMS="$(get_perm "$OPENCLAW_DIR")"
51
+ if [ "$HOME_PERMS" != "700" ] && [ "$HOME_PERMS" != "unknown" ]; then
52
+ result_warn "OpenClaw home dir has permissions $HOME_PERMS (recommended: 700)"
53
+ CRED_ISSUES=$((CRED_ISSUES + 1))
54
+ fi
55
+ else
56
+ result_clean "OpenClaw home directory not found; credential permission check skipped"
57
+ fi
58
+
59
+ # ---- Check main config file
60
+ CONFIG_FILE="$OPENCLAW_DIR/openclaw.json"
61
+ if [ -f "$CONFIG_FILE" ]; then
62
+ CONFIG_PERMS="$(get_perm "$CONFIG_FILE")"
63
+ if [ "$CONFIG_PERMS" != "600" ] && [ "$CONFIG_PERMS" != "unknown" ]; then
64
+ result_warn "openclaw.json has permissions $CONFIG_PERMS (recommended: 600)"
65
+ CRED_ISSUES=$((CRED_ISSUES + 1))
66
+ fi
67
+ fi
68
+
69
+ # ---- Check credentials directory
70
+ CRED_DIR="$OPENCLAW_DIR/credentials"
71
+ if [ -d "$CRED_DIR" ]; then
72
+ DIR_PERMS="$(get_perm "$CRED_DIR")"
73
+ if [ "$DIR_PERMS" != "700" ] && [ "$DIR_PERMS" != "unknown" ]; then
74
+ result_warn "Credentials dir has permissions $DIR_PERMS (recommended: 700)"
75
+ CRED_ISSUES=$((CRED_ISSUES + 1))
76
+ fi
77
+
78
+ while IFS= read -r cred_file; do
79
+ [ -z "$cred_file" ] && continue
80
+ FPERMS="$(get_perm "$cred_file")"
81
+ if [ "$FPERMS" != "600" ] && [ "$FPERMS" != "unknown" ]; then
82
+ result_warn "$(basename "$cred_file") has permissions $FPERMS (recommended: 600)"
83
+ CRED_ISSUES=$((CRED_ISSUES + 1))
84
+ fi
85
+ done < <(find "$CRED_DIR" -type f -name "*.json" 2>/dev/null)
86
+ fi
87
+
88
+ # ---- Check session directories under agents/*
89
+ AGENTS_DIR="$OPENCLAW_DIR/agents"
90
+ if [ -d "$AGENTS_DIR" ]; then
91
+ while IFS= read -r agent_dir; do
92
+ [ -z "$agent_dir" ] && continue
93
+
94
+ # ---- sessions/
95
+ SESSION_DIR="$agent_dir/sessions"
96
+ if [ -d "$SESSION_DIR" ]; then
97
+ SDIR_PERMS="$(get_perm "$SESSION_DIR")"
98
+ if [ "$SDIR_PERMS" != "700" ] && [ "$SDIR_PERMS" != "unknown" ]; then
99
+ result_warn "Session dir $(basename "$agent_dir")/sessions has permissions $SDIR_PERMS (recommended: 700)"
100
+ CRED_ISSUES=$((CRED_ISSUES + 1))
101
+ fi
102
+
103
+ while IFS= read -r session_file; do
104
+ [ -z "$session_file" ] && continue
105
+ SFPERMS="$(get_perm "$session_file")"
106
+ if [ "$SFPERMS" != "600" ] && [ "$SFPERMS" != "unknown" ]; then
107
+ result_warn "Session file $(basename "$agent_dir")/sessions/$(basename "$session_file") has permissions $SFPERMS (recommended: 600)"
108
+ CRED_ISSUES=$((CRED_ISSUES + 1))
109
+ fi
110
+ done < <(find "$SESSION_DIR" -type f 2>/dev/null)
111
+ fi
112
+
113
+ # ---- agent/auth-profiles.json
114
+ AUTH_PROFILE_FILE="$agent_dir/agent/auth-profiles.json"
115
+ if [ -f "$AUTH_PROFILE_FILE" ]; then
116
+ APERMS="$(get_perm "$AUTH_PROFILE_FILE")"
117
+ if [ "$APERMS" != "600" ] && [ "$APERMS" != "unknown" ]; then
118
+ result_warn "Auth profiles for agent '$(basename "$agent_dir")' have permissions $APERMS (recommended: 600)"
119
+ CRED_ISSUES=$((CRED_ISSUES + 1))
120
+ fi
121
+ fi
122
+ done < <(find "$AGENTS_DIR" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
123
+ fi
124
+
125
+ if [ "$CRED_ISSUES" -eq 0 ] && [ -d "$OPENCLAW_DIR" ]; then
126
+ result_clean "Session and credential permissions correct"
127
+ fi
128
+
129
+ # ============================================================
130
+ # CHECK 2 (new / CRED-007): API keys in memory files
131
+ # ============================================================
132
+ header 2 "Scanning memory files for leaked API keys... "
133
+
134
+ MEMKEY_ISSUES=0
135
+
136
+ # if [ -d "$AGENTS_DIR" ]; then
137
+ # while IFS= read -r agent_dir; do
138
+ # [ -z "$agent_dir" ] && continue
139
+
140
+ # for mem_name in soul.md MEMORY.md SOUL.md; do
141
+ # MEM_FILE="$agent_dir/$mem_name"
142
+ # if [ -f "$MEM_FILE" ]; then
143
+ # if has_api_key_patterns "$MEM_FILE"; then
144
+ # result_critical "API keys found in memory file '$mem_name' for agent '$(basename "$agent_dir")'"
145
+ # log " Evidence: $MEM_FILE contains API key patterns"
146
+ # MEMKEY_ISSUES=$((MEMKEY_ISSUES + 1))
147
+ # fi
148
+ # fi
149
+ # done
150
+ # done < <(find "$AGENTS_DIR" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
151
+ # fi
152
+
153
+ list_memory_files() {
154
+ # Enumerate workspace-level and agent-level memory files
155
+ local f
156
+ for f in \
157
+ "$WORKSPACE_DIR/SOUL.md" \
158
+ "$WORKSPACE_DIR/soul.md" \
159
+ "$WORKSPACE_DIR/MEMORY.md"
160
+ do
161
+ [ -f "$f" ] && echo "$f"
162
+ done
163
+
164
+ if [ -d "$OPENCLAW_DIR/agents" ]; then
165
+ find "$OPENCLAW_DIR/agents" -mindepth 2 -maxdepth 2 -type f \
166
+ \( -name 'soul.md' -o -name 'SOUL.md' -o -name 'MEMORY.md' -o -name 'soul.md' \) \
167
+ 2>/dev/null || true
168
+ fi
169
+ }
170
+
171
+ # 在 workspace 根目录下
172
+ MEM_SEARCH_DIR="$OPENCLAW_DIR/workspace"
173
+
174
+ while IFS= read -r MEM_FILE; do
175
+ [ -z "$MEM_FILE" ] && continue
176
+ [ -f "$MEM_FILE" ] || continue
177
+
178
+ if has_api_key_patterns "$MEM_FILE"; then
179
+ result_critical "API keys found in memory file '$(basename "$MEM_FILE")'"
180
+ log " Evidence: $MEM_FILE"
181
+ MEMKEY_ISSUES=$((MEMKEY_ISSUES + 1))
182
+ fi
183
+ done <<EOF
184
+ $(list_memory_files)
185
+ EOF
186
+
187
+ if [ "$MEMKEY_ISSUES" -eq 0 ]; then
188
+ result_clean "No API keys found in memory files"
189
+ fi
190
+
191
+ # ============================================================
192
+ # CHECK 3 (new / CRED-008): Scan state dir .md/.json files for API keys
193
+ # ============================================================
194
+ header 3 "Scanning state files for leaked API keys... "
195
+
196
+ STATEKEY_ISSUES=0
197
+
198
+ if [ -d "$OPENCLAW_DIR" ]; then
199
+ while IFS= read -r scan_file; do
200
+ [ -z "$scan_file" ] && continue
201
+
202
+ case "$scan_file" in
203
+ *.env)
204
+ continue
205
+ ;;
206
+ */soul.md|*/MEMORY.md|*/SOUL.md)
207
+ # Already covered by CHECK 2
208
+ continue
209
+ ;;
210
+ esac
211
+
212
+ if has_api_key_patterns "$scan_file"; then
213
+ result_warn "API key pattern found in configuration/state file '$(basename "$scan_file")'"
214
+ log " Evidence: $scan_file"
215
+ STATEKEY_ISSUES=$((STATEKEY_ISSUES + 1))
216
+ fi
217
+ done < <(find "$OPENCLAW_DIR" \( -name "*.md" -o -name "*.json" \) -type f 2>/dev/null)
218
+ fi
219
+
220
+ if [ "$STATEKEY_ISSUES" -eq 0 ]; then
221
+ result_clean "No API keys found in scanned state files"
222
+ fi