@safefence/openclaw-guardrails 0.3.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 (73) hide show
  1. package/README.md +182 -0
  2. package/dist/core/approval-store.d.ts +29 -0
  3. package/dist/core/approval-store.js +124 -0
  4. package/dist/core/approval.d.ts +18 -0
  5. package/dist/core/approval.js +129 -0
  6. package/dist/core/authorization.d.ts +7 -0
  7. package/dist/core/authorization.js +114 -0
  8. package/dist/core/budget-store.d.ts +6 -0
  9. package/dist/core/budget-store.js +33 -0
  10. package/dist/core/command-parse.d.ts +11 -0
  11. package/dist/core/command-parse.js +67 -0
  12. package/dist/core/detectors/budget-detector.d.ts +4 -0
  13. package/dist/core/detectors/budget-detector.js +35 -0
  14. package/dist/core/detectors/command-policy-detector.d.ts +3 -0
  15. package/dist/core/detectors/command-policy-detector.js +74 -0
  16. package/dist/core/detectors/index.d.ts +11 -0
  17. package/dist/core/detectors/index.js +11 -0
  18. package/dist/core/detectors/input-intent-detector.d.ts +3 -0
  19. package/dist/core/detectors/input-intent-detector.js +55 -0
  20. package/dist/core/detectors/network-egress-detector.d.ts +3 -0
  21. package/dist/core/detectors/network-egress-detector.js +68 -0
  22. package/dist/core/detectors/output-safety-detector.d.ts +2 -0
  23. package/dist/core/detectors/output-safety-detector.js +36 -0
  24. package/dist/core/detectors/owner-approval-detector.d.ts +4 -0
  25. package/dist/core/detectors/owner-approval-detector.js +62 -0
  26. package/dist/core/detectors/path-canonical-detector.d.ts +3 -0
  27. package/dist/core/detectors/path-canonical-detector.js +46 -0
  28. package/dist/core/detectors/principal-authz-detector.d.ts +3 -0
  29. package/dist/core/detectors/principal-authz-detector.js +14 -0
  30. package/dist/core/detectors/provenance-detector.d.ts +3 -0
  31. package/dist/core/detectors/provenance-detector.js +8 -0
  32. package/dist/core/detectors/restricted-info-detector.d.ts +2 -0
  33. package/dist/core/detectors/restricted-info-detector.js +50 -0
  34. package/dist/core/detectors/sensitive-data-detector.d.ts +2 -0
  35. package/dist/core/detectors/sensitive-data-detector.js +55 -0
  36. package/dist/core/detectors/types.d.ts +20 -0
  37. package/dist/core/detectors/types.js +1 -0
  38. package/dist/core/engine.d.ts +12 -0
  39. package/dist/core/engine.js +123 -0
  40. package/dist/core/event-utils.d.ts +10 -0
  41. package/dist/core/event-utils.js +105 -0
  42. package/dist/core/identity.d.ts +8 -0
  43. package/dist/core/identity.js +102 -0
  44. package/dist/core/network-guard.d.ts +5 -0
  45. package/dist/core/network-guard.js +134 -0
  46. package/dist/core/normalize.d.ts +2 -0
  47. package/dist/core/normalize.js +60 -0
  48. package/dist/core/path-canonical.d.ts +9 -0
  49. package/dist/core/path-canonical.js +69 -0
  50. package/dist/core/reason-codes.d.ts +43 -0
  51. package/dist/core/reason-codes.js +42 -0
  52. package/dist/core/retrieval-trust.d.ts +2 -0
  53. package/dist/core/retrieval-trust.js +65 -0
  54. package/dist/core/scoring.d.ts +2 -0
  55. package/dist/core/scoring.js +18 -0
  56. package/dist/core/supply-chain.d.ts +2 -0
  57. package/dist/core/supply-chain.js +78 -0
  58. package/dist/core/types.d.ts +149 -0
  59. package/dist/core/types.js +1 -0
  60. package/dist/index.d.ts +7 -0
  61. package/dist/index.js +6 -0
  62. package/dist/plugin/openclaw-adapter.d.ts +41 -0
  63. package/dist/plugin/openclaw-adapter.js +313 -0
  64. package/dist/plugin/openclaw-extension.d.ts +14 -0
  65. package/dist/plugin/openclaw-extension.js +62 -0
  66. package/dist/redaction/redact.d.ts +6 -0
  67. package/dist/redaction/redact.js +31 -0
  68. package/dist/rules/default-policy.d.ts +3 -0
  69. package/dist/rules/default-policy.js +200 -0
  70. package/dist/rules/patterns.d.ts +8 -0
  71. package/dist/rules/patterns.js +64 -0
  72. package/openclaw.plugin.json +147 -0
  73. package/package.json +52 -0
@@ -0,0 +1,43 @@
1
+ export declare const REASON_CODES: {
2
+ readonly AUDIT_WOULD_DENY: "AUDIT_WOULD_DENY";
3
+ readonly AUDIT_WOULD_REDACT: "AUDIT_WOULD_REDACT";
4
+ readonly BUDGET_REQUEST_EXCEEDED: "BUDGET_REQUEST_EXCEEDED";
5
+ readonly BUDGET_TOOL_CALL_EXCEEDED: "BUDGET_TOOL_CALL_EXCEEDED";
6
+ readonly COMMAND_ARG_PATTERN_BLOCKED: "COMMAND_ARG_PATTERN_BLOCKED";
7
+ readonly COMMAND_BINARY_NOT_ALLOWED: "COMMAND_BINARY_NOT_ALLOWED";
8
+ readonly COMMAND_SHELL_OPERATOR_BLOCKED: "COMMAND_SHELL_OPERATOR_BLOCKED";
9
+ readonly DESTRUCTIVE_COMMAND: "DESTRUCTIVE_COMMAND";
10
+ readonly ENGINE_FAILURE: "ENGINE_FAILURE";
11
+ readonly ENGINE_FAILURE_FAIL_OPEN: "ENGINE_FAILURE_FAIL_OPEN";
12
+ readonly EXFIL_PATTERN: "EXFIL_PATTERN";
13
+ readonly INPUT_LIMIT_EXCEEDED: "INPUT_LIMIT_EXCEEDED";
14
+ readonly INVALID_NETWORK_HOST: "INVALID_NETWORK_HOST";
15
+ readonly INVALID_NETWORK_URL: "INVALID_NETWORK_URL";
16
+ readonly NETWORK_HOST_BLOCKED: "NETWORK_HOST_BLOCKED";
17
+ readonly NETWORK_PRIVATE_BLOCKED: "NETWORK_PRIVATE_BLOCKED";
18
+ readonly PATH_OUTSIDE_WORKSPACE: "PATH_OUTSIDE_WORKSPACE";
19
+ readonly PATH_SYMLINK_TRAVERSAL: "PATH_SYMLINK_TRAVERSAL";
20
+ readonly PATH_TRAVERSAL: "PATH_TRAVERSAL";
21
+ readonly PII_DETECTED: "PII_DETECTED";
22
+ readonly PRINCIPAL_CONTEXT_MISSING: "PRINCIPAL_CONTEXT_MISSING";
23
+ readonly PROMPT_INJECTION: "PROMPT_INJECTION";
24
+ readonly GROUP_SENDER_NOT_ALLOWED: "GROUP_SENDER_NOT_ALLOWED";
25
+ readonly ROLE_TOOL_NOT_ALLOWED: "ROLE_TOOL_NOT_ALLOWED";
26
+ readonly ROLLOUT_AUDIT_OVERRIDE: "ROLLOUT_AUDIT_OVERRIDE";
27
+ readonly RESTRICTED_INFO_ROLE_BLOCKED: "RESTRICTED_INFO_ROLE_BLOCKED";
28
+ readonly OWNER_APPROVAL_REQUIRED: "OWNER_APPROVAL_REQUIRED";
29
+ readonly OWNER_APPROVAL_INVALID: "OWNER_APPROVAL_INVALID";
30
+ readonly OWNER_APPROVAL_EXPIRED: "OWNER_APPROVAL_EXPIRED";
31
+ readonly OWNER_APPROVAL_REPLAYED: "OWNER_APPROVAL_REPLAYED";
32
+ readonly RETRIEVAL_SIGNATURE_INVALID: "RETRIEVAL_SIGNATURE_INVALID";
33
+ readonly RETRIEVAL_TRUST_LEVEL_TOO_LOW: "RETRIEVAL_TRUST_LEVEL_TOO_LOW";
34
+ readonly RETRIEVAL_TRUST_REQUIRED: "RETRIEVAL_TRUST_REQUIRED";
35
+ readonly SECRET_DETECTED: "SECRET_DETECTED";
36
+ readonly SUPPLY_CHAIN_HASH_BLOCKED: "SUPPLY_CHAIN_HASH_BLOCKED";
37
+ readonly SUPPLY_CHAIN_HASH_REQUIRED: "SUPPLY_CHAIN_HASH_REQUIRED";
38
+ readonly SUPPLY_CHAIN_UNTRUSTED_SOURCE: "SUPPLY_CHAIN_UNTRUSTED_SOURCE";
39
+ readonly TOOL_NOT_ALLOWED: "TOOL_NOT_ALLOWED";
40
+ readonly UNTRUSTED_OUTPUT: "UNTRUSTED_OUTPUT";
41
+ readonly CONTENT_POLICY_VIOLATION: "CONTENT_POLICY_VIOLATION";
42
+ };
43
+ export type ReasonCode = (typeof REASON_CODES)[keyof typeof REASON_CODES];
@@ -0,0 +1,42 @@
1
+ export const REASON_CODES = {
2
+ AUDIT_WOULD_DENY: "AUDIT_WOULD_DENY",
3
+ AUDIT_WOULD_REDACT: "AUDIT_WOULD_REDACT",
4
+ BUDGET_REQUEST_EXCEEDED: "BUDGET_REQUEST_EXCEEDED",
5
+ BUDGET_TOOL_CALL_EXCEEDED: "BUDGET_TOOL_CALL_EXCEEDED",
6
+ COMMAND_ARG_PATTERN_BLOCKED: "COMMAND_ARG_PATTERN_BLOCKED",
7
+ COMMAND_BINARY_NOT_ALLOWED: "COMMAND_BINARY_NOT_ALLOWED",
8
+ COMMAND_SHELL_OPERATOR_BLOCKED: "COMMAND_SHELL_OPERATOR_BLOCKED",
9
+ DESTRUCTIVE_COMMAND: "DESTRUCTIVE_COMMAND",
10
+ ENGINE_FAILURE: "ENGINE_FAILURE",
11
+ ENGINE_FAILURE_FAIL_OPEN: "ENGINE_FAILURE_FAIL_OPEN",
12
+ EXFIL_PATTERN: "EXFIL_PATTERN",
13
+ INPUT_LIMIT_EXCEEDED: "INPUT_LIMIT_EXCEEDED",
14
+ INVALID_NETWORK_HOST: "INVALID_NETWORK_HOST",
15
+ INVALID_NETWORK_URL: "INVALID_NETWORK_URL",
16
+ NETWORK_HOST_BLOCKED: "NETWORK_HOST_BLOCKED",
17
+ NETWORK_PRIVATE_BLOCKED: "NETWORK_PRIVATE_BLOCKED",
18
+ PATH_OUTSIDE_WORKSPACE: "PATH_OUTSIDE_WORKSPACE",
19
+ PATH_SYMLINK_TRAVERSAL: "PATH_SYMLINK_TRAVERSAL",
20
+ PATH_TRAVERSAL: "PATH_TRAVERSAL",
21
+ PII_DETECTED: "PII_DETECTED",
22
+ PRINCIPAL_CONTEXT_MISSING: "PRINCIPAL_CONTEXT_MISSING",
23
+ PROMPT_INJECTION: "PROMPT_INJECTION",
24
+ GROUP_SENDER_NOT_ALLOWED: "GROUP_SENDER_NOT_ALLOWED",
25
+ ROLE_TOOL_NOT_ALLOWED: "ROLE_TOOL_NOT_ALLOWED",
26
+ ROLLOUT_AUDIT_OVERRIDE: "ROLLOUT_AUDIT_OVERRIDE",
27
+ RESTRICTED_INFO_ROLE_BLOCKED: "RESTRICTED_INFO_ROLE_BLOCKED",
28
+ OWNER_APPROVAL_REQUIRED: "OWNER_APPROVAL_REQUIRED",
29
+ OWNER_APPROVAL_INVALID: "OWNER_APPROVAL_INVALID",
30
+ OWNER_APPROVAL_EXPIRED: "OWNER_APPROVAL_EXPIRED",
31
+ OWNER_APPROVAL_REPLAYED: "OWNER_APPROVAL_REPLAYED",
32
+ RETRIEVAL_SIGNATURE_INVALID: "RETRIEVAL_SIGNATURE_INVALID",
33
+ RETRIEVAL_TRUST_LEVEL_TOO_LOW: "RETRIEVAL_TRUST_LEVEL_TOO_LOW",
34
+ RETRIEVAL_TRUST_REQUIRED: "RETRIEVAL_TRUST_REQUIRED",
35
+ SECRET_DETECTED: "SECRET_DETECTED",
36
+ SUPPLY_CHAIN_HASH_BLOCKED: "SUPPLY_CHAIN_HASH_BLOCKED",
37
+ SUPPLY_CHAIN_HASH_REQUIRED: "SUPPLY_CHAIN_HASH_REQUIRED",
38
+ SUPPLY_CHAIN_UNTRUSTED_SOURCE: "SUPPLY_CHAIN_UNTRUSTED_SOURCE",
39
+ TOOL_NOT_ALLOWED: "TOOL_NOT_ALLOWED",
40
+ UNTRUSTED_OUTPUT: "UNTRUSTED_OUTPUT",
41
+ CONTENT_POLICY_VIOLATION: "CONTENT_POLICY_VIOLATION"
42
+ };
@@ -0,0 +1,2 @@
1
+ import type { GuardrailsConfig, NormalizedEvent, RuleHit } from "./types.js";
2
+ export declare function detectRetrievalTrust(event: NormalizedEvent, config: GuardrailsConfig): RuleHit[];
@@ -0,0 +1,65 @@
1
+ import { REASON_CODES } from "./reason-codes.js";
2
+ const HIGH_RISK_TOOLS = new Set([
3
+ "exec",
4
+ "process",
5
+ "write",
6
+ "edit",
7
+ "apply_patch",
8
+ "skills.install"
9
+ ]);
10
+ function trustRank(level) {
11
+ switch (level) {
12
+ case "high":
13
+ return 3;
14
+ case "medium":
15
+ return 2;
16
+ case "low":
17
+ return 1;
18
+ default:
19
+ return 0;
20
+ }
21
+ }
22
+ export function detectRetrievalTrust(event, config) {
23
+ if (event.phase !== "before_tool_call") {
24
+ return [];
25
+ }
26
+ if (!config.retrievalTrust?.requiredForToolExecution) {
27
+ return [];
28
+ }
29
+ if (!event.toolName || !HIGH_RISK_TOOLS.has(event.toolName)) {
30
+ return [];
31
+ }
32
+ if (event.metadata.sourceType !== "retrieval") {
33
+ return [];
34
+ }
35
+ const hits = [];
36
+ const trustLevel = event.metadata.trustLevel;
37
+ if (!trustLevel) {
38
+ hits.push({
39
+ ruleId: "retrieval.trust.required",
40
+ reasonCode: REASON_CODES.RETRIEVAL_TRUST_REQUIRED,
41
+ decision: "DENY",
42
+ weight: 0.7
43
+ });
44
+ return hits;
45
+ }
46
+ const minimumRank = trustRank(config.retrievalTrust.minimumTrustLevel);
47
+ if (trustRank(trustLevel) < minimumRank) {
48
+ hits.push({
49
+ ruleId: "retrieval.trust.level",
50
+ reasonCode: REASON_CODES.RETRIEVAL_TRUST_LEVEL_TOO_LOW,
51
+ decision: "DENY",
52
+ weight: 0.75
53
+ });
54
+ }
55
+ if (config.retrievalTrust.requireSignedSource &&
56
+ event.metadata.sourceSignatureValid !== true) {
57
+ hits.push({
58
+ ruleId: "retrieval.trust.signature",
59
+ reasonCode: REASON_CODES.RETRIEVAL_SIGNATURE_INVALID,
60
+ decision: "DENY",
61
+ weight: 0.8
62
+ });
63
+ }
64
+ return hits;
65
+ }
@@ -0,0 +1,2 @@
1
+ import type { RuleHit } from "./types.js";
2
+ export declare function aggregateRisk(hits: RuleHit[]): number;
@@ -0,0 +1,18 @@
1
+ const DECISION_MULTIPLIER = {
2
+ DENY: 1,
3
+ REDACT: 0.6
4
+ };
5
+ function clamp(value, min, max) {
6
+ return Math.min(max, Math.max(min, value));
7
+ }
8
+ export function aggregateRisk(hits) {
9
+ if (hits.length === 0) {
10
+ return 0;
11
+ }
12
+ const weighted = hits.reduce((acc, hit) => {
13
+ const multiplier = DECISION_MULTIPLIER[hit.decision] ?? 0.5;
14
+ return acc + clamp(hit.weight, 0, 1) * multiplier;
15
+ }, 0);
16
+ const normalized = 1 - Math.exp(-weighted);
17
+ return Number(clamp(normalized, 0, 1).toFixed(4));
18
+ }
@@ -0,0 +1,2 @@
1
+ import type { GuardrailsConfig, NormalizedEvent, RuleHit } from "./types.js";
2
+ export declare function detectSupplyChainRisk(event: NormalizedEvent, config: GuardrailsConfig): Promise<RuleHit[]>;
@@ -0,0 +1,78 @@
1
+ import { canonicalizePathCandidate, canonicalizeRoots, isCanonicalPathWithinRoots } from "./path-canonical.js";
2
+ import { REASON_CODES } from "./reason-codes.js";
3
+ function pickString(args, keys) {
4
+ for (const key of keys) {
5
+ const value = args[key];
6
+ if (typeof value === "string" && value.trim().length > 0) {
7
+ return value.trim();
8
+ }
9
+ }
10
+ return undefined;
11
+ }
12
+ function isRemoteSource(source) {
13
+ return /^(?:https?:\/\/|git@|ssh:\/\/)/iu.test(source) || source.includes("github.com/");
14
+ }
15
+ function isTrustedSource(source, trustedSources) {
16
+ const normalized = source.toLowerCase();
17
+ return trustedSources.some((trusted) => normalized.startsWith(trusted.toLowerCase()));
18
+ }
19
+ export async function detectSupplyChainRisk(event, config) {
20
+ if (event.phase !== "before_tool_call" || event.toolName !== "skills.install") {
21
+ return [];
22
+ }
23
+ const hits = [];
24
+ const source = pickString(event.args, ["source", "repo", "repository", "url", "skillSource"]);
25
+ if (!source || !isTrustedSource(source, config.supplyChain.trustedSkillSources)) {
26
+ hits.push({
27
+ ruleId: "supply_chain.untrusted_source",
28
+ reasonCode: REASON_CODES.SUPPLY_CHAIN_UNTRUSTED_SOURCE,
29
+ decision: "DENY",
30
+ weight: 0.85
31
+ });
32
+ }
33
+ const hash = pickString(event.args, ["hash", "sha256", "digest"]);
34
+ if (source && isRemoteSource(source) && config.supplyChain.requireSkillHash && !hash) {
35
+ hits.push({
36
+ ruleId: "supply_chain.hash.required",
37
+ reasonCode: REASON_CODES.SUPPLY_CHAIN_HASH_REQUIRED,
38
+ decision: "DENY",
39
+ weight: 0.8
40
+ });
41
+ }
42
+ if (hash &&
43
+ config.supplyChain.allowedSkillHashes.length > 0 &&
44
+ !config.supplyChain.allowedSkillHashes.includes(hash)) {
45
+ hits.push({
46
+ ruleId: "supply_chain.hash.allowlist",
47
+ reasonCode: REASON_CODES.SUPPLY_CHAIN_HASH_BLOCKED,
48
+ decision: "DENY",
49
+ weight: 0.8
50
+ });
51
+ }
52
+ const targetDir = pickString(event.args, ["targetDir", "path", "installDir"]);
53
+ if (targetDir) {
54
+ const canonicalRoots = await canonicalizeRoots([
55
+ config.workspaceRoot,
56
+ ...config.allow.writablePaths
57
+ ]);
58
+ const checked = await canonicalizePathCandidate(targetDir, config.workspaceRoot);
59
+ if (config.pathPolicy.enforceCanonicalRealpath &&
60
+ !isCanonicalPathWithinRoots(checked.canonicalPath, canonicalRoots)) {
61
+ hits.push({
62
+ ruleId: "supply_chain.target_dir.outside_workspace",
63
+ reasonCode: REASON_CODES.PATH_OUTSIDE_WORKSPACE,
64
+ decision: "DENY",
65
+ weight: 0.9
66
+ });
67
+ }
68
+ if (config.pathPolicy.denySymlinkTraversal && checked.traversedSymlink) {
69
+ hits.push({
70
+ ruleId: "supply_chain.target_dir.symlink_traversal",
71
+ reasonCode: REASON_CODES.PATH_SYMLINK_TRAVERSAL,
72
+ decision: "DENY",
73
+ weight: 0.9
74
+ });
75
+ }
76
+ }
77
+ return hits;
78
+ }
@@ -0,0 +1,149 @@
1
+ export type Phase = "before_agent_start" | "message_received" | "before_tool_call" | "tool_result_persist" | "agent_end";
2
+ export type Decision = "ALLOW" | "REDACT" | "DENY";
3
+ export type PrincipalRole = "owner" | "admin" | "member" | "unknown";
4
+ export type ApproverRole = Extract<PrincipalRole, "owner" | "admin">;
5
+ export type ChannelType = "dm" | "group" | "thread" | "unknown";
6
+ export type DataClass = "public" | "internal" | "restricted" | "secret";
7
+ export interface AllowedCommand {
8
+ binary: string;
9
+ argPattern?: string;
10
+ allowShellOperators?: boolean;
11
+ }
12
+ export interface PrincipalContext {
13
+ senderId: string;
14
+ senderHandle?: string;
15
+ role: PrincipalRole;
16
+ channelId?: string;
17
+ conversationId: string;
18
+ channelType: ChannelType;
19
+ mentionedAgent?: boolean;
20
+ pairedDevice?: boolean;
21
+ }
22
+ export interface ApprovalContext {
23
+ token?: string;
24
+ requestId?: string;
25
+ }
26
+ export type RolloutStage = "stage_a_audit" | "stage_b_high_risk_enforce" | "stage_c_full_enforce";
27
+ export interface GuardMetadata extends Record<string, unknown> {
28
+ sourceType?: "user" | "retrieval" | "tool";
29
+ sourceId?: string;
30
+ trustLevel?: "low" | "medium" | "high";
31
+ sourceSignatureValid?: boolean;
32
+ principal?: PrincipalContext;
33
+ approval?: ApprovalContext;
34
+ dataClass?: DataClass;
35
+ }
36
+ export interface GuardrailsConfig {
37
+ mode: "enforce" | "audit";
38
+ failClosed: boolean;
39
+ workspaceRoot: string;
40
+ allow: {
41
+ tools: string[];
42
+ commands: AllowedCommand[];
43
+ writablePaths: string[];
44
+ networkHosts: string[];
45
+ allowPrivateEgress: boolean;
46
+ };
47
+ deny: {
48
+ commandPatterns: string[];
49
+ pathPatterns: string[];
50
+ promptInjectionPatterns: string[];
51
+ exfiltrationPatterns: string[];
52
+ shellOperatorPatterns: string[];
53
+ };
54
+ redaction: {
55
+ secretPatterns: string[];
56
+ piiPatterns: string[];
57
+ replacement: string;
58
+ applyInAuditMode: boolean;
59
+ };
60
+ limits: {
61
+ maxInputChars: number;
62
+ maxToolArgChars: number;
63
+ maxOutputChars: number;
64
+ maxRequestsPerMinute: number;
65
+ maxToolCallsPerMinute: number;
66
+ };
67
+ pathPolicy: {
68
+ enforceCanonicalRealpath: boolean;
69
+ denySymlinkTraversal: boolean;
70
+ };
71
+ supplyChain: {
72
+ trustedSkillSources: string[];
73
+ requireSkillHash: boolean;
74
+ allowedSkillHashes: string[];
75
+ };
76
+ retrievalTrust?: {
77
+ requiredForToolExecution: boolean;
78
+ minimumTrustLevel: "high" | "medium";
79
+ requireSignedSource: boolean;
80
+ };
81
+ principal: {
82
+ requireContext: boolean;
83
+ ownerIds: string[];
84
+ adminIds: string[];
85
+ failUnknownInGroup: boolean;
86
+ };
87
+ authorization: {
88
+ defaultEffect: "deny" | "allow";
89
+ requireMentionInGroups: boolean;
90
+ restrictedTools: string[];
91
+ restrictedDataClasses: Array<Exclude<DataClass, "public">>;
92
+ toolAllowByRole: Record<PrincipalRole, string[]>;
93
+ };
94
+ approval: {
95
+ enabled: boolean;
96
+ ttlSeconds: number;
97
+ requireForTools: string[];
98
+ requireForDataClasses: Array<"restricted" | "secret">;
99
+ ownerQuorum: number;
100
+ bindToConversation: boolean;
101
+ storagePath?: string;
102
+ };
103
+ tenancy: {
104
+ budgetKeyMode: "agent" | "agent+principal+conversation";
105
+ redactCrossPrincipalOutput: boolean;
106
+ };
107
+ rollout: {
108
+ stage: RolloutStage;
109
+ highRiskTools: string[];
110
+ };
111
+ monitoring: {
112
+ falsePositiveThresholdPct: number;
113
+ consecutiveDaysForTuning: number;
114
+ };
115
+ }
116
+ export interface GuardEvent {
117
+ phase: Phase;
118
+ agentId: string;
119
+ toolName?: string;
120
+ content?: string;
121
+ args?: Record<string, unknown>;
122
+ metadata?: GuardMetadata;
123
+ }
124
+ export interface GuardDecision {
125
+ decision: Decision;
126
+ reasonCodes: string[];
127
+ riskScore: number;
128
+ redactedContent?: string;
129
+ approvalChallenge?: {
130
+ requestId: string;
131
+ expiresAt: number;
132
+ reason: string;
133
+ requiredRole: ApproverRole;
134
+ };
135
+ telemetry: {
136
+ matchedRules: string[];
137
+ elapsedMs: number;
138
+ };
139
+ }
140
+ export interface RuleHit {
141
+ ruleId: string;
142
+ reasonCode: string;
143
+ decision: Exclude<Decision, "ALLOW">;
144
+ weight: number;
145
+ }
146
+ export interface NormalizedEvent extends GuardEvent {
147
+ args: Record<string, unknown>;
148
+ metadata: GuardMetadata;
149
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ export { GuardrailsEngine } from "./core/engine.js";
2
+ export { REASON_CODES } from "./core/reason-codes.js";
3
+ export type { ApproverRole, ChannelType, DataClass, Decision, PrincipalContext, PrincipalRole, RolloutStage, GuardDecision, GuardEvent, GuardrailsConfig, Phase } from "./core/types.js";
4
+ export { UNKNOWN_SENDER, UNKNOWN_CONVERSATION } from "./core/identity.js";
5
+ export { createDefaultConfig, mergeConfig } from "./rules/default-policy.js";
6
+ export { createOpenClawGuardrailsPlugin } from "./plugin/openclaw-adapter.js";
7
+ export { registerOpenClawGuardrails } from "./plugin/openclaw-extension.js";
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { GuardrailsEngine } from "./core/engine.js";
2
+ export { REASON_CODES } from "./core/reason-codes.js";
3
+ export { UNKNOWN_SENDER, UNKNOWN_CONVERSATION } from "./core/identity.js";
4
+ export { createDefaultConfig, mergeConfig } from "./rules/default-policy.js";
5
+ export { createOpenClawGuardrailsPlugin } from "./plugin/openclaw-adapter.js";
6
+ export { registerOpenClawGuardrails } from "./plugin/openclaw-extension.js";
@@ -0,0 +1,41 @@
1
+ import type { ApproverRole, ChannelType, DataClass, GuardDecision, GuardrailsConfig, PrincipalRole } from "../core/types.js";
2
+ export interface OpenClawContext extends Record<string, unknown> {
3
+ agentId?: string;
4
+ toolName?: string;
5
+ args?: Record<string, unknown>;
6
+ content?: string;
7
+ message?: string;
8
+ output?: string;
9
+ prompt?: string;
10
+ systemPrompt?: string;
11
+ senderId?: string;
12
+ senderHandle?: string;
13
+ role?: PrincipalRole;
14
+ conversationId?: string;
15
+ channelId?: string;
16
+ channelType?: ChannelType;
17
+ mentionedAgent?: boolean;
18
+ pairedDevice?: boolean;
19
+ dataClass?: DataClass;
20
+ metadata?: Record<string, unknown>;
21
+ }
22
+ export interface OpenClawHookResult extends OpenClawContext {
23
+ blocked?: boolean;
24
+ reasonCodes?: string[];
25
+ guardrails?: {
26
+ decision: GuardDecision;
27
+ };
28
+ }
29
+ export interface OpenClawPlugin {
30
+ name: string;
31
+ version: string;
32
+ approveRequest: (requestId: string, approverId: string, approverRole: ApproverRole) => string | null;
33
+ hooks: {
34
+ before_agent_start: (context: OpenClawContext) => Promise<OpenClawHookResult>;
35
+ message_received: (context: OpenClawContext) => Promise<OpenClawHookResult>;
36
+ before_tool_call: (context: OpenClawContext) => Promise<OpenClawHookResult>;
37
+ tool_result_persist: (context: OpenClawContext) => Promise<OpenClawHookResult>;
38
+ agent_end: (context: OpenClawContext) => Promise<OpenClawHookResult>;
39
+ };
40
+ }
41
+ export declare function createOpenClawGuardrailsPlugin(overrides?: Partial<GuardrailsConfig>): OpenClawPlugin;