@gatewaystack/gatewaystack-governance 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 (37) hide show
  1. package/README.md +187 -0
  2. package/SKILL.md +81 -0
  3. package/openclaw.plugin.json +11 -0
  4. package/package.json +64 -0
  5. package/policy.example.json +81 -0
  6. package/references/attack-patterns.md +45 -0
  7. package/references/policy-reference.md +141 -0
  8. package/scripts/governance/audit.d.ts +2 -0
  9. package/scripts/governance/audit.js +53 -0
  10. package/scripts/governance/check.d.ts +8 -0
  11. package/scripts/governance/check.js +172 -0
  12. package/scripts/governance/cli.d.ts +4 -0
  13. package/scripts/governance/cli.js +208 -0
  14. package/scripts/governance/constants.d.ts +7 -0
  15. package/scripts/governance/constants.js +99 -0
  16. package/scripts/governance/identity.d.ts +7 -0
  17. package/scripts/governance/identity.js +40 -0
  18. package/scripts/governance/injection.d.ts +6 -0
  19. package/scripts/governance/injection.js +59 -0
  20. package/scripts/governance/policy.d.ts +2 -0
  21. package/scripts/governance/policy.js +56 -0
  22. package/scripts/governance/rate-limit.d.ts +5 -0
  23. package/scripts/governance/rate-limit.js +156 -0
  24. package/scripts/governance/scope.d.ts +5 -0
  25. package/scripts/governance/scope.js +27 -0
  26. package/scripts/governance/types.d.ts +73 -0
  27. package/scripts/governance/types.js +2 -0
  28. package/scripts/governance/utils.d.ts +1 -0
  29. package/scripts/governance/utils.js +40 -0
  30. package/scripts/governance/validate-policy.d.ts +6 -0
  31. package/scripts/governance/validate-policy.js +104 -0
  32. package/scripts/governance-gateway.d.ts +11 -0
  33. package/scripts/governance-gateway.js +23 -0
  34. package/scripts/governance-gateway.ts +24 -0
  35. package/src/plugin.d.ts +17 -0
  36. package/src/plugin.js +98 -0
  37. package/src/plugin.ts +90 -0
@@ -0,0 +1,5 @@
1
+ import type { Policy } from "./types.js";
2
+ export declare function checkRateLimit(userId: string, session: string | undefined, policy: Policy): {
3
+ allowed: boolean;
4
+ detail: string;
5
+ };
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.checkRateLimit = checkRateLimit;
37
+ const fs = __importStar(require("fs"));
38
+ const constants_js_1 = require("./constants.js");
39
+ const LOCK_PATH = constants_js_1.RATE_LIMIT_STATE_PATH + ".lock";
40
+ const LOCK_TIMEOUT_MS = 5000;
41
+ const LOCK_RETRY_MS = 50;
42
+ function isLockStale() {
43
+ try {
44
+ const pidStr = fs.readFileSync(LOCK_PATH, "utf-8").trim();
45
+ const pid = parseInt(pidStr, 10);
46
+ if (isNaN(pid))
47
+ return true;
48
+ // process.kill(pid, 0) throws if the process doesn't exist
49
+ process.kill(pid, 0);
50
+ return false; // process is alive, lock is valid
51
+ }
52
+ catch {
53
+ return true; // can't read or process is dead — stale lock
54
+ }
55
+ }
56
+ function acquireLock() {
57
+ const deadline = Date.now() + LOCK_TIMEOUT_MS;
58
+ while (Date.now() < deadline) {
59
+ try {
60
+ // O_EXCL: fail if file exists — atomic advisory lock
61
+ fs.writeFileSync(LOCK_PATH, String(process.pid), { flag: "wx" });
62
+ return true;
63
+ }
64
+ catch {
65
+ // Lock file exists — check if the holding process is still alive
66
+ if (isLockStale()) {
67
+ try {
68
+ fs.unlinkSync(LOCK_PATH);
69
+ continue; // retry immediately after clearing stale lock
70
+ }
71
+ catch {
72
+ // another process beat us to cleanup — retry normally
73
+ }
74
+ }
75
+ // Lock held by a live process — spin-wait
76
+ const start = Date.now();
77
+ while (Date.now() - start < LOCK_RETRY_MS) {
78
+ // busy wait
79
+ }
80
+ }
81
+ }
82
+ return false;
83
+ }
84
+ function releaseLock() {
85
+ try {
86
+ fs.unlinkSync(LOCK_PATH);
87
+ }
88
+ catch {
89
+ // Lock already released or never acquired
90
+ }
91
+ }
92
+ function loadRateLimitState() {
93
+ if (fs.existsSync(constants_js_1.RATE_LIMIT_STATE_PATH)) {
94
+ try {
95
+ return JSON.parse(fs.readFileSync(constants_js_1.RATE_LIMIT_STATE_PATH, "utf-8"));
96
+ }
97
+ catch {
98
+ return {};
99
+ }
100
+ }
101
+ return {};
102
+ }
103
+ function saveRateLimitState(state) {
104
+ fs.writeFileSync(constants_js_1.RATE_LIMIT_STATE_PATH, JSON.stringify(state, null, 2));
105
+ }
106
+ function _checkRateLimitInner(userId, session, policy) {
107
+ const state = loadRateLimitState();
108
+ const now = Date.now();
109
+ // Per-user check
110
+ const userKey = `user:${userId}`;
111
+ const userState = state[userKey] || { calls: [] };
112
+ const userWindow = policy.rateLimits.perUser.windowSeconds * 1000;
113
+ userState.calls = userState.calls.filter((c) => now - c.timestamp < userWindow);
114
+ if (userState.calls.length >= policy.rateLimits.perUser.maxCalls) {
115
+ return {
116
+ allowed: false,
117
+ detail: `User ${userId} exceeded rate limit: ${policy.rateLimits.perUser.maxCalls} calls per ${policy.rateLimits.perUser.windowSeconds}s (current: ${userState.calls.length})`,
118
+ };
119
+ }
120
+ // Per-session check
121
+ if (session) {
122
+ const sessionKey = `session:${session}`;
123
+ const sessionState = state[sessionKey] || { calls: [] };
124
+ const sessionWindow = policy.rateLimits.perSession.windowSeconds * 1000;
125
+ sessionState.calls = sessionState.calls.filter((c) => now - c.timestamp < sessionWindow);
126
+ if (sessionState.calls.length >= policy.rateLimits.perSession.maxCalls) {
127
+ return {
128
+ allowed: false,
129
+ detail: `Session ${session} exceeded rate limit: ${policy.rateLimits.perSession.maxCalls} calls per ${policy.rateLimits.perSession.windowSeconds}s`,
130
+ };
131
+ }
132
+ sessionState.calls.push({ timestamp: now });
133
+ state[sessionKey] = sessionState;
134
+ }
135
+ userState.calls.push({ timestamp: now });
136
+ state[userKey] = userState;
137
+ saveRateLimitState(state);
138
+ return {
139
+ allowed: true,
140
+ detail: `Rate limit OK: ${userState.calls.length}/${policy.rateLimits.perUser.maxCalls} calls in window`,
141
+ };
142
+ }
143
+ function checkRateLimit(userId, session, policy) {
144
+ if (!acquireLock()) {
145
+ return {
146
+ allowed: false,
147
+ detail: "Rate limit state lock timeout — concurrent access. Try again.",
148
+ };
149
+ }
150
+ try {
151
+ return _checkRateLimitInner(userId, session, policy);
152
+ }
153
+ finally {
154
+ releaseLock();
155
+ }
156
+ }
@@ -0,0 +1,5 @@
1
+ import type { Policy } from "./types.js";
2
+ export declare function checkScope(tool: string, roles: string[], policy: Policy): {
3
+ allowed: boolean;
4
+ detail: string;
5
+ };
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkScope = checkScope;
4
+ function checkScope(tool, roles, policy) {
5
+ const toolPolicy = policy.allowedTools[tool];
6
+ // Deny-by-default: tool must be explicitly allowlisted
7
+ if (!toolPolicy) {
8
+ return {
9
+ allowed: false,
10
+ detail: `Tool "${tool}" is not in the allowlist. Deny-by-default policy enforced.`,
11
+ };
12
+ }
13
+ // If tool has role restrictions, check them
14
+ if (toolPolicy.roles && toolPolicy.roles.length > 0) {
15
+ const hasRole = roles.some((r) => toolPolicy.roles.includes(r));
16
+ if (!hasRole) {
17
+ return {
18
+ allowed: false,
19
+ detail: `Tool "${tool}" requires roles [${toolPolicy.roles.join(", ")}] but user has [${roles.join(", ")}]`,
20
+ };
21
+ }
22
+ }
23
+ return {
24
+ allowed: true,
25
+ detail: `Tool "${tool}" is allowlisted for roles [${roles.join(", ")}]`,
26
+ };
27
+ }
@@ -0,0 +1,73 @@
1
+ export interface Policy {
2
+ allowedTools: Record<string, {
3
+ roles?: string[];
4
+ maxArgsLength?: number;
5
+ description?: string;
6
+ }>;
7
+ rateLimits: {
8
+ perUser: {
9
+ maxCalls: number;
10
+ windowSeconds: number;
11
+ };
12
+ perSession: {
13
+ maxCalls: number;
14
+ windowSeconds: number;
15
+ };
16
+ };
17
+ identityMap: Record<string, {
18
+ userId: string;
19
+ roles: string[];
20
+ }>;
21
+ injectionDetection: {
22
+ enabled: boolean;
23
+ sensitivity: "low" | "medium" | "high";
24
+ customPatterns?: string[];
25
+ };
26
+ auditLog: {
27
+ path: string;
28
+ maxFileSizeMB: number;
29
+ };
30
+ }
31
+ export interface GovernanceRequest {
32
+ action: "check" | "log-result" | "self-test";
33
+ tool?: string;
34
+ args?: string;
35
+ user?: string;
36
+ channel?: string;
37
+ session?: string;
38
+ requestId?: string;
39
+ result?: string;
40
+ output?: string;
41
+ }
42
+ export interface GovernanceCheckResult {
43
+ allowed: boolean;
44
+ reason?: string;
45
+ requestId: string;
46
+ identity?: string;
47
+ roles?: string[];
48
+ patterns?: string[];
49
+ }
50
+ export interface AuditEntry {
51
+ timestamp: string;
52
+ requestId: string;
53
+ action: string;
54
+ tool?: string;
55
+ user?: string;
56
+ resolvedIdentity?: string;
57
+ roles?: string[];
58
+ channel?: string;
59
+ session?: string;
60
+ allowed?: boolean;
61
+ reason?: string;
62
+ checks?: Record<string, {
63
+ passed: boolean;
64
+ detail: string;
65
+ }>;
66
+ result?: string;
67
+ outputSummary?: string;
68
+ }
69
+ export interface RateLimitState {
70
+ calls: {
71
+ timestamp: number;
72
+ }[];
73
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
1
+ export declare function generateRequestId(): string;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.generateRequestId = generateRequestId;
37
+ const crypto = __importStar(require("crypto"));
38
+ function generateRequestId() {
39
+ return `gov-${Date.now()}-${crypto.randomBytes(4).toString("hex")}`;
40
+ }
@@ -0,0 +1,6 @@
1
+ export interface PolicyValidationResult {
2
+ valid: boolean;
3
+ errors: string[];
4
+ warnings: string[];
5
+ }
6
+ export declare function validatePolicy(policy: unknown): PolicyValidationResult;
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validatePolicy = validatePolicy;
4
+ function validatePolicy(policy) {
5
+ const errors = [];
6
+ const warnings = [];
7
+ if (typeof policy !== "object" || policy === null) {
8
+ return { valid: false, errors: ["Policy must be a non-null object"], warnings };
9
+ }
10
+ const p = policy;
11
+ // --- Required top-level fields ---
12
+ if (!p.allowedTools || typeof p.allowedTools !== "object") {
13
+ errors.push("Missing or invalid 'allowedTools' (must be an object)");
14
+ }
15
+ if (!p.rateLimits || typeof p.rateLimits !== "object") {
16
+ errors.push("Missing or invalid 'rateLimits' (must be an object)");
17
+ }
18
+ else {
19
+ const rl = p.rateLimits;
20
+ for (const key of ["perUser", "perSession"]) {
21
+ if (!rl[key] || typeof rl[key] !== "object") {
22
+ errors.push(`Missing or invalid 'rateLimits.${key}' (must be an object)`);
23
+ }
24
+ else {
25
+ const bucket = rl[key];
26
+ if (typeof bucket.maxCalls !== "number" || bucket.maxCalls < 0) {
27
+ errors.push(`'rateLimits.${key}.maxCalls' must be a non-negative number`);
28
+ }
29
+ if (typeof bucket.windowSeconds !== "number" || bucket.windowSeconds < 0) {
30
+ errors.push(`'rateLimits.${key}.windowSeconds' must be a non-negative number`);
31
+ }
32
+ }
33
+ }
34
+ }
35
+ if (!p.identityMap || typeof p.identityMap !== "object") {
36
+ errors.push("Missing or invalid 'identityMap' (must be an object)");
37
+ }
38
+ if (!p.injectionDetection || typeof p.injectionDetection !== "object") {
39
+ errors.push("Missing or invalid 'injectionDetection' (must be an object)");
40
+ }
41
+ else {
42
+ const id = p.injectionDetection;
43
+ if (typeof id.enabled !== "boolean") {
44
+ errors.push("'injectionDetection.enabled' must be a boolean");
45
+ }
46
+ if (!["low", "medium", "high"].includes(id.sensitivity)) {
47
+ errors.push("'injectionDetection.sensitivity' must be 'low', 'medium', or 'high'");
48
+ }
49
+ // Validate custom patterns
50
+ if (id.customPatterns !== undefined) {
51
+ if (!Array.isArray(id.customPatterns)) {
52
+ errors.push("'injectionDetection.customPatterns' must be an array");
53
+ }
54
+ else {
55
+ for (let i = 0; i < id.customPatterns.length; i++) {
56
+ const pat = id.customPatterns[i];
57
+ if (typeof pat !== "string") {
58
+ warnings.push(`customPatterns[${i}] is not a string`);
59
+ continue;
60
+ }
61
+ try {
62
+ new RegExp(pat);
63
+ }
64
+ catch {
65
+ warnings.push(`customPatterns[${i}] is not a valid regex: "${pat}"`);
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ if (!p.auditLog || typeof p.auditLog !== "object") {
72
+ errors.push("Missing or invalid 'auditLog' (must be an object)");
73
+ }
74
+ // --- Warnings (non-fatal) ---
75
+ // Empty collections
76
+ if (p.allowedTools && typeof p.allowedTools === "object" && Object.keys(p.allowedTools).length === 0) {
77
+ warnings.push("'allowedTools' is empty — all tools will be denied");
78
+ }
79
+ if (p.identityMap && typeof p.identityMap === "object" && Object.keys(p.identityMap).length === 0) {
80
+ warnings.push("'identityMap' is empty — all users will be denied");
81
+ }
82
+ // Cross-reference: roles referenced by tools but not assigned to any identity
83
+ if (p.allowedTools && typeof p.allowedTools === "object" &&
84
+ p.identityMap && typeof p.identityMap === "object") {
85
+ const allIdentityRoles = new Set();
86
+ for (const entry of Object.values(p.identityMap)) {
87
+ if (entry && typeof entry === "object" && Array.isArray(entry.roles)) {
88
+ for (const r of entry.roles) {
89
+ allIdentityRoles.add(r);
90
+ }
91
+ }
92
+ }
93
+ for (const [toolName, toolConfig] of Object.entries(p.allowedTools)) {
94
+ if (toolConfig && typeof toolConfig === "object" && Array.isArray(toolConfig.roles)) {
95
+ for (const role of toolConfig.roles) {
96
+ if (!allIdentityRoles.has(role)) {
97
+ warnings.push(`Tool "${toolName}" requires role "${role}" but no identity has it`);
98
+ }
99
+ }
100
+ }
101
+ }
102
+ }
103
+ return { valid: errors.length === 0, errors, warnings };
104
+ }
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * GatewayStack Governance Gateway for OpenClaw
4
+ *
5
+ * Barrel module — re-exports the public API and serves as the CLI entry point.
6
+ * Implementation lives in ./governance/*.ts modules.
7
+ */
8
+ export type { Policy, GovernanceCheckResult } from "./governance/types.js";
9
+ export { loadPolicy } from "./governance/policy.js";
10
+ export { validatePolicy } from "./governance/validate-policy.js";
11
+ export { checkGovernance } from "./governance/check.js";
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * GatewayStack Governance Gateway for OpenClaw
5
+ *
6
+ * Barrel module — re-exports the public API and serves as the CLI entry point.
7
+ * Implementation lives in ./governance/*.ts modules.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.checkGovernance = exports.validatePolicy = exports.loadPolicy = void 0;
11
+ var policy_js_1 = require("./governance/policy.js");
12
+ Object.defineProperty(exports, "loadPolicy", { enumerable: true, get: function () { return policy_js_1.loadPolicy; } });
13
+ var validate_policy_js_1 = require("./governance/validate-policy.js");
14
+ Object.defineProperty(exports, "validatePolicy", { enumerable: true, get: function () { return validate_policy_js_1.validatePolicy; } });
15
+ var check_js_1 = require("./governance/check.js");
16
+ Object.defineProperty(exports, "checkGovernance", { enumerable: true, get: function () { return check_js_1.checkGovernance; } });
17
+ // CLI entry point
18
+ const cli_js_1 = require("./governance/cli.js");
19
+ const isDirectExecution = require.main === module ||
20
+ process.argv[1]?.endsWith("governance-gateway.js");
21
+ if (isDirectExecution) {
22
+ (0, cli_js_1.runGovernanceCheck)((0, cli_js_1.parseArgs)(process.argv));
23
+ }
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * GatewayStack Governance Gateway for OpenClaw
4
+ *
5
+ * Barrel module — re-exports the public API and serves as the CLI entry point.
6
+ * Implementation lives in ./governance/*.ts modules.
7
+ */
8
+
9
+ // Re-export public API
10
+ export type { Policy, GovernanceCheckResult } from "./governance/types.js";
11
+ export { loadPolicy } from "./governance/policy.js";
12
+ export { validatePolicy } from "./governance/validate-policy.js";
13
+ export { checkGovernance } from "./governance/check.js";
14
+
15
+ // CLI entry point
16
+ import { parseArgs, runGovernanceCheck } from "./governance/cli.js";
17
+
18
+ const isDirectExecution =
19
+ require.main === module ||
20
+ process.argv[1]?.endsWith("governance-gateway.js");
21
+
22
+ if (isDirectExecution) {
23
+ runGovernanceCheck(parseArgs(process.argv));
24
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * GatewayStack Governance — OpenClaw Plugin
3
+ *
4
+ * Registers a `before_tool_call` hook that automatically runs governance
5
+ * checks on every tool invocation. The agent cannot bypass this — it runs
6
+ * at the process level before any tool executes.
7
+ *
8
+ * Identity mapping uses OpenClaw agent IDs (e.g. "main", "ops", "dev")
9
+ * rather than human users, since OpenClaw is a single-user personal AI.
10
+ */
11
+ declare const plugin: {
12
+ id: string;
13
+ name: string;
14
+ description: string;
15
+ register(api: any): void;
16
+ };
17
+ export default plugin;
package/src/plugin.js ADDED
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ /**
3
+ * GatewayStack Governance — OpenClaw Plugin
4
+ *
5
+ * Registers a `before_tool_call` hook that automatically runs governance
6
+ * checks on every tool invocation. The agent cannot bypass this — it runs
7
+ * at the process level before any tool executes.
8
+ *
9
+ * Identity mapping uses OpenClaw agent IDs (e.g. "main", "ops", "dev")
10
+ * rather than human users, since OpenClaw is a single-user personal AI.
11
+ */
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ const path = __importStar(require("path"));
47
+ const os = __importStar(require("os"));
48
+ const governance_gateway_js_1 = require("../scripts/governance-gateway.js");
49
+ // Resolve policy path: check plugin directory first, then ~/.openclaw default
50
+ function resolvePolicyPath() {
51
+ const pluginDir = path.resolve(__dirname, "..");
52
+ const localPolicy = path.join(pluginDir, "policy.json");
53
+ // Also check OpenClaw skills directory (for backward compat with skill installs)
54
+ const openclawSkillPolicy = path.join(os.homedir(), ".openclaw", "skills", "gatewaystack-governance", "policy.json");
55
+ // Prefer local plugin directory policy
56
+ try {
57
+ require("fs").accessSync(localPolicy);
58
+ return localPolicy;
59
+ }
60
+ catch {
61
+ // Fall through
62
+ }
63
+ // Try OpenClaw skills directory
64
+ try {
65
+ require("fs").accessSync(openclawSkillPolicy);
66
+ return openclawSkillPolicy;
67
+ }
68
+ catch {
69
+ // Fall through
70
+ }
71
+ // Default to local — will produce a clear error from checkGovernance
72
+ return localPolicy;
73
+ }
74
+ const plugin = {
75
+ id: "gatewaystack-governance",
76
+ name: "GatewayStack Governance",
77
+ description: "Automatic governance for every tool call — identity, scope, rate limiting, injection detection, and audit logging",
78
+ register(api) {
79
+ const policyPath = resolvePolicyPath();
80
+ api.on("before_tool_call", async (event, ctx) => {
81
+ const result = await (0, governance_gateway_js_1.checkGovernance)({
82
+ toolName: event.toolName,
83
+ args: JSON.stringify(event.params),
84
+ userId: ctx.agentId ?? "unknown",
85
+ session: ctx.sessionKey,
86
+ policyPath,
87
+ });
88
+ if (!result.allowed) {
89
+ return { block: true, blockReason: result.reason };
90
+ }
91
+ return {};
92
+ }, { priority: 0 });
93
+ if (api.logger) {
94
+ api.logger.info(`GatewayStack Governance loaded (policy: ${policyPath})`);
95
+ }
96
+ },
97
+ };
98
+ exports.default = plugin;