@drakon-systems/shieldcortex-realtime 4.45.1 → 4.46.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/dist/index.js +1 -1
- package/dist/interceptor.js +31 -4
- package/dist/openclaw.plugin.json +1 -1
- package/index.ts +1 -1
- package/interceptor.ts +44 -8
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -734,7 +734,7 @@ export default {
|
|
|
734
734
|
enabled: rawInterceptorConfig.enabled ?? DEFAULT_INTERCEPTOR_CONFIG.enabled,
|
|
735
735
|
severityActions: { ...DEFAULT_INTERCEPTOR_CONFIG.severityActions, ...rawInterceptorConfig.severityActions },
|
|
736
736
|
failurePolicy: { ...DEFAULT_INTERCEPTOR_CONFIG.failurePolicy, ...rawInterceptorConfig.failurePolicy },
|
|
737
|
-
actionGuard: { ...(DEFAULT_INTERCEPTOR_CONFIG.actionGuard ?? { enabled: true, enforce:
|
|
737
|
+
actionGuard: { ...(DEFAULT_INTERCEPTOR_CONFIG.actionGuard ?? { enabled: true, enforce: true, autoApprove: [] }), ...(rawInterceptorConfig.actionGuard ?? {}) },
|
|
738
738
|
} : {}),
|
|
739
739
|
logger: { info: api.logger?.info ?? console.log, warn: api.logger?.warn ?? console.warn },
|
|
740
740
|
};
|
package/dist/interceptor.js
CHANGED
|
@@ -27,10 +27,14 @@ const DEFAULT_CONFIG = {
|
|
|
27
27
|
critical: 'deny',
|
|
28
28
|
},
|
|
29
29
|
// Action Guard on by default: catastrophic ops are blocked out of the box;
|
|
30
|
-
// dangerous ops are
|
|
30
|
+
// recognised-dangerous ops are ENFORCED by default (P1/WS1) — attended → prompt,
|
|
31
|
+
// unattended → fail closed on failurePolicy. Populate `autoApprove` per agent to
|
|
32
|
+
// pre-approve the dangerous ops it legitimately needs unattended; set
|
|
33
|
+
// `enforce:false` to opt back down to warn-and-allow.
|
|
31
34
|
actionGuard: {
|
|
32
35
|
enabled: true,
|
|
33
|
-
enforce:
|
|
36
|
+
enforce: true,
|
|
37
|
+
autoApprove: [],
|
|
34
38
|
},
|
|
35
39
|
};
|
|
36
40
|
export { WATCHED_TOOLS, CONTENT_FIELDS, DEFAULT_CONFIG };
|
|
@@ -227,7 +231,7 @@ export function createInterceptor(config, pipeline, options) {
|
|
|
227
231
|
const rateLimiter = new RateLimiter(options?.maxPromptsPerMinute ?? 5);
|
|
228
232
|
const log = config.logger ?? { info: console.log, warn: console.warn };
|
|
229
233
|
const onAuditEntry = options?.onAuditEntry;
|
|
230
|
-
const actionGuardCfg = config.actionGuard ?? { enabled: true, enforce:
|
|
234
|
+
const actionGuardCfg = config.actionGuard ?? { enabled: true, enforce: true, autoApprove: [] };
|
|
231
235
|
const evaluateToolCall = options?.evaluateToolCall;
|
|
232
236
|
function emitAudit(entry) {
|
|
233
237
|
writeAuditEntry(entry);
|
|
@@ -272,16 +276,39 @@ export function createInterceptor(config, pipeline, options) {
|
|
|
272
276
|
log.warn(`[shieldcortex] action-guard BLOCKED ${context.toolName}: ${v.reason} [${v.signals.join(", ")}]`);
|
|
273
277
|
throw new Error(`ShieldCortex: tool call blocked — ${v.reason}`);
|
|
274
278
|
}
|
|
275
|
-
//
|
|
279
|
+
// Per-agent autoApprove allowlist: a recognised-dangerous op whose family,
|
|
280
|
+
// action, or signal is pre-approved passes without gating. This is the escape
|
|
281
|
+
// hatch that lets enforce-by-default coexist with unattended agents doing
|
|
282
|
+
// legitimate dangerous work. It NEVER applies to catastrophic ops — those
|
|
283
|
+
// hard-block above, before this branch is reached.
|
|
284
|
+
const autoApprove = actionGuardCfg.autoApprove ?? [];
|
|
285
|
+
if (autoApprove.length > 0) {
|
|
286
|
+
const hay = [v.family, v.action, ...v.signals].map(s => String(s).toLowerCase());
|
|
287
|
+
const matched = autoApprove.some(a => {
|
|
288
|
+
const n = a.toLowerCase();
|
|
289
|
+
return hay.some(h => h === n || h.includes(n));
|
|
290
|
+
});
|
|
291
|
+
if (matched) {
|
|
292
|
+
emitAudit({ ...base, action: 'require_approval', outcome: 'approved' });
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// require_approval — ENFORCED by default (P1/WS1). `enforce:false` opts back
|
|
297
|
+
// down to warn-and-allow (advisory) for operators who want the old behaviour.
|
|
276
298
|
if (!actionGuardCfg.enforce) {
|
|
277
299
|
log.warn(`[shieldcortex] ⚠️ Action Guard: ${context.toolName} — ${v.reason}`);
|
|
278
300
|
emitAudit({ ...base, action: 'warn', outcome: 'warned' });
|
|
279
301
|
return;
|
|
280
302
|
}
|
|
281
303
|
if (typeof context.requireApproval !== 'function') {
|
|
304
|
+
// Unattended (no approver, e.g. cron/heartbeat): fail closed on the failure
|
|
305
|
+
// policy. High-severity dangerous defaults to deny — surfaced loudly to the
|
|
306
|
+
// gateway log so an operator sees it, because a silent no-op is exactly the
|
|
307
|
+
// failure mode we are eliminating.
|
|
282
308
|
const failAction = config.failurePolicy[severity];
|
|
283
309
|
emitAudit({ ...base, action: 'require_approval', outcome: failAction === 'deny' ? 'failure_denied' : 'failure_allowed' });
|
|
284
310
|
if (failAction === 'deny') {
|
|
311
|
+
log.warn(`[shieldcortex] action-guard DENIED (unattended, no approver) ${context.toolName}: ${v.reason} [${v.signals.join(", ")}]`);
|
|
285
312
|
throw new Error(`ShieldCortex: tool call blocked — ${v.reason} (no approver, failure policy: deny)`);
|
|
286
313
|
}
|
|
287
314
|
return;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "shieldcortex-realtime",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.46.0",
|
|
4
4
|
"name": "ShieldCortex Real-time Scanner",
|
|
5
5
|
"description": "Real-time defence scanning on LLM input, memory extraction on LLM output, and active tool call interception with approval gating.",
|
|
6
6
|
"kind": null,
|
package/index.ts
CHANGED
|
@@ -880,7 +880,7 @@ export default {
|
|
|
880
880
|
enabled: rawInterceptorConfig.enabled ?? DEFAULT_INTERCEPTOR_CONFIG.enabled,
|
|
881
881
|
severityActions: { ...DEFAULT_INTERCEPTOR_CONFIG.severityActions, ...rawInterceptorConfig.severityActions },
|
|
882
882
|
failurePolicy: { ...DEFAULT_INTERCEPTOR_CONFIG.failurePolicy, ...rawInterceptorConfig.failurePolicy },
|
|
883
|
-
actionGuard: { ...(DEFAULT_INTERCEPTOR_CONFIG.actionGuard ?? { enabled: true, enforce:
|
|
883
|
+
actionGuard: { ...(DEFAULT_INTERCEPTOR_CONFIG.actionGuard ?? { enabled: true, enforce: true, autoApprove: [] }), ...(rawInterceptorConfig.actionGuard ?? {}) },
|
|
884
884
|
} : {}),
|
|
885
885
|
logger: { info: api.logger?.info ?? console.log, warn: (api.logger as any)?.warn ?? console.warn },
|
|
886
886
|
};
|
package/interceptor.ts
CHANGED
|
@@ -10,14 +10,22 @@ export type FailureAction = 'allow' | 'deny';
|
|
|
10
10
|
/**
|
|
11
11
|
* Action Guard config — gates what the agent DOES (shell/file/network/git),
|
|
12
12
|
* not just what it remembers. Catastrophic operations (rm -rf /, fork bombs,
|
|
13
|
-
* disk wipes, secret exfil) are always blocked when `enabled
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* disk wipes, secret exfil) are always blocked when `enabled`.
|
|
14
|
+
*
|
|
15
|
+
* Recognised-dangerous ops (`rm <path>`, `sudo`, force-push, external egress,
|
|
16
|
+
* touching secret paths) are ENFORCED by default (P1/WS1): with an approver in
|
|
17
|
+
* the loop they prompt; unattended (no approver) they fail closed on the failure
|
|
18
|
+
* policy. `enforce:false` opts back down to warn-and-allow (advisory).
|
|
19
|
+
*
|
|
20
|
+
* `autoApprove` is the per-agent escape hatch: a dangerous op whose family,
|
|
21
|
+
* action, or signal matches an entry passes without gating — so enforce-by-default
|
|
22
|
+
* does not break unattended agents doing legitimate dangerous work. It NEVER
|
|
23
|
+
* relaxes catastrophic ops (those hard-block regardless).
|
|
17
24
|
*/
|
|
18
25
|
export interface ActionGuardConfig {
|
|
19
26
|
enabled: boolean;
|
|
20
27
|
enforce: boolean;
|
|
28
|
+
autoApprove?: string[];
|
|
21
29
|
}
|
|
22
30
|
|
|
23
31
|
/** Structural shape of a Tool Action Guard verdict (kept local to avoid a
|
|
@@ -91,10 +99,14 @@ const DEFAULT_CONFIG: InterceptorConfig = {
|
|
|
91
99
|
critical: 'deny',
|
|
92
100
|
},
|
|
93
101
|
// Action Guard on by default: catastrophic ops are blocked out of the box;
|
|
94
|
-
// dangerous ops are
|
|
102
|
+
// recognised-dangerous ops are ENFORCED by default (P1/WS1) — attended → prompt,
|
|
103
|
+
// unattended → fail closed on failurePolicy. Populate `autoApprove` per agent to
|
|
104
|
+
// pre-approve the dangerous ops it legitimately needs unattended; set
|
|
105
|
+
// `enforce:false` to opt back down to warn-and-allow.
|
|
95
106
|
actionGuard: {
|
|
96
107
|
enabled: true,
|
|
97
|
-
enforce:
|
|
108
|
+
enforce: true,
|
|
109
|
+
autoApprove: [],
|
|
98
110
|
},
|
|
99
111
|
};
|
|
100
112
|
|
|
@@ -372,7 +384,7 @@ export function createInterceptor(
|
|
|
372
384
|
const rateLimiter = new RateLimiter(options?.maxPromptsPerMinute ?? 5);
|
|
373
385
|
const log = config.logger ?? { info: console.log, warn: console.warn };
|
|
374
386
|
const onAuditEntry = options?.onAuditEntry;
|
|
375
|
-
const actionGuardCfg: ActionGuardConfig = config.actionGuard ?? { enabled: true, enforce:
|
|
387
|
+
const actionGuardCfg: ActionGuardConfig = config.actionGuard ?? { enabled: true, enforce: true, autoApprove: [] };
|
|
376
388
|
const evaluateToolCall = options?.evaluateToolCall;
|
|
377
389
|
|
|
378
390
|
function emitAudit(entry: InterceptAuditEntry): void {
|
|
@@ -421,7 +433,26 @@ export function createInterceptor(
|
|
|
421
433
|
throw new Error(`ShieldCortex: tool call blocked — ${v.reason}`);
|
|
422
434
|
}
|
|
423
435
|
|
|
424
|
-
//
|
|
436
|
+
// Per-agent autoApprove allowlist: a recognised-dangerous op whose family,
|
|
437
|
+
// action, or signal is pre-approved passes without gating. This is the escape
|
|
438
|
+
// hatch that lets enforce-by-default coexist with unattended agents doing
|
|
439
|
+
// legitimate dangerous work. It NEVER applies to catastrophic ops — those
|
|
440
|
+
// hard-block above, before this branch is reached.
|
|
441
|
+
const autoApprove = actionGuardCfg.autoApprove ?? [];
|
|
442
|
+
if (autoApprove.length > 0) {
|
|
443
|
+
const hay = [v.family, v.action, ...v.signals].map(s => String(s).toLowerCase());
|
|
444
|
+
const matched = autoApprove.some(a => {
|
|
445
|
+
const n = a.toLowerCase();
|
|
446
|
+
return hay.some(h => h === n || h.includes(n));
|
|
447
|
+
});
|
|
448
|
+
if (matched) {
|
|
449
|
+
emitAudit({ ...base, action: 'require_approval', outcome: 'approved' });
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// require_approval — ENFORCED by default (P1/WS1). `enforce:false` opts back
|
|
455
|
+
// down to warn-and-allow (advisory) for operators who want the old behaviour.
|
|
425
456
|
if (!actionGuardCfg.enforce) {
|
|
426
457
|
log.warn(`[shieldcortex] ⚠️ Action Guard: ${context.toolName} — ${v.reason}`);
|
|
427
458
|
emitAudit({ ...base, action: 'warn', outcome: 'warned' });
|
|
@@ -429,9 +460,14 @@ export function createInterceptor(
|
|
|
429
460
|
}
|
|
430
461
|
|
|
431
462
|
if (typeof context.requireApproval !== 'function') {
|
|
463
|
+
// Unattended (no approver, e.g. cron/heartbeat): fail closed on the failure
|
|
464
|
+
// policy. High-severity dangerous defaults to deny — surfaced loudly to the
|
|
465
|
+
// gateway log so an operator sees it, because a silent no-op is exactly the
|
|
466
|
+
// failure mode we are eliminating.
|
|
432
467
|
const failAction = config.failurePolicy[severity];
|
|
433
468
|
emitAudit({ ...base, action: 'require_approval', outcome: failAction === 'deny' ? 'failure_denied' : 'failure_allowed' });
|
|
434
469
|
if (failAction === 'deny') {
|
|
470
|
+
log.warn(`[shieldcortex] action-guard DENIED (unattended, no approver) ${context.toolName}: ${v.reason} [${v.signals.join(", ")}]`);
|
|
435
471
|
throw new Error(`ShieldCortex: tool call blocked — ${v.reason} (no approver, failure policy: deny)`);
|
|
436
472
|
}
|
|
437
473
|
return;
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "shieldcortex-realtime",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.46.0",
|
|
4
4
|
"name": "ShieldCortex Real-time Scanner",
|
|
5
5
|
"description": "Real-time defence scanning on LLM input, memory extraction on LLM output, and active tool call interception with approval gating.",
|
|
6
6
|
"kind": null,
|
package/package.json
CHANGED