@clawdstrike/openclaw 0.1.3 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/audit/adapter-logger.d.ts +3 -3
- package/dist/audit/adapter-logger.d.ts.map +1 -1
- package/dist/audit/adapter-logger.js +3 -3
- package/dist/audit/adapter-logger.js.map +1 -1
- package/dist/audit/store.d.ts +2 -2
- package/dist/audit/store.d.ts.map +1 -1
- package/dist/audit/store.js +13 -13
- package/dist/audit/store.js.map +1 -1
- package/dist/classification.d.ts +2 -2
- package/dist/classification.d.ts.map +1 -1
- package/dist/classification.js +96 -28
- package/dist/classification.js.map +1 -1
- package/dist/cli/bin.js +1 -1
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +29 -29
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/policy.d.ts.map +1 -1
- package/dist/cli/commands/policy.js +33 -33
- package/dist/cli/commands/policy.js.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +45 -56
- package/dist/cli/index.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -9
- package/dist/config.js.map +1 -1
- package/dist/e2e/openclaw-e2e.js +58 -49
- package/dist/e2e/openclaw-e2e.js.map +1 -1
- package/dist/engine-holder.d.ts +2 -2
- package/dist/engine-holder.js +1 -1
- package/dist/guards/egress.d.ts +2 -2
- package/dist/guards/egress.d.ts.map +1 -1
- package/dist/guards/egress.js +71 -73
- package/dist/guards/egress.js.map +1 -1
- package/dist/guards/forbidden-path.d.ts +2 -2
- package/dist/guards/forbidden-path.d.ts.map +1 -1
- package/dist/guards/forbidden-path.js +41 -43
- package/dist/guards/forbidden-path.js.map +1 -1
- package/dist/guards/index.d.ts +6 -6
- package/dist/guards/index.d.ts.map +1 -1
- package/dist/guards/index.js +5 -5
- package/dist/guards/index.js.map +1 -1
- package/dist/guards/patch-integrity.d.ts +2 -2
- package/dist/guards/patch-integrity.d.ts.map +1 -1
- package/dist/guards/patch-integrity.js +69 -70
- package/dist/guards/patch-integrity.js.map +1 -1
- package/dist/guards/secret-leak.d.ts +2 -2
- package/dist/guards/secret-leak.d.ts.map +1 -1
- package/dist/guards/secret-leak.js +81 -82
- package/dist/guards/secret-leak.js.map +1 -1
- package/dist/guards/types.d.ts +2 -2
- package/dist/guards/types.d.ts.map +1 -1
- package/dist/guards/types.js +4 -4
- package/dist/guards/types.js.map +1 -1
- package/dist/hooks/agent-bootstrap/handler.d.ts +1 -1
- package/dist/hooks/agent-bootstrap/handler.d.ts.map +1 -1
- package/dist/hooks/agent-bootstrap/handler.js +5 -5
- package/dist/hooks/agent-bootstrap/handler.js.map +1 -1
- package/dist/hooks/approval-state.d.ts +1 -1
- package/dist/hooks/approval-state.d.ts.map +1 -1
- package/dist/hooks/approval-state.js +15 -15
- package/dist/hooks/approval-state.js.map +1 -1
- package/dist/hooks/approval-utils.d.ts +1 -1
- package/dist/hooks/approval-utils.d.ts.map +1 -1
- package/dist/hooks/approval-utils.js +41 -20
- package/dist/hooks/approval-utils.js.map +1 -1
- package/dist/hooks/audit-logger/handler.d.ts +1 -1
- package/dist/hooks/audit-logger/handler.d.ts.map +1 -1
- package/dist/hooks/audit-logger/handler.js +9 -9
- package/dist/hooks/audit-logger/handler.js.map +1 -1
- package/dist/hooks/cua-bridge/handler.d.ts +4 -4
- package/dist/hooks/cua-bridge/handler.d.ts.map +1 -1
- package/dist/hooks/cua-bridge/handler.js +85 -70
- package/dist/hooks/cua-bridge/handler.js.map +1 -1
- package/dist/hooks/tool-guard/handler.d.ts +1 -1
- package/dist/hooks/tool-guard/handler.d.ts.map +1 -1
- package/dist/hooks/tool-guard/handler.js +112 -101
- package/dist/hooks/tool-guard/handler.js.map +1 -1
- package/dist/hooks/tool-preflight/handler.d.ts +2 -2
- package/dist/hooks/tool-preflight/handler.d.ts.map +1 -1
- package/dist/hooks/tool-preflight/handler.js +115 -91
- package/dist/hooks/tool-preflight/handler.js.map +1 -1
- package/dist/index.d.ts +16 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -18
- package/dist/index.js.map +1 -1
- package/dist/openclaw-adapter.d.ts +2 -2
- package/dist/openclaw-adapter.d.ts.map +1 -1
- package/dist/openclaw-adapter.js +4 -4
- package/dist/openclaw-adapter.js.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +39 -40
- package/dist/plugin.js.map +1 -1
- package/dist/policy/engine.d.ts +1 -1
- package/dist/policy/engine.d.ts.map +1 -1
- package/dist/policy/engine.js +237 -221
- package/dist/policy/engine.js.map +1 -1
- package/dist/policy/index.d.ts +3 -3
- package/dist/policy/index.d.ts.map +1 -1
- package/dist/policy/index.js +3 -3
- package/dist/policy/index.js.map +1 -1
- package/dist/policy/loader.d.ts +1 -1
- package/dist/policy/loader.d.ts.map +1 -1
- package/dist/policy/loader.js +76 -63
- package/dist/policy/loader.js.map +1 -1
- package/dist/policy/validator.d.ts +1 -1
- package/dist/policy/validator.d.ts.map +1 -1
- package/dist/policy/validator.js +158 -151
- package/dist/policy/validator.js.map +1 -1
- package/dist/receipt/signer.d.ts +2 -2
- package/dist/receipt/signer.d.ts.map +1 -1
- package/dist/receipt/signer.js +12 -12
- package/dist/receipt/signer.js.map +1 -1
- package/dist/receipt/types.d.ts +2 -2
- package/dist/receipt/types.d.ts.map +1 -1
- package/dist/sanitizer/output-sanitizer.d.ts +1 -1
- package/dist/sanitizer/output-sanitizer.d.ts.map +1 -1
- package/dist/sanitizer/output-sanitizer.js +8 -8
- package/dist/sanitizer/output-sanitizer.js.map +1 -1
- package/dist/security-prompt.d.ts +1 -1
- package/dist/security-prompt.d.ts.map +1 -1
- package/dist/security-prompt.js +16 -12
- package/dist/security-prompt.js.map +1 -1
- package/dist/tools/policy-check.d.ts +3 -3
- package/dist/tools/policy-check.d.ts.map +1 -1
- package/dist/tools/policy-check.js +60 -52
- package/dist/tools/policy-check.js.map +1 -1
- package/dist/translator/openclaw-translator.d.ts +1 -1
- package/dist/translator/openclaw-translator.d.ts.map +1 -1
- package/dist/translator/openclaw-translator.js +100 -80
- package/dist/translator/openclaw-translator.js.map +1 -1
- package/dist/types.d.ts +11 -13
- package/dist/types.d.ts.map +1 -1
- package/package.json +9 -4
package/dist/policy/validator.js
CHANGED
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
import { validatePolicy as validateCanonicalPolicy } from
|
|
2
|
-
export const POLICY_SCHEMA_VERSION =
|
|
3
|
-
const SUPPORTED_CANONICAL_VERSIONS = new Set([
|
|
4
|
-
const VALID_EGRESS_MODES = new Set([
|
|
5
|
-
const VALID_VIOLATION_ACTIONS = new Set([
|
|
6
|
-
const UNIMPLEMENTED_VIOLATION_ACTIONS = new Set([
|
|
7
|
-
const VALID_TIMEOUT_BEHAVIORS = new Set([
|
|
8
|
-
const VALID_EXECUTION_MODES = new Set([
|
|
9
|
-
const VALID_COMPUTER_USE_MODES = new Set([
|
|
1
|
+
import { validatePolicy as validateCanonicalPolicy } from "@clawdstrike/policy";
|
|
2
|
+
export const POLICY_SCHEMA_VERSION = "clawdstrike-v1.0";
|
|
3
|
+
const SUPPORTED_CANONICAL_VERSIONS = new Set(["1.1.0", "1.2.0"]);
|
|
4
|
+
const VALID_EGRESS_MODES = new Set(["allowlist", "denylist", "open", "deny_all"]);
|
|
5
|
+
const VALID_VIOLATION_ACTIONS = new Set(["cancel", "warn"]);
|
|
6
|
+
const UNIMPLEMENTED_VIOLATION_ACTIONS = new Set(["isolate", "escalate"]);
|
|
7
|
+
const VALID_TIMEOUT_BEHAVIORS = new Set(["allow", "deny", "warn", "defer"]);
|
|
8
|
+
const VALID_EXECUTION_MODES = new Set(["parallel", "sequential", "background"]);
|
|
9
|
+
const VALID_COMPUTER_USE_MODES = new Set(["observe", "guardrail", "fail_closed"]);
|
|
10
10
|
const PLACEHOLDER_RE = /\$\{([^}]+)\}/g;
|
|
11
11
|
const RESERVED_PACKAGES = new Set([
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
"clawdstrike-virustotal",
|
|
13
|
+
"clawdstrike-safe-browsing",
|
|
14
|
+
"clawdstrike-snyk",
|
|
15
15
|
]);
|
|
16
16
|
const POLICY_KEYS = new Set([
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
"version",
|
|
18
|
+
"extends",
|
|
19
|
+
"egress",
|
|
20
|
+
"filesystem",
|
|
21
|
+
"execution",
|
|
22
|
+
"tools",
|
|
23
|
+
"limits",
|
|
24
|
+
"guards",
|
|
25
|
+
"on_violation",
|
|
26
26
|
]);
|
|
27
|
-
const EGRESS_KEYS = new Set([
|
|
28
|
-
const FILESYSTEM_KEYS = new Set([
|
|
29
|
-
const EXECUTION_KEYS = new Set([
|
|
30
|
-
const TOOLS_KEYS = new Set([
|
|
31
|
-
const LIMITS_KEYS = new Set([
|
|
27
|
+
const EGRESS_KEYS = new Set(["mode", "allowed_domains", "allowed_cidrs", "denied_domains"]);
|
|
28
|
+
const FILESYSTEM_KEYS = new Set(["allowed_write_roots", "allowed_read_paths", "forbidden_paths"]);
|
|
29
|
+
const EXECUTION_KEYS = new Set(["allowed_commands", "denied_patterns"]);
|
|
30
|
+
const TOOLS_KEYS = new Set(["allowed", "denied"]);
|
|
31
|
+
const LIMITS_KEYS = new Set(["max_execution_seconds", "max_memory_mb", "max_output_bytes"]);
|
|
32
32
|
const GUARDS_KEYS = new Set([
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
"forbidden_path",
|
|
34
|
+
"egress",
|
|
35
|
+
"secret_leak",
|
|
36
|
+
"patch_integrity",
|
|
37
|
+
"mcp_tool",
|
|
38
|
+
"custom",
|
|
39
|
+
"computer_use",
|
|
40
|
+
"remote_desktop_side_channel",
|
|
41
|
+
"input_injection_capability",
|
|
42
42
|
]);
|
|
43
|
-
const COMPUTER_USE_KEYS = new Set([
|
|
43
|
+
const COMPUTER_USE_KEYS = new Set(["enabled", "mode", "allowed_actions"]);
|
|
44
44
|
const REMOTE_DESKTOP_SIDE_CHANNEL_KEYS = new Set([
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
45
|
+
"enabled",
|
|
46
|
+
"clipboard_enabled",
|
|
47
|
+
"file_transfer_enabled",
|
|
48
|
+
"audio_enabled",
|
|
49
|
+
"drive_mapping_enabled",
|
|
50
|
+
"printing_enabled",
|
|
51
|
+
"session_share_enabled",
|
|
52
|
+
"max_transfer_size_bytes",
|
|
53
53
|
]);
|
|
54
54
|
const INPUT_INJECTION_CAPABILITY_KEYS = new Set([
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
"enabled",
|
|
56
|
+
"allowed_input_types",
|
|
57
|
+
"require_postcondition_probe",
|
|
58
58
|
]);
|
|
59
59
|
function isPlainObject(value) {
|
|
60
|
-
return typeof value ===
|
|
60
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
61
61
|
}
|
|
62
62
|
function ensureAllowedKeys(obj, field, allowed, errors) {
|
|
63
63
|
for (const key of Object.keys(obj)) {
|
|
@@ -69,7 +69,7 @@ function ensureAllowedKeys(obj, field, allowed, errors) {
|
|
|
69
69
|
function ensureBoolean(value, field, errors) {
|
|
70
70
|
if (value === undefined)
|
|
71
71
|
return;
|
|
72
|
-
if (typeof value !==
|
|
72
|
+
if (typeof value !== "boolean") {
|
|
73
73
|
errors.push(`${field} must be a boolean`);
|
|
74
74
|
}
|
|
75
75
|
}
|
|
@@ -83,11 +83,11 @@ function ensureStringArray(value, field, errors, warnings) {
|
|
|
83
83
|
const out = [];
|
|
84
84
|
for (let i = 0; i < value.length; i++) {
|
|
85
85
|
const item = value[i];
|
|
86
|
-
if (typeof item !==
|
|
86
|
+
if (typeof item !== "string") {
|
|
87
87
|
errors.push(`${field}[${i}] must be a string`);
|
|
88
88
|
continue;
|
|
89
89
|
}
|
|
90
|
-
if (item.includes(
|
|
90
|
+
if (item.includes("\u0000")) {
|
|
91
91
|
errors.push(`${field}[${i}] contains a null byte`);
|
|
92
92
|
continue;
|
|
93
93
|
}
|
|
@@ -101,14 +101,14 @@ function ensureStringArray(value, field, errors, warnings) {
|
|
|
101
101
|
function ensurePositiveNumber(value, field, errors) {
|
|
102
102
|
if (value === undefined)
|
|
103
103
|
return;
|
|
104
|
-
if (typeof value !==
|
|
104
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
105
105
|
errors.push(`${field} must be a positive number`);
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
function ensureFiniteNumber(value, field, errors) {
|
|
109
109
|
if (value === undefined)
|
|
110
110
|
return;
|
|
111
|
-
if (typeof value !==
|
|
111
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
112
112
|
errors.push(`${field} must be a finite number`);
|
|
113
113
|
}
|
|
114
114
|
}
|
|
@@ -116,15 +116,15 @@ export function validatePolicy(policy) {
|
|
|
116
116
|
const errors = [];
|
|
117
117
|
const warnings = [];
|
|
118
118
|
if (!isPlainObject(policy)) {
|
|
119
|
-
return { valid: false, errors: [
|
|
119
|
+
return { valid: false, errors: ["Policy must be an object"], warnings: [] };
|
|
120
120
|
}
|
|
121
|
-
ensureAllowedKeys(policy,
|
|
121
|
+
ensureAllowedKeys(policy, "policy", POLICY_KEYS, errors);
|
|
122
122
|
const p = policy;
|
|
123
123
|
if (p.version === undefined) {
|
|
124
124
|
errors.push(`version is required (expected: ${POLICY_SCHEMA_VERSION})`);
|
|
125
125
|
}
|
|
126
|
-
else if (typeof p.version !==
|
|
127
|
-
errors.push(
|
|
126
|
+
else if (typeof p.version !== "string") {
|
|
127
|
+
errors.push("version must be a string");
|
|
128
128
|
}
|
|
129
129
|
else if (SUPPORTED_CANONICAL_VERSIONS.has(p.version)) {
|
|
130
130
|
const canonical = validateCanonicalPolicy(policy);
|
|
@@ -137,49 +137,49 @@ export function validatePolicy(policy) {
|
|
|
137
137
|
else if (p.version !== POLICY_SCHEMA_VERSION) {
|
|
138
138
|
errors.push(`unsupported policy version: ${p.version} (supported: ${POLICY_SCHEMA_VERSION}, 1.1.0, 1.2.0)`);
|
|
139
139
|
}
|
|
140
|
-
if (p.extends !== undefined && typeof p.extends !==
|
|
141
|
-
errors.push(
|
|
140
|
+
if (p.extends !== undefined && typeof p.extends !== "string") {
|
|
141
|
+
errors.push("extends must be a string");
|
|
142
142
|
}
|
|
143
143
|
// Egress validation
|
|
144
144
|
if (p.egress !== undefined) {
|
|
145
145
|
if (!isPlainObject(p.egress)) {
|
|
146
|
-
errors.push(
|
|
146
|
+
errors.push("egress must be an object");
|
|
147
147
|
}
|
|
148
148
|
else {
|
|
149
|
-
ensureAllowedKeys(p.egress,
|
|
149
|
+
ensureAllowedKeys(p.egress, "egress", EGRESS_KEYS, errors);
|
|
150
150
|
const mode = p.egress.mode;
|
|
151
|
-
if (mode !== undefined && (!VALID_EGRESS_MODES.has(mode) || typeof mode !==
|
|
152
|
-
errors.push(`egress.mode must be one of: ${[...VALID_EGRESS_MODES].join(
|
|
151
|
+
if (mode !== undefined && (!VALID_EGRESS_MODES.has(mode) || typeof mode !== "string")) {
|
|
152
|
+
errors.push(`egress.mode must be one of: ${[...VALID_EGRESS_MODES].join(", ")}`);
|
|
153
153
|
}
|
|
154
|
-
const allowed = ensureStringArray(p.egress.allowed_domains,
|
|
155
|
-
if (mode ===
|
|
156
|
-
warnings.push(
|
|
154
|
+
const allowed = ensureStringArray(p.egress.allowed_domains, "egress.allowed_domains", errors);
|
|
155
|
+
if (mode === "allowlist" && allowed && allowed.length === 0) {
|
|
156
|
+
warnings.push("egress.allowlist with empty allowed_domains will deny all egress");
|
|
157
157
|
}
|
|
158
|
-
ensureStringArray(p.egress.denied_domains,
|
|
159
|
-
ensureStringArray(p.egress.allowed_cidrs,
|
|
158
|
+
ensureStringArray(p.egress.denied_domains, "egress.denied_domains", errors);
|
|
159
|
+
ensureStringArray(p.egress.allowed_cidrs, "egress.allowed_cidrs", errors);
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
// Filesystem validation
|
|
163
163
|
if (p.filesystem !== undefined) {
|
|
164
164
|
if (!isPlainObject(p.filesystem)) {
|
|
165
|
-
errors.push(
|
|
165
|
+
errors.push("filesystem must be an object");
|
|
166
166
|
}
|
|
167
167
|
else {
|
|
168
|
-
ensureAllowedKeys(p.filesystem,
|
|
169
|
-
ensureStringArray(p.filesystem.allowed_write_roots,
|
|
170
|
-
ensureStringArray(p.filesystem.allowed_read_paths,
|
|
171
|
-
ensureStringArray(p.filesystem.forbidden_paths,
|
|
168
|
+
ensureAllowedKeys(p.filesystem, "filesystem", FILESYSTEM_KEYS, errors);
|
|
169
|
+
ensureStringArray(p.filesystem.allowed_write_roots, "filesystem.allowed_write_roots", errors);
|
|
170
|
+
ensureStringArray(p.filesystem.allowed_read_paths, "filesystem.allowed_read_paths", errors);
|
|
171
|
+
ensureStringArray(p.filesystem.forbidden_paths, "filesystem.forbidden_paths", errors, warnings);
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
// Execution validation
|
|
175
175
|
if (p.execution !== undefined) {
|
|
176
176
|
if (!isPlainObject(p.execution)) {
|
|
177
|
-
errors.push(
|
|
177
|
+
errors.push("execution must be an object");
|
|
178
178
|
}
|
|
179
179
|
else {
|
|
180
|
-
ensureAllowedKeys(p.execution,
|
|
181
|
-
ensureStringArray(p.execution.allowed_commands,
|
|
182
|
-
const patterns = ensureStringArray(p.execution.denied_patterns,
|
|
180
|
+
ensureAllowedKeys(p.execution, "execution", EXECUTION_KEYS, errors);
|
|
181
|
+
ensureStringArray(p.execution.allowed_commands, "execution.allowed_commands", errors);
|
|
182
|
+
const patterns = ensureStringArray(p.execution.denied_patterns, "execution.denied_patterns", errors);
|
|
183
183
|
if (patterns) {
|
|
184
184
|
for (const pattern of patterns) {
|
|
185
185
|
try {
|
|
@@ -196,95 +196,97 @@ export function validatePolicy(policy) {
|
|
|
196
196
|
// Tool policy validation
|
|
197
197
|
if (p.tools !== undefined) {
|
|
198
198
|
if (!isPlainObject(p.tools)) {
|
|
199
|
-
errors.push(
|
|
199
|
+
errors.push("tools must be an object");
|
|
200
200
|
}
|
|
201
201
|
else {
|
|
202
|
-
ensureAllowedKeys(p.tools,
|
|
203
|
-
ensureStringArray(p.tools.allowed,
|
|
204
|
-
ensureStringArray(p.tools.denied,
|
|
202
|
+
ensureAllowedKeys(p.tools, "tools", TOOLS_KEYS, errors);
|
|
203
|
+
ensureStringArray(p.tools.allowed, "tools.allowed", errors);
|
|
204
|
+
ensureStringArray(p.tools.denied, "tools.denied", errors);
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
// Limits validation
|
|
208
208
|
if (p.limits !== undefined) {
|
|
209
209
|
if (!isPlainObject(p.limits)) {
|
|
210
|
-
errors.push(
|
|
210
|
+
errors.push("limits must be an object");
|
|
211
211
|
}
|
|
212
212
|
else {
|
|
213
|
-
ensureAllowedKeys(p.limits,
|
|
214
|
-
ensurePositiveNumber(p.limits.max_execution_seconds,
|
|
215
|
-
ensurePositiveNumber(p.limits.max_memory_mb,
|
|
216
|
-
ensurePositiveNumber(p.limits.max_output_bytes,
|
|
213
|
+
ensureAllowedKeys(p.limits, "limits", LIMITS_KEYS, errors);
|
|
214
|
+
ensurePositiveNumber(p.limits.max_execution_seconds, "limits.max_execution_seconds", errors);
|
|
215
|
+
ensurePositiveNumber(p.limits.max_memory_mb, "limits.max_memory_mb", errors);
|
|
216
|
+
ensurePositiveNumber(p.limits.max_output_bytes, "limits.max_output_bytes", errors);
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
// Guard toggles validation
|
|
220
220
|
if (p.guards !== undefined) {
|
|
221
221
|
if (!isPlainObject(p.guards)) {
|
|
222
|
-
errors.push(
|
|
222
|
+
errors.push("guards must be an object");
|
|
223
223
|
}
|
|
224
224
|
else {
|
|
225
|
-
ensureAllowedKeys(p.guards,
|
|
226
|
-
ensureBoolean(p.guards.forbidden_path,
|
|
227
|
-
ensureBoolean(p.guards.egress,
|
|
228
|
-
ensureBoolean(p.guards.secret_leak,
|
|
229
|
-
ensureBoolean(p.guards.patch_integrity,
|
|
230
|
-
ensureBoolean(p.guards.mcp_tool,
|
|
225
|
+
ensureAllowedKeys(p.guards, "guards", GUARDS_KEYS, errors);
|
|
226
|
+
ensureBoolean(p.guards.forbidden_path, "guards.forbidden_path", errors);
|
|
227
|
+
ensureBoolean(p.guards.egress, "guards.egress", errors);
|
|
228
|
+
ensureBoolean(p.guards.secret_leak, "guards.secret_leak", errors);
|
|
229
|
+
ensureBoolean(p.guards.patch_integrity, "guards.patch_integrity", errors);
|
|
230
|
+
ensureBoolean(p.guards.mcp_tool, "guards.mcp_tool", errors);
|
|
231
231
|
const computerUse = p.guards.computer_use;
|
|
232
232
|
if (computerUse !== undefined) {
|
|
233
233
|
if (!isPlainObject(computerUse)) {
|
|
234
|
-
errors.push(
|
|
234
|
+
errors.push("guards.computer_use must be an object");
|
|
235
235
|
}
|
|
236
236
|
else {
|
|
237
|
-
ensureAllowedKeys(computerUse,
|
|
238
|
-
ensureBoolean(computerUse.enabled,
|
|
237
|
+
ensureAllowedKeys(computerUse, "guards.computer_use", COMPUTER_USE_KEYS, errors);
|
|
238
|
+
ensureBoolean(computerUse.enabled, "guards.computer_use.enabled", errors);
|
|
239
239
|
const mode = computerUse.mode;
|
|
240
|
-
if (mode !== undefined &&
|
|
241
|
-
|
|
240
|
+
if (mode !== undefined &&
|
|
241
|
+
(typeof mode !== "string" || !VALID_COMPUTER_USE_MODES.has(mode))) {
|
|
242
|
+
errors.push(`guards.computer_use.mode must be one of: ${[...VALID_COMPUTER_USE_MODES].join(", ")}`);
|
|
242
243
|
}
|
|
243
|
-
const allowedActions = ensureStringArray(computerUse.allowed_actions,
|
|
244
|
+
const allowedActions = ensureStringArray(computerUse.allowed_actions, "guards.computer_use.allowed_actions", errors);
|
|
244
245
|
if (allowedActions && allowedActions.length === 0) {
|
|
245
|
-
warnings.push(
|
|
246
|
+
warnings.push("guards.computer_use.allowed_actions is empty (all actions allowed)");
|
|
246
247
|
}
|
|
247
248
|
}
|
|
248
249
|
}
|
|
249
250
|
const remoteSideChannel = p.guards.remote_desktop_side_channel;
|
|
250
251
|
if (remoteSideChannel !== undefined) {
|
|
251
252
|
if (!isPlainObject(remoteSideChannel)) {
|
|
252
|
-
errors.push(
|
|
253
|
+
errors.push("guards.remote_desktop_side_channel must be an object");
|
|
253
254
|
}
|
|
254
255
|
else {
|
|
255
|
-
ensureAllowedKeys(remoteSideChannel,
|
|
256
|
-
ensureBoolean(remoteSideChannel.enabled,
|
|
257
|
-
ensureBoolean(remoteSideChannel.clipboard_enabled,
|
|
258
|
-
ensureBoolean(remoteSideChannel.file_transfer_enabled,
|
|
259
|
-
ensureBoolean(remoteSideChannel.audio_enabled,
|
|
260
|
-
ensureBoolean(remoteSideChannel.drive_mapping_enabled,
|
|
261
|
-
ensureBoolean(remoteSideChannel.printing_enabled,
|
|
262
|
-
ensureBoolean(remoteSideChannel.session_share_enabled,
|
|
263
|
-
ensureFiniteNumber(remoteSideChannel.max_transfer_size_bytes,
|
|
264
|
-
if (typeof remoteSideChannel.max_transfer_size_bytes ===
|
|
265
|
-
|
|
256
|
+
ensureAllowedKeys(remoteSideChannel, "guards.remote_desktop_side_channel", REMOTE_DESKTOP_SIDE_CHANNEL_KEYS, errors);
|
|
257
|
+
ensureBoolean(remoteSideChannel.enabled, "guards.remote_desktop_side_channel.enabled", errors);
|
|
258
|
+
ensureBoolean(remoteSideChannel.clipboard_enabled, "guards.remote_desktop_side_channel.clipboard_enabled", errors);
|
|
259
|
+
ensureBoolean(remoteSideChannel.file_transfer_enabled, "guards.remote_desktop_side_channel.file_transfer_enabled", errors);
|
|
260
|
+
ensureBoolean(remoteSideChannel.audio_enabled, "guards.remote_desktop_side_channel.audio_enabled", errors);
|
|
261
|
+
ensureBoolean(remoteSideChannel.drive_mapping_enabled, "guards.remote_desktop_side_channel.drive_mapping_enabled", errors);
|
|
262
|
+
ensureBoolean(remoteSideChannel.printing_enabled, "guards.remote_desktop_side_channel.printing_enabled", errors);
|
|
263
|
+
ensureBoolean(remoteSideChannel.session_share_enabled, "guards.remote_desktop_side_channel.session_share_enabled", errors);
|
|
264
|
+
ensureFiniteNumber(remoteSideChannel.max_transfer_size_bytes, "guards.remote_desktop_side_channel.max_transfer_size_bytes", errors);
|
|
265
|
+
if (typeof remoteSideChannel.max_transfer_size_bytes === "number" &&
|
|
266
|
+
remoteSideChannel.max_transfer_size_bytes < 0) {
|
|
267
|
+
errors.push("guards.remote_desktop_side_channel.max_transfer_size_bytes must be >= 0");
|
|
266
268
|
}
|
|
267
269
|
}
|
|
268
270
|
}
|
|
269
271
|
const inputInjection = p.guards.input_injection_capability;
|
|
270
272
|
if (inputInjection !== undefined) {
|
|
271
273
|
if (!isPlainObject(inputInjection)) {
|
|
272
|
-
errors.push(
|
|
274
|
+
errors.push("guards.input_injection_capability must be an object");
|
|
273
275
|
}
|
|
274
276
|
else {
|
|
275
|
-
ensureAllowedKeys(inputInjection,
|
|
276
|
-
ensureBoolean(inputInjection.enabled,
|
|
277
|
-
const inputTypes = ensureStringArray(inputInjection.allowed_input_types,
|
|
277
|
+
ensureAllowedKeys(inputInjection, "guards.input_injection_capability", INPUT_INJECTION_CAPABILITY_KEYS, errors);
|
|
278
|
+
ensureBoolean(inputInjection.enabled, "guards.input_injection_capability.enabled", errors);
|
|
279
|
+
const inputTypes = ensureStringArray(inputInjection.allowed_input_types, "guards.input_injection_capability.allowed_input_types", errors);
|
|
278
280
|
if (inputTypes && inputTypes.length === 0) {
|
|
279
|
-
warnings.push(
|
|
281
|
+
warnings.push("guards.input_injection_capability.allowed_input_types is empty (all input types allowed)");
|
|
280
282
|
}
|
|
281
|
-
ensureBoolean(inputInjection.require_postcondition_probe,
|
|
283
|
+
ensureBoolean(inputInjection.require_postcondition_probe, "guards.input_injection_capability.require_postcondition_probe", errors);
|
|
282
284
|
}
|
|
283
285
|
}
|
|
284
286
|
const custom = p.guards.custom;
|
|
285
287
|
if (custom !== undefined) {
|
|
286
288
|
if (!Array.isArray(custom)) {
|
|
287
|
-
errors.push(
|
|
289
|
+
errors.push("guards.custom must be an array");
|
|
288
290
|
}
|
|
289
291
|
else {
|
|
290
292
|
for (let i = 0; i < custom.length; i++) {
|
|
@@ -295,17 +297,17 @@ export function validatePolicy(policy) {
|
|
|
295
297
|
}
|
|
296
298
|
}
|
|
297
299
|
// Validate placeholders across the entire policy tree (fail closed on missing env).
|
|
298
|
-
validatePlaceholders(policy,
|
|
300
|
+
validatePlaceholders(policy, "policy", errors);
|
|
299
301
|
// on_violation validation
|
|
300
302
|
if (p.on_violation !== undefined) {
|
|
301
|
-
if (typeof p.on_violation !==
|
|
302
|
-
errors.push(`on_violation must be one of: ${[...VALID_VIOLATION_ACTIONS].join(
|
|
303
|
+
if (typeof p.on_violation !== "string") {
|
|
304
|
+
errors.push(`on_violation must be one of: ${[...VALID_VIOLATION_ACTIONS].join(", ")}`);
|
|
303
305
|
}
|
|
304
306
|
else if (UNIMPLEMENTED_VIOLATION_ACTIONS.has(p.on_violation)) {
|
|
305
307
|
warnings.push(`on_violation value '${p.on_violation}' is not yet implemented; use 'cancel' or 'warn'`);
|
|
306
308
|
}
|
|
307
309
|
else if (!VALID_VIOLATION_ACTIONS.has(p.on_violation)) {
|
|
308
|
-
errors.push(`on_violation must be one of: ${[...VALID_VIOLATION_ACTIONS].join(
|
|
310
|
+
errors.push(`on_violation must be one of: ${[...VALID_VIOLATION_ACTIONS].join(", ")}`);
|
|
309
311
|
}
|
|
310
312
|
}
|
|
311
313
|
return { valid: errors.length === 0, errors, warnings };
|
|
@@ -316,7 +318,7 @@ function validateCustomGuardSpec(value, base, errors) {
|
|
|
316
318
|
return;
|
|
317
319
|
}
|
|
318
320
|
const pkg = value.package;
|
|
319
|
-
if (typeof pkg !==
|
|
321
|
+
if (typeof pkg !== "string" || pkg.trim() === "") {
|
|
320
322
|
errors.push(`${base}.package must be a non-empty string`);
|
|
321
323
|
return;
|
|
322
324
|
}
|
|
@@ -325,7 +327,7 @@ function validateCustomGuardSpec(value, base, errors) {
|
|
|
325
327
|
return;
|
|
326
328
|
}
|
|
327
329
|
const enabled = value.enabled;
|
|
328
|
-
if (enabled !== undefined && typeof enabled !==
|
|
330
|
+
if (enabled !== undefined && typeof enabled !== "boolean") {
|
|
329
331
|
errors.push(`${base}.enabled must be a boolean`);
|
|
330
332
|
}
|
|
331
333
|
const config = value.config;
|
|
@@ -334,14 +336,14 @@ function validateCustomGuardSpec(value, base, errors) {
|
|
|
334
336
|
return;
|
|
335
337
|
}
|
|
336
338
|
const cfg = (isPlainObject(config) ? config : {});
|
|
337
|
-
if (pkg ===
|
|
339
|
+
if (pkg === "clawdstrike-virustotal") {
|
|
338
340
|
requireString(cfg, `${base}.config.api_key`, errors);
|
|
339
341
|
}
|
|
340
|
-
else if (pkg ===
|
|
342
|
+
else if (pkg === "clawdstrike-safe-browsing") {
|
|
341
343
|
requireString(cfg, `${base}.config.api_key`, errors);
|
|
342
344
|
requireString(cfg, `${base}.config.client_id`, errors);
|
|
343
345
|
}
|
|
344
|
-
else if (pkg ===
|
|
346
|
+
else if (pkg === "clawdstrike-snyk") {
|
|
345
347
|
requireString(cfg, `${base}.config.api_token`, errors);
|
|
346
348
|
requireString(cfg, `${base}.config.org_id`, errors);
|
|
347
349
|
}
|
|
@@ -356,16 +358,18 @@ function validateAsyncConfig(value, base, errors) {
|
|
|
356
358
|
return;
|
|
357
359
|
}
|
|
358
360
|
const timeoutMs = value.timeout_ms;
|
|
359
|
-
if (timeoutMs !== undefined &&
|
|
361
|
+
if (timeoutMs !== undefined &&
|
|
362
|
+
(!isFiniteNumber(timeoutMs) || timeoutMs < 100 || timeoutMs > 300_000)) {
|
|
360
363
|
errors.push(`${base}.timeout_ms must be between 100 and 300000`);
|
|
361
364
|
}
|
|
362
365
|
const onTimeout = value.on_timeout;
|
|
363
|
-
if (onTimeout !== undefined &&
|
|
364
|
-
|
|
366
|
+
if (onTimeout !== undefined &&
|
|
367
|
+
(typeof onTimeout !== "string" || !VALID_TIMEOUT_BEHAVIORS.has(onTimeout))) {
|
|
368
|
+
errors.push(`${base}.on_timeout must be one of: ${[...VALID_TIMEOUT_BEHAVIORS].join(", ")}`);
|
|
365
369
|
}
|
|
366
370
|
const mode = value.execution_mode;
|
|
367
|
-
if (mode !== undefined && (typeof mode !==
|
|
368
|
-
errors.push(`${base}.execution_mode must be one of: ${[...VALID_EXECUTION_MODES].join(
|
|
371
|
+
if (mode !== undefined && (typeof mode !== "string" || !VALID_EXECUTION_MODES.has(mode))) {
|
|
372
|
+
errors.push(`${base}.execution_mode must be one of: ${[...VALID_EXECUTION_MODES].join(", ")}`);
|
|
369
373
|
}
|
|
370
374
|
if (value.rate_limit !== undefined) {
|
|
371
375
|
if (!isPlainObject(value.rate_limit)) {
|
|
@@ -385,7 +389,8 @@ function validateAsyncConfig(value, base, errors) {
|
|
|
385
389
|
errors.push(`${base}.rate_limit must specify only one of requests_per_second or requests_per_minute`);
|
|
386
390
|
}
|
|
387
391
|
const burst = rl.burst;
|
|
388
|
-
if (burst !== undefined &&
|
|
392
|
+
if (burst !== undefined &&
|
|
393
|
+
(typeof burst !== "number" || !Number.isInteger(burst) || burst < 1)) {
|
|
389
394
|
errors.push(`${base}.rate_limit.burst must be >= 1`);
|
|
390
395
|
}
|
|
391
396
|
}
|
|
@@ -397,11 +402,11 @@ function validateAsyncConfig(value, base, errors) {
|
|
|
397
402
|
else {
|
|
398
403
|
const cache = value.cache;
|
|
399
404
|
const ttl = cache.ttl_seconds;
|
|
400
|
-
if (ttl !== undefined && (typeof ttl !==
|
|
405
|
+
if (ttl !== undefined && (typeof ttl !== "number" || !Number.isInteger(ttl) || ttl < 1)) {
|
|
401
406
|
errors.push(`${base}.cache.ttl_seconds must be >= 1`);
|
|
402
407
|
}
|
|
403
408
|
const max = cache.max_size_mb;
|
|
404
|
-
if (max !== undefined && (typeof max !==
|
|
409
|
+
if (max !== undefined && (typeof max !== "number" || !Number.isInteger(max) || max < 1)) {
|
|
405
410
|
errors.push(`${base}.cache.max_size_mb must be >= 1`);
|
|
406
411
|
}
|
|
407
412
|
}
|
|
@@ -413,15 +418,16 @@ function validateAsyncConfig(value, base, errors) {
|
|
|
413
418
|
else {
|
|
414
419
|
const cb = value.circuit_breaker;
|
|
415
420
|
const f = cb.failure_threshold;
|
|
416
|
-
if (f !== undefined && (typeof f !==
|
|
421
|
+
if (f !== undefined && (typeof f !== "number" || !Number.isInteger(f) || f < 1)) {
|
|
417
422
|
errors.push(`${base}.circuit_breaker.failure_threshold must be >= 1`);
|
|
418
423
|
}
|
|
419
424
|
const reset = cb.reset_timeout_ms;
|
|
420
|
-
if (reset !== undefined &&
|
|
425
|
+
if (reset !== undefined &&
|
|
426
|
+
(typeof reset !== "number" || !Number.isInteger(reset) || reset < 1000)) {
|
|
421
427
|
errors.push(`${base}.circuit_breaker.reset_timeout_ms must be >= 1000`);
|
|
422
428
|
}
|
|
423
429
|
const s = cb.success_threshold;
|
|
424
|
-
if (s !== undefined && (typeof s !==
|
|
430
|
+
if (s !== undefined && (typeof s !== "number" || !Number.isInteger(s) || s < 1)) {
|
|
425
431
|
errors.push(`${base}.circuit_breaker.success_threshold must be >= 1`);
|
|
426
432
|
}
|
|
427
433
|
}
|
|
@@ -437,30 +443,31 @@ function validateAsyncConfig(value, base, errors) {
|
|
|
437
443
|
errors.push(`${base}.retry.multiplier must be >= 1`);
|
|
438
444
|
}
|
|
439
445
|
const init = retry.initial_backoff_ms;
|
|
440
|
-
if (init !== undefined &&
|
|
446
|
+
if (init !== undefined &&
|
|
447
|
+
(typeof init !== "number" || !Number.isInteger(init) || init < 100)) {
|
|
441
448
|
errors.push(`${base}.retry.initial_backoff_ms must be >= 100`);
|
|
442
449
|
}
|
|
443
450
|
const max = retry.max_backoff_ms;
|
|
444
|
-
if (max !== undefined && (typeof max !==
|
|
451
|
+
if (max !== undefined && (typeof max !== "number" || !Number.isInteger(max) || max < 100)) {
|
|
445
452
|
errors.push(`${base}.retry.max_backoff_ms must be >= 100`);
|
|
446
453
|
}
|
|
447
|
-
if (typeof init ===
|
|
454
|
+
if (typeof init === "number" && typeof max === "number" && max < init) {
|
|
448
455
|
errors.push(`${base}.retry.max_backoff_ms must be >= initial_backoff_ms`);
|
|
449
456
|
}
|
|
450
457
|
}
|
|
451
458
|
}
|
|
452
459
|
}
|
|
453
460
|
function requireString(obj, field, errors) {
|
|
454
|
-
const key = field.split(
|
|
461
|
+
const key = field.split(".").slice(-1)[0] ?? "";
|
|
455
462
|
const value = obj[key];
|
|
456
|
-
if (typeof value !==
|
|
463
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
457
464
|
errors.push(`${field} missing/invalid required string`);
|
|
458
465
|
}
|
|
459
466
|
}
|
|
460
467
|
function validatePlaceholders(value, base, errors) {
|
|
461
|
-
if (typeof value ===
|
|
468
|
+
if (typeof value === "string") {
|
|
462
469
|
for (const match of value.matchAll(PLACEHOLDER_RE)) {
|
|
463
|
-
const raw = match[1] ??
|
|
470
|
+
const raw = match[1] ?? "";
|
|
464
471
|
const envName = envVarForPlaceholder(raw);
|
|
465
472
|
if (!envName.ok) {
|
|
466
473
|
errors.push(`${base}: ${envName.error}`);
|
|
@@ -485,19 +492,19 @@ function validatePlaceholders(value, base, errors) {
|
|
|
485
492
|
}
|
|
486
493
|
}
|
|
487
494
|
function envVarForPlaceholder(raw) {
|
|
488
|
-
if (raw.startsWith(
|
|
489
|
-
const name = raw.slice(
|
|
495
|
+
if (raw.startsWith("secrets.")) {
|
|
496
|
+
const name = raw.slice("secrets.".length);
|
|
490
497
|
if (!name) {
|
|
491
|
-
return { ok: false, error:
|
|
498
|
+
return { ok: false, error: "placeholder ${secrets.} is invalid" };
|
|
492
499
|
}
|
|
493
500
|
return { ok: true, value: name };
|
|
494
501
|
}
|
|
495
502
|
if (!raw) {
|
|
496
|
-
return { ok: false, error:
|
|
503
|
+
return { ok: false, error: "placeholder ${} is invalid" };
|
|
497
504
|
}
|
|
498
505
|
return { ok: true, value: raw };
|
|
499
506
|
}
|
|
500
507
|
function isFiniteNumber(value) {
|
|
501
|
-
return typeof value ===
|
|
508
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
502
509
|
}
|
|
503
510
|
//# sourceMappingURL=validator.js.map
|