@poolzin/pool-bot 2026.3.6 → 2026.3.9
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/CHANGELOG.md +16 -0
- package/dist/.buildstamp +1 -1
- package/dist/agents/error-classifier.js +302 -0
- package/dist/agents/pi-tools.js +32 -2
- package/dist/agents/skills/security.js +217 -0
- package/dist/auto-reply/reply/get-reply.js +6 -0
- package/dist/auto-reply/reply/message-preprocess-hooks.js +17 -0
- package/dist/build-info.json +3 -3
- package/dist/cli/banner.js +20 -1
- package/dist/cli/lazy-commands.example.js +113 -0
- package/dist/cli/lazy-commands.js +329 -0
- package/dist/cli/program/command-registry.js +13 -0
- package/dist/cli/program/register.skills.js +4 -0
- package/dist/cli/security-cli.js +211 -2
- package/dist/cli/tagline.js +7 -0
- package/dist/config/config.js +1 -0
- package/dist/config/secrets-integration.js +88 -0
- package/dist/config/types.cli.js +1 -0
- package/dist/config/types.security.js +33 -0
- package/dist/config/zod-schema.js +15 -0
- package/dist/config/zod-schema.providers-core.js +1 -0
- package/dist/config/zod-schema.security.js +113 -0
- package/dist/context-engine/index.js +33 -0
- package/dist/context-engine/legacy.js +181 -0
- package/dist/context-engine/registry.js +86 -0
- package/dist/context-engine/summarizing.js +293 -0
- package/dist/context-engine/types.js +7 -0
- package/dist/discord/monitor/message-handler.preflight.js +11 -2
- package/dist/gateway/http-common.js +6 -1
- package/dist/hooks/fire-and-forget.js +6 -0
- package/dist/hooks/internal-hooks.js +64 -19
- package/dist/hooks/message-hook-mappers.js +179 -0
- package/dist/infra/abort-pattern.js +106 -0
- package/dist/infra/retry.js +94 -0
- package/dist/secrets/index.js +28 -0
- package/dist/secrets/resolver.js +185 -0
- package/dist/secrets/runtime.js +142 -0
- package/dist/secrets/types.js +11 -0
- package/dist/security/capability-guards.js +89 -0
- package/dist/security/capability-manager.js +76 -0
- package/dist/security/capability.js +147 -0
- package/dist/security/dangerous-tools.js +80 -0
- package/dist/security/index.js +7 -0
- package/dist/security/middleware.js +105 -0
- package/dist/security/types.js +12 -0
- package/dist/skills/commands.js +351 -0
- package/dist/skills/index.js +167 -0
- package/dist/skills/loader.js +282 -0
- package/dist/skills/parser.js +461 -0
- package/dist/skills/registry.js +397 -0
- package/dist/skills/security.js +318 -0
- package/dist/skills/types.js +21 -0
- package/dist/slack/monitor/context.js +1 -0
- package/dist/slack/monitor/message-handler/dispatch.js +14 -1
- package/dist/slack/monitor/provider.js +2 -0
- package/dist/test-utils/index.js +219 -0
- package/dist/tui/index.js +595 -0
- package/docs/INTEGRATION_PLAN.md +475 -0
- package/docs/INTEGRATION_SUMMARY.md +215 -0
- package/docs/integrations/HEXSTRIKE_PLAN.md +796 -0
- package/docs/integrations/INTEGRATION_PLAN.md +424 -0
- package/docs/integrations/PAGE_AGENT_PLAN.md +370 -0
- package/docs/integrations/XYOPS_PLAN.md +978 -0
- package/docs/skills/IMPLEMENTATION_SUMMARY.md +145 -0
- package/docs/skills/SKILL.md +524 -0
- package/docs/skills.md +405 -0
- package/package.json +1 -1
- package/skills/example-skill/SKILL.md +195 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { capabilityMatches, CapabilityResult } from "./capability.js";
|
|
2
|
+
/**
|
|
3
|
+
* Manages capability grants for all agents.
|
|
4
|
+
* Uses a Map for O(1) lookups. Thread-safe for single-threaded Node.js.
|
|
5
|
+
*/
|
|
6
|
+
export class CapabilityManager {
|
|
7
|
+
grants = new Map();
|
|
8
|
+
/** Grant capabilities to an agent. Replaces any existing grants. */
|
|
9
|
+
grant(agentId, capabilities) {
|
|
10
|
+
this.grants.set(agentId, capabilities);
|
|
11
|
+
}
|
|
12
|
+
/** Add capabilities to an agent's existing grants. */
|
|
13
|
+
add(agentId, capabilities) {
|
|
14
|
+
const existing = this.grants.get(agentId) ?? [];
|
|
15
|
+
this.grants.set(agentId, [...existing, ...capabilities]);
|
|
16
|
+
}
|
|
17
|
+
/** Check whether an agent has a specific capability. */
|
|
18
|
+
check(agentId, required) {
|
|
19
|
+
const grants = this.grants.get(agentId);
|
|
20
|
+
if (!grants) {
|
|
21
|
+
return CapabilityResult.denied(`No capabilities registered for agent ${agentId}`);
|
|
22
|
+
}
|
|
23
|
+
for (const granted of grants) {
|
|
24
|
+
if (capabilityMatches(granted, required)) {
|
|
25
|
+
return CapabilityResult.granted();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return CapabilityResult.denied(`Agent ${agentId} does not have capability: ${JSON.stringify(required)}`);
|
|
29
|
+
}
|
|
30
|
+
/** Check multiple capabilities at once. Returns first denial or grants all. */
|
|
31
|
+
checkAll(agentId, required) {
|
|
32
|
+
for (const req of required) {
|
|
33
|
+
const result = this.check(agentId, req);
|
|
34
|
+
if (!result.granted)
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
return CapabilityResult.granted();
|
|
38
|
+
}
|
|
39
|
+
/** List all capabilities for an agent. */
|
|
40
|
+
list(agentId) {
|
|
41
|
+
return this.grants.get(agentId) ?? [];
|
|
42
|
+
}
|
|
43
|
+
/** Remove all capabilities for an agent. */
|
|
44
|
+
revokeAll(agentId) {
|
|
45
|
+
this.grants.delete(agentId);
|
|
46
|
+
}
|
|
47
|
+
/** Check if an agent has any capabilities registered. */
|
|
48
|
+
has(agentId) {
|
|
49
|
+
return this.grants.has(agentId);
|
|
50
|
+
}
|
|
51
|
+
/** Get all registered agent IDs. */
|
|
52
|
+
agents() {
|
|
53
|
+
return Array.from(this.grants.keys());
|
|
54
|
+
}
|
|
55
|
+
/** Clear all grants. */
|
|
56
|
+
clear() {
|
|
57
|
+
this.grants.clear();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Global singleton instance. */
|
|
61
|
+
let globalManager;
|
|
62
|
+
/** Get or create the global capability manager. */
|
|
63
|
+
export function getCapabilityManager() {
|
|
64
|
+
if (!globalManager) {
|
|
65
|
+
globalManager = new CapabilityManager();
|
|
66
|
+
}
|
|
67
|
+
return globalManager;
|
|
68
|
+
}
|
|
69
|
+
/** Reset the global manager (useful for testing). */
|
|
70
|
+
export function resetCapabilityManager() {
|
|
71
|
+
globalManager = undefined;
|
|
72
|
+
}
|
|
73
|
+
/** Set a custom global manager (useful for testing). */
|
|
74
|
+
export function setCapabilityManager(manager) {
|
|
75
|
+
globalManager = manager;
|
|
76
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability-based security system for Pool Bot.
|
|
3
|
+
*
|
|
4
|
+
* Inspired by OpenFang's capability system, adapted for TypeScript.
|
|
5
|
+
* Agents can only perform actions they've been explicitly granted permission for.
|
|
6
|
+
* Capabilities are immutable after agent creation and enforced at runtime.
|
|
7
|
+
*/
|
|
8
|
+
/** All available capability types for CLI and validation. */
|
|
9
|
+
export const CAPABILITY_TYPES = [
|
|
10
|
+
// File system
|
|
11
|
+
"file:read",
|
|
12
|
+
"file:write",
|
|
13
|
+
// Network
|
|
14
|
+
"net:connect",
|
|
15
|
+
"net:listen",
|
|
16
|
+
// Tools
|
|
17
|
+
"tool:invoke",
|
|
18
|
+
"tool:all",
|
|
19
|
+
// LLM
|
|
20
|
+
"llm:query",
|
|
21
|
+
"llm:maxTokens",
|
|
22
|
+
// Agent interaction
|
|
23
|
+
"agent:spawn",
|
|
24
|
+
"agent:message",
|
|
25
|
+
"agent:kill",
|
|
26
|
+
// Memory
|
|
27
|
+
"memory:read",
|
|
28
|
+
"memory:write",
|
|
29
|
+
// Shell
|
|
30
|
+
"shell:exec",
|
|
31
|
+
"env:read",
|
|
32
|
+
// Gateway
|
|
33
|
+
"gateway:admin",
|
|
34
|
+
"gateway:channels:read",
|
|
35
|
+
"gateway:channels:write",
|
|
36
|
+
// Economic
|
|
37
|
+
"econ:spend",
|
|
38
|
+
"econ:earn",
|
|
39
|
+
"econ:transfer",
|
|
40
|
+
];
|
|
41
|
+
/** Helper to create capability check results. */
|
|
42
|
+
export const CapabilityResult = {
|
|
43
|
+
granted() {
|
|
44
|
+
return { granted: true };
|
|
45
|
+
},
|
|
46
|
+
denied(reason) {
|
|
47
|
+
return { granted: false, reason };
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
/** Simple glob pattern matching supporting '*' as wildcard. */
|
|
51
|
+
export function globMatches(pattern, value) {
|
|
52
|
+
if (pattern === "*")
|
|
53
|
+
return true;
|
|
54
|
+
if (pattern === value)
|
|
55
|
+
return true;
|
|
56
|
+
// Prefix wildcard: "*.example.com"
|
|
57
|
+
if (pattern.startsWith("*")) {
|
|
58
|
+
const suffix = pattern.slice(1);
|
|
59
|
+
return value.endsWith(suffix);
|
|
60
|
+
}
|
|
61
|
+
// Suffix wildcard: "api.*"
|
|
62
|
+
if (pattern.endsWith("*")) {
|
|
63
|
+
const prefix = pattern.slice(0, -1);
|
|
64
|
+
return value.startsWith(prefix);
|
|
65
|
+
}
|
|
66
|
+
// Middle wildcard: "api.*.com"
|
|
67
|
+
const starPos = pattern.indexOf("*");
|
|
68
|
+
if (starPos !== -1) {
|
|
69
|
+
const prefix = pattern.slice(0, starPos);
|
|
70
|
+
const suffix = pattern.slice(starPos + 1);
|
|
71
|
+
return (value.startsWith(prefix) &&
|
|
72
|
+
value.endsWith(suffix) &&
|
|
73
|
+
value.length >= prefix.length + suffix.length);
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Check whether a required capability matches any granted capability.
|
|
79
|
+
*/
|
|
80
|
+
export function capabilityMatches(granted, required) {
|
|
81
|
+
// Tool:all grants any specific tool
|
|
82
|
+
if (granted.type === "tool:all" && required.type === "tool:invoke") {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
// Same variant type matching
|
|
86
|
+
if (granted.type !== required.type)
|
|
87
|
+
return false;
|
|
88
|
+
switch (granted.type) {
|
|
89
|
+
case "file:read":
|
|
90
|
+
case "file:write":
|
|
91
|
+
return globMatches(granted.pattern, required.pattern);
|
|
92
|
+
case "net:connect":
|
|
93
|
+
return globMatches(granted.pattern, required.pattern);
|
|
94
|
+
case "net:listen":
|
|
95
|
+
return granted.port === required.port;
|
|
96
|
+
case "tool:invoke":
|
|
97
|
+
return granted.toolId === required.toolId || granted.toolId === "*";
|
|
98
|
+
case "llm:query":
|
|
99
|
+
return globMatches(granted.pattern, required.pattern);
|
|
100
|
+
case "llm:maxTokens":
|
|
101
|
+
return granted.limit >= required.limit;
|
|
102
|
+
case "agent:spawn":
|
|
103
|
+
return true;
|
|
104
|
+
case "agent:message":
|
|
105
|
+
case "agent:kill":
|
|
106
|
+
return globMatches(granted.pattern, required.pattern);
|
|
107
|
+
case "memory:read":
|
|
108
|
+
case "memory:write":
|
|
109
|
+
return globMatches(granted.scope, required.scope);
|
|
110
|
+
case "shell:exec":
|
|
111
|
+
return globMatches(granted.pattern, required.pattern);
|
|
112
|
+
case "env:read":
|
|
113
|
+
return globMatches(granted.pattern, required.pattern);
|
|
114
|
+
case "gateway:admin":
|
|
115
|
+
case "gateway:channels:read":
|
|
116
|
+
return true;
|
|
117
|
+
case "gateway:channels:write":
|
|
118
|
+
return globMatches(granted.pattern, required.pattern);
|
|
119
|
+
case "econ:spend":
|
|
120
|
+
return granted.limit >= required.limit;
|
|
121
|
+
case "econ:earn":
|
|
122
|
+
return true;
|
|
123
|
+
case "econ:transfer":
|
|
124
|
+
return globMatches(granted.pattern, required.pattern);
|
|
125
|
+
case "tool:all":
|
|
126
|
+
// tool:all only matches tool:all (already handled above for tool:invoke)
|
|
127
|
+
return false;
|
|
128
|
+
default:
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Validate that child capabilities are a subset of parent capabilities.
|
|
134
|
+
* Prevents privilege escalation.
|
|
135
|
+
*/
|
|
136
|
+
export function validateCapabilityInheritance(parentCaps, childCaps) {
|
|
137
|
+
for (const childCap of childCaps) {
|
|
138
|
+
const isCovered = parentCaps.some((parentCap) => capabilityMatches(parentCap, childCap));
|
|
139
|
+
if (!isCovered) {
|
|
140
|
+
return {
|
|
141
|
+
valid: false,
|
|
142
|
+
reason: `Privilege escalation denied: child requests ${JSON.stringify(childCap)} but parent does not have a matching grant`,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return { valid: true };
|
|
147
|
+
}
|
|
@@ -14,6 +14,20 @@ export const DEFAULT_GATEWAY_HTTP_TOOL_DENY = [
|
|
|
14
14
|
"gateway",
|
|
15
15
|
// Interactive setup — requires terminal QR scan, hangs on HTTP
|
|
16
16
|
"whatsapp_login",
|
|
17
|
+
// Cron automation — scheduling can be used for persistence
|
|
18
|
+
"cron",
|
|
19
|
+
// Exec tools — remote code execution risk
|
|
20
|
+
"exec",
|
|
21
|
+
"shell",
|
|
22
|
+
"spawn",
|
|
23
|
+
// File system mutations
|
|
24
|
+
"fs_write",
|
|
25
|
+
"fs_delete",
|
|
26
|
+
"fs_move",
|
|
27
|
+
"apply_patch",
|
|
28
|
+
// Device pairing
|
|
29
|
+
"device_pair",
|
|
30
|
+
"pair_device",
|
|
17
31
|
];
|
|
18
32
|
/**
|
|
19
33
|
* ACP tools that should always require explicit user approval.
|
|
@@ -30,5 +44,71 @@ export const DANGEROUS_ACP_TOOL_NAMES = [
|
|
|
30
44
|
"fs_delete",
|
|
31
45
|
"fs_move",
|
|
32
46
|
"apply_patch",
|
|
47
|
+
"cron",
|
|
48
|
+
"exec_approved",
|
|
49
|
+
"elevated_exec",
|
|
33
50
|
];
|
|
34
51
|
export const DANGEROUS_ACP_TOOLS = new Set(DANGEROUS_ACP_TOOL_NAMES);
|
|
52
|
+
/**
|
|
53
|
+
* Tool categories by risk level
|
|
54
|
+
*/
|
|
55
|
+
export const TOOL_CATEGORIES = {
|
|
56
|
+
/** Execution tools - can run arbitrary code */
|
|
57
|
+
EXECUTION: ["exec", "spawn", "shell", "exec_approved", "elevated_exec"],
|
|
58
|
+
/** File system mutators - can modify files */
|
|
59
|
+
FILE_MUTATORS: ["fs_write", "fs_delete", "fs_move", "apply_patch", "fs_mkdir", "fs_rename"],
|
|
60
|
+
/** Session orchestrators - can spawn/control sessions */
|
|
61
|
+
SESSION_ORCHESTRATORS: ["sessions_spawn", "sessions_send", "sessions_kill", "sessions_reset"],
|
|
62
|
+
/** Gateway control - can reconfigure gateway */
|
|
63
|
+
GATEWAY_CONTROL: ["gateway", "config_set", "config_reload"],
|
|
64
|
+
/** Automation - can schedule/automate */
|
|
65
|
+
AUTOMATION: ["cron", "hook_register", "automation_create"],
|
|
66
|
+
/** External integrations - can interact with external services */
|
|
67
|
+
EXTERNAL: ["whatsapp_login", "device_pair", "pair_device", "oauth_flow"],
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Check if a tool is in a specific category
|
|
71
|
+
*/
|
|
72
|
+
export function isToolInCategory(toolName, category) {
|
|
73
|
+
return TOOL_CATEGORIES[category].includes(toolName);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Check if a tool is considered dangerous
|
|
77
|
+
*/
|
|
78
|
+
export function isDangerousTool(toolName) {
|
|
79
|
+
return DANGEROUS_ACP_TOOLS.has(toolName.toLowerCase().trim());
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Check if a tool is denied over HTTP gateway
|
|
83
|
+
*/
|
|
84
|
+
export function isGatewayHttpDenied(toolName) {
|
|
85
|
+
return DEFAULT_GATEWAY_HTTP_TOOL_DENY.includes(toolName.toLowerCase().trim());
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get risk level for a tool
|
|
89
|
+
*/
|
|
90
|
+
export function getToolRiskLevel(toolName) {
|
|
91
|
+
if (["exec", "spawn", "shell", "sessions_spawn"].includes(toolName)) {
|
|
92
|
+
return "critical";
|
|
93
|
+
}
|
|
94
|
+
if (["fs_write", "fs_delete", "apply_patch", "gateway"].includes(toolName)) {
|
|
95
|
+
return "high";
|
|
96
|
+
}
|
|
97
|
+
if (["fs_move", "cron", "sessions_send"].includes(toolName)) {
|
|
98
|
+
return "medium";
|
|
99
|
+
}
|
|
100
|
+
return "low";
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Get tools that require explicit approval
|
|
104
|
+
*/
|
|
105
|
+
export function getToolsRequiringApproval(riskLevel = "high") {
|
|
106
|
+
const allDangerous = Array.from(DANGEROUS_ACP_TOOLS);
|
|
107
|
+
if (riskLevel === "critical") {
|
|
108
|
+
return allDangerous.filter((t) => getToolRiskLevel(t) === "critical");
|
|
109
|
+
}
|
|
110
|
+
if (riskLevel === "high") {
|
|
111
|
+
return allDangerous.filter((t) => ["critical", "high"].includes(getToolRiskLevel(t)));
|
|
112
|
+
}
|
|
113
|
+
return allDangerous;
|
|
114
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { getCapabilityManager } from "./capability-manager.js";
|
|
2
|
+
import { CapabilityError } from "./capability-guards.js";
|
|
3
|
+
import { logVerbose } from "../globals.js";
|
|
4
|
+
/**
|
|
5
|
+
* Creates a middleware that checks tool invocation capabilities.
|
|
6
|
+
*/
|
|
7
|
+
export function createCapabilityMiddleware() {
|
|
8
|
+
return async (ctx, toolId, _args, next) => {
|
|
9
|
+
const manager = getCapabilityManager();
|
|
10
|
+
// Check for tool:all first (grants any tool)
|
|
11
|
+
const allCheck = manager.check(ctx.agentId, { type: "tool:all" });
|
|
12
|
+
if (allCheck.granted) {
|
|
13
|
+
logVerbose(`[capability] ${ctx.agentId} granted tool:all for ${toolId}`);
|
|
14
|
+
return await next();
|
|
15
|
+
}
|
|
16
|
+
// Check for specific tool invocation
|
|
17
|
+
const toolCheck = manager.check(ctx.agentId, {
|
|
18
|
+
type: "tool:invoke",
|
|
19
|
+
toolId,
|
|
20
|
+
});
|
|
21
|
+
if (!toolCheck.granted) {
|
|
22
|
+
logVerbose(`[capability] ${ctx.agentId} denied access to tool ${toolId}: ${toolCheck.reason}`);
|
|
23
|
+
throw new CapabilityError(`Tool '${toolId}' access denied: ${toolCheck.reason}`, ctx.agentId, { type: "tool:invoke", toolId });
|
|
24
|
+
}
|
|
25
|
+
logVerbose(`[capability] ${ctx.agentId} granted access to ${toolId}`);
|
|
26
|
+
return await next();
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Creates a middleware that checks file access capabilities.
|
|
31
|
+
*/
|
|
32
|
+
export function createFileAccessMiddleware() {
|
|
33
|
+
return async (ctx, toolId, args, next) => {
|
|
34
|
+
// Tools that read files
|
|
35
|
+
if (toolId === "file_read" || toolId === "read_file") {
|
|
36
|
+
const path = args.path || args.file_path || args.filePath;
|
|
37
|
+
if (typeof path === "string") {
|
|
38
|
+
const manager = getCapabilityManager();
|
|
39
|
+
const check = manager.check(ctx.agentId, {
|
|
40
|
+
type: "file:read",
|
|
41
|
+
pattern: path,
|
|
42
|
+
});
|
|
43
|
+
if (!check.granted) {
|
|
44
|
+
throw new CapabilityError(`File read denied for '${path}': ${check.reason}`, ctx.agentId, { type: "file:read", pattern: path });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Tools that write files
|
|
49
|
+
if (toolId === "file_write" || toolId === "write_file") {
|
|
50
|
+
const path = args.path || args.file_path || args.filePath;
|
|
51
|
+
if (typeof path === "string") {
|
|
52
|
+
const manager = getCapabilityManager();
|
|
53
|
+
const check = manager.check(ctx.agentId, {
|
|
54
|
+
type: "file:write",
|
|
55
|
+
pattern: path,
|
|
56
|
+
});
|
|
57
|
+
if (!check.granted) {
|
|
58
|
+
throw new CapabilityError(`File write denied for '${path}': ${check.reason}`, ctx.agentId, { type: "file:write", pattern: path });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return await next();
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Creates a middleware that checks shell execution capabilities.
|
|
67
|
+
*/
|
|
68
|
+
export function createShellExecutionMiddleware() {
|
|
69
|
+
return async (ctx, toolId, args, next) => {
|
|
70
|
+
if (toolId === "shell" || toolId === "bash" || toolId === "exec") {
|
|
71
|
+
const command = args.command || args.cmd || args.shell;
|
|
72
|
+
if (typeof command === "string") {
|
|
73
|
+
const manager = getCapabilityManager();
|
|
74
|
+
const check = manager.check(ctx.agentId, {
|
|
75
|
+
type: "shell:exec",
|
|
76
|
+
pattern: command,
|
|
77
|
+
});
|
|
78
|
+
if (!check.granted) {
|
|
79
|
+
throw new CapabilityError(`Shell execution denied for '${command}': ${check.reason}`, ctx.agentId, { type: "shell:exec", pattern: command });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return await next();
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Composes multiple middlewares into a single middleware.
|
|
88
|
+
*/
|
|
89
|
+
export function composeMiddlewares(...middlewares) {
|
|
90
|
+
return async (ctx, toolId, args, finalNext) => {
|
|
91
|
+
let index = 0;
|
|
92
|
+
const dispatch = async () => {
|
|
93
|
+
if (index >= middlewares.length) {
|
|
94
|
+
return await finalNext();
|
|
95
|
+
}
|
|
96
|
+
const middleware = middlewares[index++];
|
|
97
|
+
return await middleware(ctx, toolId, args, dispatch);
|
|
98
|
+
};
|
|
99
|
+
return await dispatch();
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/** Default security middleware stack. */
|
|
103
|
+
export function createDefaultSecurityMiddleware() {
|
|
104
|
+
return composeMiddlewares(createCapabilityMiddleware(), createFileAccessMiddleware(), createShellExecutionMiddleware());
|
|
105
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Module for PoolBot
|
|
3
|
+
*
|
|
4
|
+
* Provides enterprise-grade security controls:
|
|
5
|
+
* - Audit logging for security events
|
|
6
|
+
* - Dangerous tool detection
|
|
7
|
+
* - Safe regex validation
|
|
8
|
+
* - External content validation
|
|
9
|
+
*
|
|
10
|
+
* Based on OpenClaw patterns but adapted for PoolBot architecture.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|