@token-security/clawdit 0.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.
Files changed (94) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +197 -0
  3. package/dist/checks/auth-001-device-auth-disabled.d.ts +10 -0
  4. package/dist/checks/auth-001-device-auth-disabled.js +34 -0
  5. package/dist/checks/auth-002-insecure-fallback.d.ts +10 -0
  6. package/dist/checks/auth-002-insecure-fallback.js +34 -0
  7. package/dist/checks/auth-003-no-gateway-auth.d.ts +10 -0
  8. package/dist/checks/auth-003-no-gateway-auth.js +40 -0
  9. package/dist/checks/auth-004-public-trusted-proxies.d.ts +10 -0
  10. package/dist/checks/auth-004-public-trusted-proxies.js +37 -0
  11. package/dist/checks/auth-005-hooks-no-token.d.ts +10 -0
  12. package/dist/checks/auth-005-hooks-no-token.js +42 -0
  13. package/dist/checks/auth-006-pairing-exposed.d.ts +10 -0
  14. package/dist/checks/auth-006-pairing-exposed.js +46 -0
  15. package/dist/checks/auth-007-missing-trusted-proxies.d.ts +11 -0
  16. package/dist/checks/auth-007-missing-trusted-proxies.js +46 -0
  17. package/dist/checks/chan-001-open-dm.d.ts +10 -0
  18. package/dist/checks/chan-001-open-dm.js +48 -0
  19. package/dist/checks/chan-002-group-policy.d.ts +10 -0
  20. package/dist/checks/chan-002-group-policy.js +43 -0
  21. package/dist/checks/chan-003-no-mention.d.ts +10 -0
  22. package/dist/checks/chan-003-no-mention.js +45 -0
  23. package/dist/checks/chan-004-dm-isolation.d.ts +10 -0
  24. package/dist/checks/chan-004-dm-isolation.js +50 -0
  25. package/dist/checks/chan-005-verbose-groups.d.ts +10 -0
  26. package/dist/checks/chan-005-verbose-groups.js +53 -0
  27. package/dist/checks/disc-001-mdns-full.d.ts +10 -0
  28. package/dist/checks/disc-001-mdns-full.js +34 -0
  29. package/dist/checks/disc-002-mdns-enabled.d.ts +10 -0
  30. package/dist/checks/disc-002-mdns-enabled.js +35 -0
  31. package/dist/checks/exec-001-full-security.d.ts +10 -0
  32. package/dist/checks/exec-001-full-security.js +34 -0
  33. package/dist/checks/exec-002-sandbox-disabled.d.ts +10 -0
  34. package/dist/checks/exec-002-sandbox-disabled.js +34 -0
  35. package/dist/checks/exec-003-elevated-unrestricted.d.ts +10 -0
  36. package/dist/checks/exec-003-elevated-unrestricted.js +38 -0
  37. package/dist/checks/exec-004-approval-fallback.d.ts +10 -0
  38. package/dist/checks/exec-004-approval-fallback.js +50 -0
  39. package/dist/checks/exec-005-sandbox-non-main.d.ts +10 -0
  40. package/dist/checks/exec-005-sandbox-non-main.js +34 -0
  41. package/dist/checks/exec-006-cross-agent-sandbox.d.ts +10 -0
  42. package/dist/checks/exec-006-cross-agent-sandbox.js +34 -0
  43. package/dist/checks/exec-007-workspace-rw.d.ts +10 -0
  44. package/dist/checks/exec-007-workspace-rw.js +34 -0
  45. package/dist/checks/index.d.ts +16 -0
  46. package/dist/checks/index.js +94 -0
  47. package/dist/checks/loader.d.ts +38 -0
  48. package/dist/checks/loader.js +149 -0
  49. package/dist/checks/model-001-weak-model-tools.d.ts +10 -0
  50. package/dist/checks/model-001-weak-model-tools.js +68 -0
  51. package/dist/checks/net-001-gateway-binding.d.ts +10 -0
  52. package/dist/checks/net-001-gateway-binding.js +34 -0
  53. package/dist/checks/net-002-default-port.d.ts +10 -0
  54. package/dist/checks/net-002-default-port.js +35 -0
  55. package/dist/checks/net-003-tailnet-no-token.d.ts +10 -0
  56. package/dist/checks/net-003-tailnet-no-token.js +34 -0
  57. package/dist/checks/plug-001-no-allowlist.d.ts +10 -0
  58. package/dist/checks/plug-001-no-allowlist.js +52 -0
  59. package/dist/checks/plug-002-extensions-exposed.d.ts +10 -0
  60. package/dist/checks/plug-002-extensions-exposed.js +41 -0
  61. package/dist/checks/runner.d.ts +14 -0
  62. package/dist/checks/runner.js +72 -0
  63. package/dist/checks/schema.d.ts +54 -0
  64. package/dist/checks/schema.js +171 -0
  65. package/dist/checks/sec-001-hardcoded-keys.d.ts +10 -0
  66. package/dist/checks/sec-001-hardcoded-keys.js +34 -0
  67. package/dist/checks/sec-002-world-readable-config.d.ts +10 -0
  68. package/dist/checks/sec-002-world-readable-config.js +39 -0
  69. package/dist/checks/sec-003-credentials-exposed.d.ts +10 -0
  70. package/dist/checks/sec-003-credentials-exposed.js +41 -0
  71. package/dist/checks/sec-004-env-readable.d.ts +10 -0
  72. package/dist/checks/sec-004-env-readable.js +40 -0
  73. package/dist/checks/sec-005-transcripts-exposed.d.ts +11 -0
  74. package/dist/checks/sec-005-transcripts-exposed.js +62 -0
  75. package/dist/checks/sec-006-redaction-disabled.d.ts +10 -0
  76. package/dist/checks/sec-006-redaction-disabled.js +34 -0
  77. package/dist/checks/sec-007-no-redact-patterns.d.ts +10 -0
  78. package/dist/checks/sec-007-no-redact-patterns.js +39 -0
  79. package/dist/checks/sec-008-state-dir-permissions.d.ts +10 -0
  80. package/dist/checks/sec-008-state-dir-permissions.js +49 -0
  81. package/dist/checks/types.d.ts +45 -0
  82. package/dist/checks/types.js +2 -0
  83. package/dist/clawdit-output.schema.json +162 -0
  84. package/dist/cli.d.ts +23 -0
  85. package/dist/cli.js +132 -0
  86. package/dist/config.d.ts +22 -0
  87. package/dist/config.js +150 -0
  88. package/dist/formatter.d.ts +42 -0
  89. package/dist/formatter.js +233 -0
  90. package/dist/index.d.ts +3 -0
  91. package/dist/index.js +168 -0
  92. package/dist/utils.d.ts +46 -0
  93. package/dist/utils.js +146 -0
  94. package/package.json +48 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Token Security
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,197 @@
1
+ <p align="center">
2
+ <img src="./assets/logo.png" alt="clawdit" width="400">
3
+ </p>
4
+
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg)](package.json)
7
+ [![GitHub stars](https://img.shields.io/github/stars/token-security/clawdit?style=social)](https://github.com/token-security/clawdit)
8
+
9
+ > Security audit tool for OpenClaw configurations. Find misconfigurations before attackers do.
10
+
11
+ ## Features
12
+
13
+ - **Auto-discovery** - Finds config files in standard locations
14
+ - **35 security checks** - Across 8 categories (network, auth, execution, secrets, and more)
15
+ - **CI/CD ready** - JSON output with structured exit codes
16
+ - **Stdin support** - Pipe configs directly for scripting
17
+ - **Zero config** - Works out of the box
18
+
19
+ ## Demo
20
+
21
+ <!-- PLACEHOLDER: Add a terminal GIF or screenshot showing clawdit in action -->
22
+ <!-- Tools like asciinema, terminalizer, or vhs can record terminal sessions -->
23
+ <!-- Example: ![clawdit demo](./assets/demo.gif) -->
24
+
25
+ ```bash
26
+ $ clawdit
27
+ clawdit v0.1.0
28
+
29
+ Scanning: /home/user/.openclaw/openclaw.json
30
+
31
+ HIGH NET-001 Gateway binding to 0.0.0.0 exposes service to all interfaces
32
+ HIGH AUTH-002 No authentication configured for gateway
33
+ MED SEC-003 Config file has overly permissive permissions (0644)
34
+
35
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
36
+ Summary: 2 HIGH, 1 MEDIUM, 0 LOW (35 checks run)
37
+ ```
38
+
39
+ ## Quick Start
40
+
41
+ ### Installation
42
+
43
+ ```bash
44
+ npm install -g @token-security/clawdit
45
+ ```
46
+
47
+ Or from source:
48
+ ```bash
49
+ git clone https://github.com/token-security/clawdit.git
50
+ cd clawdit && npm install && npm run build
51
+ npm install -g .
52
+ ```
53
+
54
+ ### Uninstall
55
+
56
+ ```bash
57
+ npm uninstall -g @token-security/clawdit
58
+ ```
59
+
60
+ ### Usage
61
+
62
+ ```bash
63
+ clawdit # Auto-discover config and audit
64
+ clawdit /path/to/config.json # Audit specific file
65
+ clawdit - # Read from stdin
66
+ clawdit --format=json # JSON output (default when piped)
67
+ clawdit --list-checks # List all security checks
68
+ clawdit --severity=high # Only show HIGH findings
69
+ clawdit --help # Full options
70
+ ```
71
+
72
+ ## Security Checks
73
+
74
+ ### Network (NET)
75
+
76
+ | ID | Severity | Description |
77
+ |----|----------|-------------|
78
+ | NET-001 | HIGH | Gateway bound to all interfaces |
79
+ | NET-002 | MEDIUM | Non-default gateway port |
80
+ | NET-003 | LOW | Gateway bound to tailnet without token auth |
81
+
82
+ ### Authentication (AUTH)
83
+
84
+ | ID | Severity | Description |
85
+ |----|----------|-------------|
86
+ | AUTH-001 | HIGH | Device authentication disabled |
87
+ | AUTH-002 | HIGH | Insecure auth fallback enabled |
88
+ | AUTH-003 | HIGH | No gateway authentication configured |
89
+ | AUTH-004 | MEDIUM | Trusted proxies includes non-private IPs |
90
+ | AUTH-005 | MEDIUM | Hooks token not configured |
91
+ | AUTH-006 | MEDIUM | Node pairing credentials exposed |
92
+ | AUTH-007 | LOW | Missing trusted proxies configuration |
93
+
94
+ ### Execution (EXEC)
95
+
96
+ | ID | Severity | Description |
97
+ |----|----------|-------------|
98
+ | EXEC-001 | HIGH | Exec security set to full |
99
+ | EXEC-002 | HIGH | Sandbox disabled for all sessions |
100
+ | EXEC-003 | HIGH | Elevated mode enabled without restrictions |
101
+ | EXEC-004 | MEDIUM | Exec approval fallback not set to deny |
102
+ | EXEC-005 | MEDIUM | Sandbox only protects non-main sessions |
103
+ | EXEC-006 | HIGH | Sandbox scope enables cross-agent access |
104
+ | EXEC-007 | MEDIUM | Workspace access too permissive |
105
+
106
+ ### Secrets (SEC)
107
+
108
+ | ID | Severity | Description |
109
+ |----|----------|-------------|
110
+ | SEC-001 | HIGH | API keys hardcoded in config |
111
+ | SEC-002 | HIGH | Configuration file is world-readable |
112
+ | SEC-003 | HIGH | Credentials directory exposed |
113
+ | SEC-004 | MEDIUM | .env file is readable by other users |
114
+ | SEC-005 | MEDIUM | Session transcripts exposed |
115
+ | SEC-006 | LOW | Log redaction disabled |
116
+ | SEC-007 | LOW | No custom redact patterns |
117
+ | SEC-008 | MEDIUM | State directory has insecure permissions |
118
+
119
+ ### Discovery (DISC)
120
+
121
+ | ID | Severity | Description |
122
+ |----|----------|-------------|
123
+ | DISC-001 | MEDIUM | mDNS full mode exposes system info |
124
+ | DISC-002 | LOW | mDNS enabled (information disclosure) |
125
+
126
+ ### Channels (CHAN)
127
+
128
+ | ID | Severity | Description |
129
+ |----|----------|-------------|
130
+ | CHAN-001 | HIGH | Open DM policy without allowlist |
131
+ | CHAN-002 | MEDIUM | Group policy not set to allowlist |
132
+ | CHAN-003 | LOW | Require mention disabled in groups |
133
+ | CHAN-004 | MEDIUM | DM session isolation disabled |
134
+ | CHAN-005 | LOW | Verbose/reasoning enabled in groups |
135
+
136
+ ### Model (MODEL)
137
+
138
+ | ID | Severity | Description |
139
+ |----|----------|-------------|
140
+ | MODEL-001 | LOW | Weak model with tools enabled |
141
+
142
+ ### Plugins (PLUG)
143
+
144
+ | ID | Severity | Description |
145
+ |----|----------|-------------|
146
+ | PLUG-001 | MEDIUM | Plugins without explicit allowlist |
147
+ | PLUG-002 | MEDIUM | Plugin directory permissions exposed |
148
+
149
+ ## Scripting
150
+
151
+ ```bash
152
+ # Check exit code in scripts
153
+ clawdit config.json && echo "All clear" || echo "Issues found: $?"
154
+
155
+ # Parse JSON output
156
+ clawdit --format=json | jq '.findings[] | {id, severity, name}'
157
+
158
+ # Pipe config from another command
159
+ cat config.json | clawdit -
160
+ ```
161
+
162
+ ## Exit Codes
163
+
164
+ | Code | Meaning |
165
+ |------|---------|
166
+ | 0 | All checks passed |
167
+ | 1 | HIGH severity findings |
168
+ | 2 | MEDIUM severity findings |
169
+ | 3 | LOW severity findings |
170
+ | 10+ | Configuration/runtime errors |
171
+
172
+ ## Versioning
173
+
174
+ This project follows [Semantic Versioning](https://semver.org/):
175
+
176
+ | Version Bump | When to Use | Example |
177
+ |--------------|-------------|---------|
178
+ | **MAJOR** (x.0.0) | Breaking changes: removed checks, changed exit codes, incompatible CLI flags | 1.0.0 → 2.0.0 |
179
+ | **MINOR** (0.x.0) | New features: new checks, new output formats, new CLI options | 0.1.0 → 0.2.0 |
180
+ | **PATCH** (0.0.x) | Bug fixes: fixed false positives, documentation updates | 0.1.0 → 0.1.1 |
181
+
182
+ To release a new version:
183
+ ```bash
184
+ npm version patch|minor|major
185
+ git push --follow-tags
186
+ npm publish --access public
187
+ ```
188
+
189
+ ## Contributing
190
+
191
+ PRs welcome! Please run `npm test` before submitting.
192
+
193
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines and [CLAUDE.md](CLAUDE.md) for architecture details.
194
+
195
+ ## License
196
+
197
+ [MIT](LICENSE) - Token Security
@@ -0,0 +1,10 @@
1
+ /**
2
+ * AUTH-001: Device authentication disabled
3
+ *
4
+ * Detects when device authentication is explicitly disabled, allowing
5
+ * unauthorized control of the gateway.
6
+ */
7
+ import type { Check } from './types.js';
8
+ declare const check: Check;
9
+ export default check;
10
+ //# sourceMappingURL=auth-001-device-auth-disabled.d.ts.map
@@ -0,0 +1,34 @@
1
+ /**
2
+ * AUTH-001: Device authentication disabled
3
+ *
4
+ * Detects when device authentication is explicitly disabled, allowing
5
+ * unauthorized control of the gateway.
6
+ */
7
+ import { getValueAtPath } from '../utils.js';
8
+ const check = {
9
+ id: 'AUTH-001',
10
+ severity: 'HIGH',
11
+ name: 'Device authentication disabled',
12
+ execute(ctx) {
13
+ const disabled = getValueAtPath(ctx.config, 'gateway.controlUi.dangerouslyDisableDeviceAuth');
14
+ if (disabled === true) {
15
+ return [{
16
+ id: 'AUTH-001',
17
+ severity: 'HIGH',
18
+ name: 'Device authentication disabled',
19
+ location: { file: ctx.configPath, path: 'gateway.controlUi.dangerouslyDisableDeviceAuth' },
20
+ currentValue: true,
21
+ expectedValue: 'false (or not set)',
22
+ risk: 'Attackers can bypass device identity verification, enabling unauthorized control of the gateway from any paired client.',
23
+ fix: {
24
+ description: 'Remove or set to false in config',
25
+ command: `jq 'del(.gateway.controlUi.dangerouslyDisableDeviceAuth)' ${ctx.configPath} > tmp.json && mv tmp.json ${ctx.configPath}`,
26
+ },
27
+ references: ['https://docs.openclaw.ai/gateway/security#device-authentication'],
28
+ }];
29
+ }
30
+ return [];
31
+ },
32
+ };
33
+ export default check;
34
+ //# sourceMappingURL=auth-001-device-auth-disabled.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * AUTH-002: Insecure auth fallback enabled
3
+ *
4
+ * Detects when insecure authentication fallback is enabled, allowing
5
+ * authentication to be bypassed.
6
+ */
7
+ import type { Check } from './types.js';
8
+ declare const check: Check;
9
+ export default check;
10
+ //# sourceMappingURL=auth-002-insecure-fallback.d.ts.map
@@ -0,0 +1,34 @@
1
+ /**
2
+ * AUTH-002: Insecure auth fallback enabled
3
+ *
4
+ * Detects when insecure authentication fallback is enabled, allowing
5
+ * authentication to be bypassed.
6
+ */
7
+ import { getValueAtPath } from '../utils.js';
8
+ const check = {
9
+ id: 'AUTH-002',
10
+ severity: 'HIGH',
11
+ name: 'Insecure auth fallback enabled',
12
+ execute(ctx) {
13
+ const insecure = getValueAtPath(ctx.config, 'gateway.controlUi.allowInsecureAuth');
14
+ if (insecure === true) {
15
+ return [{
16
+ id: 'AUTH-002',
17
+ severity: 'HIGH',
18
+ name: 'Insecure auth fallback enabled',
19
+ location: { file: ctx.configPath, path: 'gateway.controlUi.allowInsecureAuth' },
20
+ currentValue: true,
21
+ expectedValue: 'false (or not set)',
22
+ risk: 'Authentication can be bypassed using insecure fallback mechanisms, allowing unauthorized access.',
23
+ fix: {
24
+ description: 'Remove or set to false in config',
25
+ command: `jq 'del(.gateway.controlUi.allowInsecureAuth)' ${ctx.configPath} > tmp.json && mv tmp.json ${ctx.configPath}`,
26
+ },
27
+ references: ['https://docs.openclaw.ai/gateway/security#authentication'],
28
+ }];
29
+ }
30
+ return [];
31
+ },
32
+ };
33
+ export default check;
34
+ //# sourceMappingURL=auth-002-insecure-fallback.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * AUTH-003: No gateway authentication configured
3
+ *
4
+ * Detects when the gateway is network-accessible but has no authentication
5
+ * configured, allowing anyone to control it.
6
+ */
7
+ import type { Check } from './types.js';
8
+ declare const check: Check;
9
+ export default check;
10
+ //# sourceMappingURL=auth-003-no-gateway-auth.d.ts.map
@@ -0,0 +1,40 @@
1
+ /**
2
+ * AUTH-003: No gateway authentication configured
3
+ *
4
+ * Detects when the gateway is network-accessible but has no authentication
5
+ * configured, allowing anyone to control it.
6
+ */
7
+ import { getValueAtPath, hasGatewayAuth } from '../utils.js';
8
+ const check = {
9
+ id: 'AUTH-003',
10
+ severity: 'HIGH',
11
+ name: 'No gateway authentication configured',
12
+ execute(ctx) {
13
+ const bind = getValueAtPath(ctx.config, 'gateway.bind');
14
+ // Skip if gateway.bind is not configured (safe default)
15
+ if (bind === undefined)
16
+ return [];
17
+ // Only check if not bound to loopback
18
+ if (bind !== 'loopback' && bind !== '127.0.0.1' && bind !== 'localhost') {
19
+ if (!hasGatewayAuth(ctx.config)) {
20
+ return [{
21
+ id: 'AUTH-003',
22
+ severity: 'HIGH',
23
+ name: 'No gateway authentication configured',
24
+ location: { file: ctx.configPath, path: 'gateway.auth' },
25
+ currentValue: 'not configured',
26
+ expectedValue: 'gateway.auth.token or gateway.auth.password set',
27
+ risk: 'The gateway is network-accessible but has no authentication configured. Anyone who can reach the gateway can control it.',
28
+ fix: {
29
+ description: 'Configure authentication token',
30
+ command: `jq '.gateway.auth.token = "YOUR_SECRET_TOKEN"' ${ctx.configPath} > tmp.json && mv tmp.json ${ctx.configPath}`,
31
+ },
32
+ references: ['https://docs.openclaw.ai/gateway/security#authentication'],
33
+ }];
34
+ }
35
+ }
36
+ return [];
37
+ },
38
+ };
39
+ export default check;
40
+ //# sourceMappingURL=auth-003-no-gateway-auth.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * AUTH-004: Trusted proxies includes non-private IPs
3
+ *
4
+ * Detects when the trusted proxies list contains public (non-RFC1918/RFC6598) IP addresses,
5
+ * which could allow IP spoofing attacks.
6
+ */
7
+ import type { Check } from './types.js';
8
+ declare const check: Check;
9
+ export default check;
10
+ //# sourceMappingURL=auth-004-public-trusted-proxies.d.ts.map
@@ -0,0 +1,37 @@
1
+ /**
2
+ * AUTH-004: Trusted proxies includes non-private IPs
3
+ *
4
+ * Detects when the trusted proxies list contains public (non-RFC1918/RFC6598) IP addresses,
5
+ * which could allow IP spoofing attacks.
6
+ */
7
+ import { getValueAtPath, isPrivateIP } from '../utils.js';
8
+ const check = {
9
+ id: 'AUTH-004',
10
+ severity: 'MEDIUM',
11
+ name: 'Trusted proxies includes non-private IPs',
12
+ execute(ctx) {
13
+ const trustedProxies = getValueAtPath(ctx.config, 'gateway.trustedProxies');
14
+ if (!Array.isArray(trustedProxies))
15
+ return [];
16
+ const publicIPs = trustedProxies.filter((ip) => typeof ip === 'string' && !isPrivateIP(ip));
17
+ if (publicIPs.length > 0) {
18
+ return [{
19
+ id: 'AUTH-004',
20
+ severity: 'MEDIUM',
21
+ name: 'Trusted proxies includes non-private IPs',
22
+ location: { file: ctx.configPath, path: 'gateway.trustedProxies' },
23
+ currentValue: publicIPs.join(', '),
24
+ expectedValue: 'Only private IPs (RFC1918/RFC6598)',
25
+ risk: `Public IP addresses in trusted proxies can be spoofed. Attackers could manipulate X-Forwarded-For headers to bypass IP-based access controls.`,
26
+ fix: {
27
+ description: 'Remove public IPs from trusted proxies',
28
+ command: `jq '.gateway.trustedProxies = [.gateway.trustedProxies[] | select(. | test("^(10\\\\.|172\\\\.(1[6-9]|2[0-9]|3[01])\\\\.|192\\\\.168\\\\.|127\\\\.|100\\\\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\\\\.)") or . == "loopback" or . == "localhost")]' ${ctx.configPath} > tmp.json && mv tmp.json ${ctx.configPath}`,
29
+ },
30
+ references: ['https://docs.openclaw.ai/gateway/security#trusted-proxies'],
31
+ }];
32
+ }
33
+ return [];
34
+ },
35
+ };
36
+ export default check;
37
+ //# sourceMappingURL=auth-004-public-trusted-proxies.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * AUTH-005: Hooks token not configured
3
+ *
4
+ * Detects when hooks are enabled but no authentication token is configured,
5
+ * allowing unauthenticated webhook requests.
6
+ */
7
+ import type { Check } from './types.js';
8
+ declare const check: Check;
9
+ export default check;
10
+ //# sourceMappingURL=auth-005-hooks-no-token.d.ts.map
@@ -0,0 +1,42 @@
1
+ /**
2
+ * AUTH-005: Hooks token not configured
3
+ *
4
+ * Detects when hooks are enabled but no authentication token is configured,
5
+ * allowing unauthenticated webhook requests.
6
+ */
7
+ import { getValueAtPath } from '../utils.js';
8
+ const check = {
9
+ id: 'AUTH-005',
10
+ severity: 'MEDIUM',
11
+ name: 'Hooks token not configured',
12
+ execute(ctx) {
13
+ const hooks = getValueAtPath(ctx.config, 'hooks');
14
+ if (!hooks || typeof hooks !== 'object')
15
+ return [];
16
+ const hooksConfig = hooks;
17
+ const enabled = hooksConfig.enabled;
18
+ const token = hooksConfig.token;
19
+ // Check if hooks are enabled (explicit or implicit via having hook definitions)
20
+ const hasHookDefinitions = Object.keys(hooksConfig).some(key => key !== 'enabled' && key !== 'token' && typeof hooksConfig[key] === 'object');
21
+ const hooksEnabled = enabled === true || (enabled !== false && hasHookDefinitions);
22
+ if (hooksEnabled && (!token || (typeof token === 'string' && token.length === 0))) {
23
+ return [{
24
+ id: 'AUTH-005',
25
+ severity: 'MEDIUM',
26
+ name: 'Hooks token not configured',
27
+ location: { file: ctx.configPath, path: 'hooks.token' },
28
+ currentValue: token ?? 'not set',
29
+ expectedValue: 'A secure token string',
30
+ risk: 'Hooks are enabled without authentication. Attackers who discover the webhook endpoint can trigger arbitrary hook events.',
31
+ fix: {
32
+ description: 'Configure a secure token for webhook authentication',
33
+ command: `jq '.hooks.token = "your-secure-webhook-token"' ${ctx.configPath} > tmp.json && mv tmp.json ${ctx.configPath}`,
34
+ },
35
+ references: ['https://docs.openclaw.ai/hooks/security#authentication'],
36
+ }];
37
+ }
38
+ return [];
39
+ },
40
+ };
41
+ export default check;
42
+ //# sourceMappingURL=auth-005-hooks-no-token.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * AUTH-006: Node pairing credentials exposed
3
+ *
4
+ * Detects when ~/.openclaw/nodes/paired.json has permissions other than 600,
5
+ * potentially exposing node pairing credentials to other users.
6
+ */
7
+ import type { Check } from './types.js';
8
+ declare const check: Check;
9
+ export default check;
10
+ //# sourceMappingURL=auth-006-pairing-exposed.d.ts.map
@@ -0,0 +1,46 @@
1
+ /**
2
+ * AUTH-006: Node pairing credentials exposed
3
+ *
4
+ * Detects when ~/.openclaw/nodes/paired.json has permissions other than 600,
5
+ * potentially exposing node pairing credentials to other users.
6
+ */
7
+ import { getFileMode, formatMode } from '../utils.js';
8
+ import { homedir } from 'node:os';
9
+ import { join } from 'node:path';
10
+ import { existsSync } from 'node:fs';
11
+ const check = {
12
+ id: 'AUTH-006',
13
+ severity: 'MEDIUM',
14
+ name: 'Node pairing credentials exposed',
15
+ execute(ctx) {
16
+ const pairedPath = join(homedir(), '.openclaw', 'nodes', 'paired.json');
17
+ // Skip if file doesn't exist
18
+ if (!existsSync(pairedPath))
19
+ return [];
20
+ const mode = getFileMode(pairedPath);
21
+ // Skip on Windows
22
+ if (mode === null)
23
+ return [];
24
+ // Check if group or other has any permissions
25
+ const groupOther = mode & 0o077;
26
+ if (groupOther > 0) {
27
+ return [{
28
+ id: 'AUTH-006',
29
+ severity: 'MEDIUM',
30
+ name: 'Node pairing credentials exposed',
31
+ location: { file: pairedPath, path: null },
32
+ currentValue: formatMode(mode),
33
+ expectedValue: '600',
34
+ risk: 'Node pairing credentials are readable by other users on the system. An attacker could use these credentials to impersonate paired nodes.',
35
+ fix: {
36
+ description: 'Restrict file permissions',
37
+ command: `chmod 600 ${pairedPath}`,
38
+ },
39
+ references: ['https://docs.openclaw.ai/nodes/security#pairing-credentials'],
40
+ }];
41
+ }
42
+ return [];
43
+ },
44
+ };
45
+ export default check;
46
+ //# sourceMappingURL=auth-006-pairing-exposed.js.map
@@ -0,0 +1,11 @@
1
+ /**
2
+ * AUTH-007: Missing trusted proxies configuration
3
+ *
4
+ * Detects when gateway is bound to loopback but trustedProxies is not configured.
5
+ * This is a low-severity informational check because it only matters if the
6
+ * service is exposed through a reverse proxy.
7
+ */
8
+ import type { Check } from './types.js';
9
+ declare const check: Check;
10
+ export default check;
11
+ //# sourceMappingURL=auth-007-missing-trusted-proxies.d.ts.map
@@ -0,0 +1,46 @@
1
+ /**
2
+ * AUTH-007: Missing trusted proxies configuration
3
+ *
4
+ * Detects when gateway is bound to loopback but trustedProxies is not configured.
5
+ * This is a low-severity informational check because it only matters if the
6
+ * service is exposed through a reverse proxy.
7
+ */
8
+ import { getValueAtPath } from '../utils.js';
9
+ const check = {
10
+ id: 'AUTH-007',
11
+ severity: 'LOW',
12
+ name: 'Missing trusted proxies configuration',
13
+ execute(ctx) {
14
+ const bind = getValueAtPath(ctx.config, 'gateway.bind');
15
+ const trustedProxies = getValueAtPath(ctx.config, 'gateway.trustedProxies');
16
+ // Check if binding is loopback (or undefined, which defaults to loopback)
17
+ const isLoopback = bind === 'loopback' ||
18
+ bind === '127.0.0.1' ||
19
+ bind === 'localhost' ||
20
+ bind === undefined;
21
+ // Check if trusted proxies is configured
22
+ const hasProxies = Array.isArray(trustedProxies) && trustedProxies.length > 0;
23
+ if (isLoopback && !hasProxies) {
24
+ return [{
25
+ id: 'AUTH-007',
26
+ severity: 'LOW',
27
+ name: 'Missing trusted proxies configuration',
28
+ location: {
29
+ file: ctx.configPath,
30
+ path: 'gateway.trustedProxies',
31
+ },
32
+ currentValue: trustedProxies ?? '(not set)',
33
+ expectedValue: 'Array of trusted proxy IPs (if using reverse proxy)',
34
+ risk: 'If the Control UI is exposed through a reverse proxy without configuring trusted proxies, local-client checks can be spoofed via X-Forwarded-For headers.',
35
+ fix: {
36
+ description: 'Configure trusted proxy IPs if using a reverse proxy',
37
+ command: `jq '.gateway.trustedProxies = ["127.0.0.1"]' ${ctx.configPath} > tmp.json && mv tmp.json ${ctx.configPath}`,
38
+ },
39
+ references: [],
40
+ }];
41
+ }
42
+ return [];
43
+ },
44
+ };
45
+ export default check;
46
+ //# sourceMappingURL=auth-007-missing-trusted-proxies.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * CHAN-001: Open DM policy without allowlist
3
+ *
4
+ * Detects when a channel has open DM policy without a specific allowlist,
5
+ * allowing anyone to send direct messages to the bot.
6
+ */
7
+ import type { Check } from './types.js';
8
+ declare const check: Check;
9
+ export default check;
10
+ //# sourceMappingURL=chan-001-open-dm.d.ts.map
@@ -0,0 +1,48 @@
1
+ /**
2
+ * CHAN-001: Open DM policy without allowlist
3
+ *
4
+ * Detects when a channel has open DM policy without a specific allowlist,
5
+ * allowing anyone to send direct messages to the bot.
6
+ */
7
+ import { getValueAtPath } from '../utils.js';
8
+ const check = {
9
+ id: 'CHAN-001',
10
+ severity: 'HIGH',
11
+ name: 'Open DM policy without allowlist',
12
+ execute(ctx) {
13
+ const findings = [];
14
+ const channels = getValueAtPath(ctx.config, 'channels');
15
+ if (!channels || typeof channels !== 'object')
16
+ return [];
17
+ for (const [channelName, channelConfig] of Object.entries(channels)) {
18
+ if (!channelConfig || typeof channelConfig !== 'object')
19
+ continue;
20
+ const dmPolicy = channelConfig.dmPolicy;
21
+ const allowFrom = channelConfig.allowFrom;
22
+ if (dmPolicy === 'open') {
23
+ const hasAllowlist = Array.isArray(allowFrom) &&
24
+ allowFrom.length > 0 &&
25
+ !allowFrom.includes('*');
26
+ if (!hasAllowlist) {
27
+ findings.push({
28
+ id: 'CHAN-001',
29
+ severity: 'HIGH',
30
+ name: 'Open DM policy without allowlist',
31
+ location: { file: ctx.configPath, path: `channels.${channelName}.dmPolicy` },
32
+ currentValue: `dmPolicy: open, allowFrom: ${allowFrom ? JSON.stringify(allowFrom) : 'not set'}`,
33
+ expectedValue: 'dmPolicy: allowlist OR allowFrom with specific users',
34
+ risk: `Channel "${channelName}" accepts DMs from anyone. Attackers can send malicious prompts directly to the bot.`,
35
+ fix: {
36
+ description: 'Configure allowlist for DM policy',
37
+ command: `jq '.channels["${channelName}"].dmPolicy = "allowlist"' ${ctx.configPath} > tmp.json && mv tmp.json ${ctx.configPath}`,
38
+ },
39
+ references: ['https://docs.openclaw.ai/channels/security#dm-policy'],
40
+ });
41
+ }
42
+ }
43
+ }
44
+ return findings;
45
+ },
46
+ };
47
+ export default check;
48
+ //# sourceMappingURL=chan-001-open-dm.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * CHAN-002: Group policy not set to allowlist
3
+ *
4
+ * Detects when a channel's groupPolicy is not set to 'allowlist',
5
+ * potentially allowing messages from unauthorized groups.
6
+ */
7
+ import type { Check } from './types.js';
8
+ declare const check: Check;
9
+ export default check;
10
+ //# sourceMappingURL=chan-002-group-policy.d.ts.map