clawarmor 1.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pinzas AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,163 @@
1
+ <div align="center">
2
+
3
+ # 🛡 ClawArmor
4
+
5
+ **The security auditor for OpenClaw agents.**
6
+
7
+ Checks your config. Probes your live gateway. Scans your skills.
8
+ Runs in 30 seconds. Finds what config-only tools miss. Free forever.
9
+
10
+ [![npm version](https://img.shields.io/npm/v/clawarmor?color=3fb950&label=npm&style=flat-square)](https://www.npmjs.com/package/clawarmor)
11
+ [![license](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](LICENSE)
12
+ [![node](https://img.shields.io/badge/node-%3E%3D18-green?style=flat-square)](package.json)
13
+
14
+ ```bash
15
+ npm install -g clawarmor && clawarmor audit
16
+ ```
17
+
18
+ </div>
19
+
20
+ ---
21
+
22
+ ```
23
+ ℹ Reads: ~/.openclaw/openclaw.json + file permissions only
24
+ Network: registry.npmjs.org (version check) + 127.0.0.1:18789 (live probes)
25
+ Sends nothing. Source: github.com/pinzasai/clawarmor
26
+
27
+ ── LIVE GATEWAY PROBES (connecting to 127.0.0.1) ──
28
+ ✓ Gateway running on port 18789
29
+ ✓ Not reachable on network interfaces (probed live)
30
+ ✓ Authentication required (WebSocket probe confirmed)
31
+ ✓ /health endpoint does not leak sensitive data
32
+ ✓ CORS not open to arbitrary origins
33
+
34
+ Security Score: 100/100 ┃ Grade: A
35
+ ████████████████████ 100%
36
+
37
+ Verdict: Your instance is secure. No issues found.
38
+
39
+ ── PASSED (30 checks) ──────────────────────────────
40
+ ✓ Gateway bound to loopback only
41
+ ✓ Auth token is strong
42
+ ✓ Agent sandbox mode: "non-main" (sessions isolated)
43
+ ✓ Browser SSRF to private networks blocked
44
+ ✓ All channel allowFrom settings are restricted
45
+ ... 25 more
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Why ClawArmor
51
+
52
+ Every other OpenClaw security tool reads your config file and tells you if things look right on paper.
53
+
54
+ **ClawArmor also connects locally to your running gateway and verifies live behavior.**
55
+
56
+ Config says `bind: loopback`. Is your gateway *actually* unreachable on LAN? Config says auth is enabled. Does the live WebSocket endpoint *actually* reject unauthenticated connections? A misconfigured nginx in front can make your config lie. Live probes can't be faked.
57
+
58
+ > All probes connect from your machine to `127.0.0.1` (and your local network interfaces). Nothing leaves your machine.
59
+
60
+ ---
61
+
62
+ ## Five commands
63
+
64
+ ```bash
65
+ clawarmor audit # 30 checks + 5 live gateway probes. Score 0-100. Plain-English verdict.
66
+ clawarmor scan # Scan every skill file (.js .sh .py .ts SKILL.md) for malicious code.
67
+ clawarmor fix # Auto-apply safe fixes. --dry-run to preview, --apply to execute.
68
+ clawarmor verify # Re-run only previously-failed checks. Exit 0 if all fixed (CI-friendly).
69
+ clawarmor trend # ASCII chart of your security score over time.
70
+ ```
71
+
72
+ ---
73
+
74
+ ## What it checks
75
+
76
+ ### Live gateway probes (behavioral — not just config reads)
77
+
78
+ | Probe | What it checks |
79
+ |---|---|
80
+ | Port reachability | TCP-connects to gateway on every non-loopback interface |
81
+ | Auth enforcement | WebSocket handshake without token — does server reject it? |
82
+ | Health endpoint | GET /health — does response contain config data or secrets? |
83
+ | CORS headers | OPTIONS with `Origin: https://evil.example.com` |
84
+
85
+ These probes are **read-only and non-destructive**. They observe — they don't modify anything.
86
+
87
+ ### Config audit (30 checks)
88
+
89
+ Gateway bind · auth mode · token strength · dangerous flags · mDNS exposure · real-IP fallback · trusted proxy config · file permissions (`~/.openclaw/`, `openclaw.json`, `agent-accounts.json`, `credentials/`) · channel allowFrom policies · wildcard detection · group policies · elevated tools · exec sandbox · tool restrictions (filesystem scope, apply_patch scope) · browser SSRF policy · plugin allowlist · log redaction · version currency · webhook security · multi-user trust model
90
+
91
+ ### Skill supply chain scan
92
+
93
+ Scans **all files** in every installed skill — `.js`, `.ts`, `.sh`, `.py`, `.rb` and `SKILL.md`. Not just markdown.
94
+
95
+ **Code patterns:** `eval()`, `new Function()`, `child_process`, credential file reads, pipe-to-shell, known exfil domains, large base64 blobs, dynamic `require()`
96
+
97
+ **SKILL.md instruction patterns:** credential read instructions, system prompt overrides, exfiltration instructions, deception instructions, hardcoded IP fetches
98
+
99
+ > **Honest limitation:** The scanner catches unsophisticated threats and common patterns. Obfuscated code (string concatenation, encoded payloads) can bypass static analysis. Treat a clean scan as a good signal, not a guarantee.
100
+
101
+ ---
102
+
103
+ ## What it protects against
104
+
105
+ | Threat | Covered | Notes |
106
+ |---|---|---|
107
+ | T-ACCESS-003: Token/config exposure | ✅ | File permission checks + config hardening |
108
+ | T-PERSIST-001: Malicious skill supply chain | ✅ | All skill files scanned, not just SKILL.md |
109
+ | T-EXEC-001/002: Prompt injection | ❌ | Runtime policy layer — use [SupraWall](https://suprawall.io) |
110
+ | T-EXFIL-001: Data exfiltration | ❌ | Runtime policy layer — use SupraWall |
111
+
112
+ ClawArmor hardens your configuration and detects supply chain threats. It does not provide runtime policy enforcement — that's a different layer.
113
+
114
+ ---
115
+
116
+ ## Auto-fix
117
+
118
+ ```bash
119
+ clawarmor fix --dry-run # preview what would change
120
+ clawarmor fix --apply # apply safe one-liner fixes + gateway restart instructions
121
+ ```
122
+
123
+ Sandbox isolation is enabled safely: if Docker is installed, `fix --apply` sets `sandbox.mode=non-main` + `workspaceAccess=rw` so your Telegram/group sessions keep workspace access.
124
+
125
+ ---
126
+
127
+ ## CI integration
128
+
129
+ ```bash
130
+ # Fail CI if security score drops
131
+ clawarmor verify # exit 0 = all previously-failed checks now pass
132
+ # exit 1 = still failing
133
+ ```
134
+
135
+ Score history persists in `~/.clawarmor/history.json`.
136
+
137
+ ---
138
+
139
+ ## Privacy & security
140
+
141
+ - `audit`, `scan`, `fix`, `verify`, `trend` run **entirely locally**
142
+ - One optional network call: `registry.npmjs.org` for version check (skippable with `--offline`)
143
+ - Every run prints exactly what files it reads and what network calls it makes before executing
144
+ - Nothing is sent anywhere
145
+
146
+ **Found a vulnerability in ClawArmor itself?** Please email `pinzasrojas@proton.me` before public disclosure.
147
+
148
+ ---
149
+
150
+ ## Installation
151
+
152
+ ```bash
153
+ npm install -g clawarmor # requires Node.js 18+
154
+ clawarmor audit
155
+ ```
156
+
157
+ Zero runtime npm dependencies. Node.js built-ins only (`net`, `http`, `os`, `fs`, `crypto`).
158
+
159
+ ---
160
+
161
+ ## License
162
+
163
+ MIT — see [LICENSE](LICENSE)
package/SECURITY.md ADDED
@@ -0,0 +1,40 @@
1
+ # Security Policy & Ethics Statement
2
+
3
+ ## What ClawArmor scans
4
+
5
+ **`clawarmor audit`** reads a single file: `~/.openclaw/openclaw.json`. It checks configuration values — bind address, auth mode, file permissions, channel policies. It never reads credentials or sends any data over the network.
6
+
7
+ **`clawarmor scan`** reads source files in installed skill directories. It runs regex patterns against file content to detect suspicious code. It does not execute any skill code. It never sends file contents anywhere.
8
+
9
+ **`clawarmor monitor`** is a planned future command. Details TBD.
10
+
11
+ ## What ClawArmor never does
12
+
13
+ - Sends your config, credentials, or file contents to any server
14
+ - Stores anything on disk beyond what you explicitly configure
15
+ - Executes skill code during scanning
16
+ - Makes outbound network calls during `audit` or `scan` (except: `clawarmor audit` fetches the latest OpenClaw version from the npm registry to check if you're up to date — this is a GET request with no identifying information)
17
+ - Shares scan results with third parties
18
+
19
+ ## Responsible disclosure
20
+
21
+ ClawArmor is built on research into exposed OpenClaw instances. We follow responsible disclosure principles:
22
+
23
+ 1. We notify owners before publishing any findings about their specific instances
24
+ 2. We do not exploit vulnerabilities we discover — we report them
25
+ 3. We do not store or log the content of notifications sent to exposed instances
26
+ 4. We maintain a public transparency log of notifications sent (IP + timestamp + message hash, no content)
27
+
28
+ ## Reporting vulnerabilities in ClawArmor
29
+
30
+ If you find a security vulnerability in ClawArmor itself, please email **pinzasrojas@proton.me** before public disclosure. We commit to:
31
+
32
+ - Acknowledging your report within 48 hours
33
+ - Providing a fix or mitigation within 14 days for critical issues
34
+ - Crediting you in the release notes (if desired)
35
+
36
+ ## False positives
37
+
38
+ ClawArmor's `scan` command uses static pattern matching. Some findings — particularly in built-in skills — are legitimate uses of flagged patterns (e.g., `spawnSync` in a TTS binary wrapper). Always review findings in context before taking action.
39
+
40
+ If you believe a pattern produces excessive false positives, please open an issue at github.com/pinzasai/clawarmor.
package/SKILL.md ADDED
@@ -0,0 +1,87 @@
1
+ # ClawArmor
2
+
3
+ Security armor for your OpenClaw agent. Audits your config, probes your live gateway, and scans installed skills for threats.
4
+
5
+ ## When to use this skill
6
+
7
+ Call `clawarmor` when you want to:
8
+ - Check if your OpenClaw instance is securely configured
9
+ - Verify your live gateway actually requires authentication (not just config)
10
+ - Scan installed skills for malicious code or dangerous instructions
11
+ - Auto-fix common misconfigurations
12
+ - Track your security score over time
13
+
14
+ ## Commands
15
+
16
+ ### Audit (most important — run this first)
17
+ ```
18
+ Run: clawarmor audit
19
+ ```
20
+ Performs 32 security checks including live behavioral probes of the running gateway. Returns a 0-100 score, letter grade, plain-English verdict, and specific findings with fix commands.
21
+
22
+ ### Scan installed skills
23
+ ```
24
+ Run: clawarmor scan
25
+ ```
26
+ Scans all installed skill files (.js, .sh, .py, .ts, SKILL.md) for malicious patterns: credential theft, RCE, exfiltration, prompt injection.
27
+
28
+ ### Auto-fix safe issues
29
+ ```
30
+ Run: clawarmor fix --dry-run # preview what would change
31
+ Run: clawarmor fix --apply # apply safe one-liner fixes
32
+ ```
33
+
34
+ ### Verify fixes worked
35
+ ```
36
+ Run: clawarmor verify
37
+ ```
38
+ Re-runs only previously-failed checks. Exits 0 if all now pass.
39
+
40
+ ### Track score over time
41
+ ```
42
+ Run: clawarmor trend
43
+ ```
44
+ Shows ASCII chart of score history across all previous audits.
45
+
46
+ ## What it checks
47
+
48
+ **Live gateway probes** (behavioral — not just config reads):
49
+ - Is the gateway port actually open on non-loopback network interfaces?
50
+ - Does the live WebSocket endpoint reject connections without an auth token?
51
+ - Does the /health endpoint leak sensitive config data?
52
+ - Are CORS headers misconfigured to allow arbitrary origins?
53
+
54
+ **Config audit** (32 static checks):
55
+ - Gateway bind address, auth mode, token strength
56
+ - File permissions on ~/.openclaw/, openclaw.json, agent-accounts.json, credentials/
57
+ - Channel allowFrom policies — allowlist enforcement, wildcard detection
58
+ - Tool restrictions — filesystem scope, apply_patch scope, elevated tools
59
+ - Sandbox isolation configuration
60
+ - Plugin allowlist enforcement
61
+ - OpenClaw version currency
62
+ - Browser SSRF policy
63
+ - Webhook security
64
+ - mDNS exposure mode
65
+ - Multi-user trust model vs sandbox isolation
66
+
67
+ **Skill supply chain scan**:
68
+ - Malicious code patterns in .js, .sh, .py, .ts skill scripts
69
+ - Dangerous natural language instructions in SKILL.md files
70
+ - Context-aware severity: built-in skills capped at INFO, user-installed get full severity
71
+
72
+ ## Installation
73
+
74
+ ```
75
+ npm install -g clawarmor
76
+ ```
77
+
78
+ Zero runtime dependencies. Node.js 18+ required. Local only — nothing sent to external servers except one version check to registry.npmjs.org.
79
+
80
+ Source and full documentation: https://github.com/pinzasai/clawarmor
81
+
82
+ ## Notes
83
+
84
+ - Run `clawarmor audit` after any config change to verify your security posture
85
+ - The `verify` command is CI-friendly (exit codes: 0 = all fixed, 1 = still failing)
86
+ - Score history is stored at `~/.clawarmor/history.json`
87
+ - This skill does NOT replace runtime policy enforcement (see SupraWall for that)
package/cli.js ADDED
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+ // ClawArmor v1.1.0 — Security armor for OpenClaw agents
3
+
4
+ import { paint } from './lib/output/colors.js';
5
+
6
+ const VERSION = '1.1.0';
7
+ const GATEWAY_PORT_DEFAULT = 18789;
8
+
9
+ function isLocalhost(host) {
10
+ return host === '127.0.0.1' || host === 'localhost' || host === '::1';
11
+ }
12
+
13
+ function parseUrlFlag(urlArg) {
14
+ // Accepts host:port or plain host (defaults to GATEWAY_PORT_DEFAULT)
15
+ if (!urlArg) return null;
16
+ const lastColon = urlArg.lastIndexOf(':');
17
+ if (lastColon !== -1 && lastColon !== 0) {
18
+ const maybePort = urlArg.slice(lastColon + 1);
19
+ if (/^\d+$/.test(maybePort)) {
20
+ return { host: urlArg.slice(0, lastColon), port: parseInt(maybePort, 10) };
21
+ }
22
+ }
23
+ return { host: urlArg, port: GATEWAY_PORT_DEFAULT };
24
+ }
25
+
26
+ function trustHeader(port, targetHost) {
27
+ const isRemote = targetHost && !isLocalhost(targetHost);
28
+ const probeTarget = targetHost ? `${targetHost}:${port}` : `127.0.0.1:${port}`;
29
+ console.log('');
30
+ console.log(` ${paint.dim('ℹ')} ${paint.dim('Config: local (~/.openclaw/openclaw.json)')}`);
31
+ console.log(` ${paint.dim('Probes: ' + probeTarget + (isRemote ? ' (remote)' : ' (local)'))}`);
32
+ console.log(` ${paint.dim('Sends nothing. Source: github.com/pinzasai/clawarmor')}`);
33
+ }
34
+
35
+ function usage() {
36
+ console.log('');
37
+ console.log(` ${paint.bold('ClawArmor')} ${paint.dim('v'+VERSION)} — Security armor for OpenClaw agents`);
38
+ console.log('');
39
+ console.log(` ${paint.cyan('Usage:')} clawarmor <command> [flags]`);
40
+ console.log('');
41
+ console.log(` ${paint.bold('Commands:')}`);
42
+ console.log(` ${paint.cyan('audit')} Score your OpenClaw config (0-100), zero false positives`);
43
+ console.log(` ${paint.cyan('scan')} Scan ALL skill files for malicious code + SKILL.md instructions`);
44
+ console.log(` ${paint.cyan('verify')} Re-check only previously-failed items`);
45
+ console.log(` ${paint.cyan('trend')} Show score over last N audits (ASCII chart)`);
46
+ console.log(` ${paint.cyan('compare')} Compare coverage vs openclaw security audit`);
47
+ console.log(` ${paint.cyan('fix')} Auto-apply safe fixes (--dry-run to preview, --apply to run)`);
48
+ console.log('');
49
+ console.log(` ${paint.dim('Flags:')}`);
50
+ console.log(` ${paint.dim('--url <host:port>')} Probe a specific host:port instead of 127.0.0.1`);
51
+ console.log(` ${paint.dim('--config <path>')} Use a specific config file instead of ~/.openclaw/openclaw.json`);
52
+ console.log(` ${paint.dim('--json')} Machine-readable JSON output (audit only)`);
53
+ console.log(` ${paint.dim('--explain-reads')} Print every file read and network call before executing`);
54
+ console.log('');
55
+ console.log(` ${paint.dim('Examples:')}`);
56
+ console.log(` ${paint.dim('clawarmor audit')} ${paint.dim('# local, default')}`);
57
+ console.log(` ${paint.dim('clawarmor audit --url 10.0.0.5:18789')} ${paint.dim('# probe LAN instance')}`);
58
+ console.log(` ${paint.dim('clawarmor audit --url myserver.com:18789')} ${paint.dim('# probe remote (auth warning)')}`);
59
+ console.log('');
60
+ console.log(` ${paint.dim('github.com/pinzasai/clawarmor')}`);
61
+ console.log('');
62
+ }
63
+
64
+ const args = process.argv.slice(2);
65
+ const cmd = args[0];
66
+
67
+ // Parse --url flag
68
+ const urlIdx = args.indexOf('--url');
69
+ const urlArg = urlIdx !== -1 ? args[urlIdx + 1] : null;
70
+ const parsedUrl = parseUrlFlag(urlArg);
71
+
72
+ // Parse --config flag
73
+ const configIdx = args.indexOf('--config');
74
+ const configPathArg = configIdx !== -1 ? args[configIdx + 1] : null;
75
+
76
+ const flags = {
77
+ json: args.includes('--json'),
78
+ explainReads: args.includes('--explain-reads'),
79
+ targetHost: parsedUrl?.host || null,
80
+ targetPort: parsedUrl?.port || null,
81
+ configPath: configPathArg || null,
82
+ };
83
+
84
+ if (!cmd || cmd === '--help' || cmd === '-h' || cmd === 'help') { usage(); process.exit(0); }
85
+ if (cmd === '--version' || cmd === '-v') { console.log(VERSION); process.exit(0); }
86
+
87
+ // Load config once for port info (used in trust header)
88
+ const { loadConfig } = await import('./lib/config.js');
89
+ const { config } = loadConfig(flags.configPath);
90
+ const gatewayPort = flags.targetPort || config?.gateway?.port || GATEWAY_PORT_DEFAULT;
91
+ const targetHost = flags.targetHost || null;
92
+
93
+ if (flags.explainReads) {
94
+ const probeTarget = targetHost ? `${targetHost}:${gatewayPort}` : `127.0.0.1:${gatewayPort}`;
95
+ console.log('');
96
+ console.log(` ${paint.cyan('--explain-reads')} — files and network calls this command will make:`);
97
+ console.log(` ${paint.dim('Read:')} ${flags.configPath || '~/.openclaw/openclaw.json'}`);
98
+ console.log(` ${paint.dim('Read:')} ~/.openclaw/agent-accounts.json (permissions only)`);
99
+ console.log(` ${paint.dim('Read:')} ~/.openclaw/ (directory permissions)`);
100
+ console.log(` ${paint.dim('Read:')} ~/.clawarmor/history.json (audit history)`);
101
+ if (['audit', 'verify'].includes(cmd)) {
102
+ console.log(` ${paint.dim('Network:')} ${probeTarget} (TCP/WebSocket/HTTP live probes — gateway only)`);
103
+ }
104
+ console.log(` ${paint.dim('Network:')} registry.npmjs.org (version check)`);
105
+ console.log('');
106
+ }
107
+
108
+ // Remote host warning (printed before trust header so it's prominent)
109
+ if (targetHost && !isLocalhost(targetHost)) {
110
+ console.log('');
111
+ console.log(` ${paint.yellow('⚠')} ${paint.bold('Probing remote host — ensure you have authorization')}`);
112
+ console.log(` ${paint.dim('Target: ' + targetHost + ':' + gatewayPort)}`);
113
+ }
114
+
115
+ // Print trust header before every command (except --json mode)
116
+ if (!flags.json) {
117
+ trustHeader(gatewayPort, targetHost);
118
+ }
119
+
120
+ if (cmd === 'audit') {
121
+ const { runAudit } = await import('./lib/audit.js');
122
+ process.exit(await runAudit(flags));
123
+ }
124
+
125
+ if (cmd === 'scan') {
126
+ const { runScan } = await import('./lib/scan.js');
127
+ process.exit(await runScan());
128
+ }
129
+
130
+ if (cmd === 'verify') {
131
+ const { runVerify } = await import('./lib/verify.js');
132
+ process.exit(await runVerify());
133
+ }
134
+
135
+ if (cmd === 'trend') {
136
+ const idx = args.indexOf('--last');
137
+ const n = (idx !== -1 && args[idx+1]) ? (parseInt(args[idx+1], 10) || 10) : 10;
138
+ const { runTrend } = await import('./lib/trend.js');
139
+ process.exit(await runTrend({ n }));
140
+ }
141
+
142
+ if (cmd === 'compare') {
143
+ const { runCompare } = await import('./lib/compare.js');
144
+ process.exit(await runCompare());
145
+ }
146
+
147
+
148
+ if (cmd === 'fix') {
149
+ const { runFix } = await import('./lib/fix.js');
150
+ const fixFlags = { apply: process.argv.includes('--apply'), dryRun: process.argv.includes('--dry-run') };
151
+ process.exit(await runFix(fixFlags));
152
+ }
153
+
154
+ console.log(` ${paint.red('✗')} Unknown command: ${paint.bold(cmd)}`);
155
+ usage(); process.exit(1);