@cdot65/prisma-airs 0.2.2 → 0.2.4
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 +40 -28
- package/hooks/prisma-airs-audit/handler.ts +10 -3
- package/hooks/prisma-airs-context/handler.ts +56 -3
- package/hooks/prisma-airs-guard/HOOK.md +2 -5
- package/hooks/prisma-airs-guard/handler.ts +5 -0
- package/hooks/prisma-airs-outbound/handler.test.ts +62 -18
- package/hooks/prisma-airs-outbound/handler.ts +24 -1
- package/hooks/prisma-airs-tools/handler.ts +66 -62
- package/index.ts +74 -18
- package/openclaw.plugin.json +23 -6
- package/package.json +1 -1
- package/src/scan-cache.test.ts +6 -2
- package/src/scanner.test.ts +432 -22
- package/src/scanner.ts +351 -20
|
@@ -32,6 +32,7 @@ interface PluginConfig {
|
|
|
32
32
|
config?: {
|
|
33
33
|
tool_gating_enabled?: boolean;
|
|
34
34
|
high_risk_tools?: string[];
|
|
35
|
+
api_key?: string;
|
|
35
36
|
};
|
|
36
37
|
};
|
|
37
38
|
};
|
|
@@ -45,81 +46,84 @@ interface HookResult {
|
|
|
45
46
|
blockReason?: string;
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
// Shared tool lists
|
|
50
|
+
const ALL_EXTERNAL_TOOLS = [
|
|
51
|
+
"exec",
|
|
52
|
+
"Bash",
|
|
53
|
+
"bash",
|
|
54
|
+
"write",
|
|
55
|
+
"Write",
|
|
56
|
+
"edit",
|
|
57
|
+
"Edit",
|
|
58
|
+
"gateway",
|
|
59
|
+
"message",
|
|
60
|
+
"cron",
|
|
61
|
+
"browser",
|
|
62
|
+
"web_fetch",
|
|
63
|
+
"WebFetch",
|
|
64
|
+
"database",
|
|
65
|
+
"query",
|
|
66
|
+
"sql",
|
|
67
|
+
"eval",
|
|
68
|
+
"NotebookEdit",
|
|
69
|
+
];
|
|
70
|
+
const DB_TOOLS = ["exec", "Bash", "bash", "database", "query", "sql", "eval"];
|
|
71
|
+
const CODE_TOOLS = [
|
|
72
|
+
"exec",
|
|
73
|
+
"Bash",
|
|
74
|
+
"bash",
|
|
75
|
+
"write",
|
|
76
|
+
"Write",
|
|
77
|
+
"edit",
|
|
78
|
+
"Edit",
|
|
79
|
+
"eval",
|
|
80
|
+
"NotebookEdit",
|
|
81
|
+
];
|
|
82
|
+
const SENSITIVE_TOOLS = ["exec", "Bash", "bash", "gateway", "message", "cron"];
|
|
83
|
+
const WEB_TOOLS = ["web_fetch", "WebFetch", "browser", "Browser", "curl"];
|
|
84
|
+
|
|
48
85
|
// Tool blocking rules by threat category
|
|
49
86
|
const TOOL_BLOCKS: Record<string, string[]> = {
|
|
50
87
|
// AI Agent threats - block ALL external actions
|
|
51
|
-
"agent-threat":
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"write",
|
|
56
|
-
"Write",
|
|
57
|
-
"edit",
|
|
58
|
-
"Edit",
|
|
59
|
-
"gateway",
|
|
60
|
-
"message",
|
|
61
|
-
"cron",
|
|
62
|
-
"browser",
|
|
63
|
-
"web_fetch",
|
|
64
|
-
"WebFetch",
|
|
65
|
-
"database",
|
|
66
|
-
"query",
|
|
67
|
-
"sql",
|
|
68
|
-
"eval",
|
|
69
|
-
"NotebookEdit",
|
|
70
|
-
],
|
|
88
|
+
"agent-threat": ALL_EXTERNAL_TOOLS,
|
|
89
|
+
agent_threat: ALL_EXTERNAL_TOOLS,
|
|
90
|
+
agent_threat_prompt: ALL_EXTERNAL_TOOLS,
|
|
91
|
+
agent_threat_response: ALL_EXTERNAL_TOOLS,
|
|
71
92
|
|
|
72
93
|
// SQL/Database injection - block database and exec tools
|
|
73
|
-
"sql-injection":
|
|
74
|
-
db_security:
|
|
75
|
-
"db-security":
|
|
94
|
+
"sql-injection": DB_TOOLS,
|
|
95
|
+
db_security: DB_TOOLS,
|
|
96
|
+
"db-security": DB_TOOLS,
|
|
97
|
+
db_security_response: DB_TOOLS,
|
|
76
98
|
|
|
77
99
|
// Malicious code - block code execution and file writes
|
|
78
|
-
"malicious-code":
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
"write",
|
|
83
|
-
"Write",
|
|
84
|
-
"edit",
|
|
85
|
-
"Edit",
|
|
86
|
-
"eval",
|
|
87
|
-
"NotebookEdit",
|
|
88
|
-
],
|
|
89
|
-
malicious_code: [
|
|
90
|
-
"exec",
|
|
91
|
-
"Bash",
|
|
92
|
-
"bash",
|
|
93
|
-
"write",
|
|
94
|
-
"Write",
|
|
95
|
-
"edit",
|
|
96
|
-
"Edit",
|
|
97
|
-
"eval",
|
|
98
|
-
"NotebookEdit",
|
|
99
|
-
],
|
|
100
|
+
"malicious-code": CODE_TOOLS,
|
|
101
|
+
malicious_code: CODE_TOOLS,
|
|
102
|
+
malicious_code_prompt: CODE_TOOLS,
|
|
103
|
+
malicious_code_response: CODE_TOOLS,
|
|
100
104
|
|
|
101
105
|
// Prompt injection - block sensitive tools
|
|
102
|
-
"prompt-injection":
|
|
103
|
-
prompt_injection:
|
|
106
|
+
"prompt-injection": SENSITIVE_TOOLS,
|
|
107
|
+
prompt_injection: SENSITIVE_TOOLS,
|
|
104
108
|
|
|
105
109
|
// Malicious URLs - block web access
|
|
106
|
-
"malicious-url":
|
|
107
|
-
malicious_url:
|
|
108
|
-
url_filtering_prompt:
|
|
110
|
+
"malicious-url": WEB_TOOLS,
|
|
111
|
+
malicious_url: WEB_TOOLS,
|
|
112
|
+
url_filtering_prompt: WEB_TOOLS,
|
|
113
|
+
url_filtering_response: WEB_TOOLS,
|
|
114
|
+
|
|
115
|
+
// Toxic content - block code/write tools
|
|
116
|
+
toxic_content: CODE_TOOLS,
|
|
117
|
+
toxic_content_prompt: CODE_TOOLS,
|
|
118
|
+
toxic_content_response: CODE_TOOLS,
|
|
119
|
+
|
|
120
|
+
// Topic violations - block sensitive tools
|
|
121
|
+
topic_violation: SENSITIVE_TOOLS,
|
|
122
|
+
topic_violation_prompt: SENSITIVE_TOOLS,
|
|
123
|
+
topic_violation_response: SENSITIVE_TOOLS,
|
|
109
124
|
|
|
110
125
|
// Scan failure - block high-risk tools
|
|
111
|
-
"scan-failure": [
|
|
112
|
-
"exec",
|
|
113
|
-
"Bash",
|
|
114
|
-
"bash",
|
|
115
|
-
"write",
|
|
116
|
-
"Write",
|
|
117
|
-
"edit",
|
|
118
|
-
"Edit",
|
|
119
|
-
"gateway",
|
|
120
|
-
"message",
|
|
121
|
-
"cron",
|
|
122
|
-
],
|
|
126
|
+
"scan-failure": SENSITIVE_TOOLS.concat(["write", "Write", "edit", "Edit"]),
|
|
123
127
|
};
|
|
124
128
|
|
|
125
129
|
// Default high-risk tools (blocked on any threat)
|
package/index.ts
CHANGED
|
@@ -11,13 +11,17 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { scan, isConfigured, ScanRequest } from "./src/scanner";
|
|
14
|
-
import
|
|
15
|
-
import
|
|
14
|
+
import guardHandler from "./hooks/prisma-airs-guard/handler";
|
|
15
|
+
import auditHandler from "./hooks/prisma-airs-audit/handler";
|
|
16
|
+
import contextHandler from "./hooks/prisma-airs-context/handler";
|
|
17
|
+
import outboundHandler from "./hooks/prisma-airs-outbound/handler";
|
|
18
|
+
import toolsHandler from "./hooks/prisma-airs-tools/handler";
|
|
16
19
|
|
|
17
20
|
// Plugin config interface
|
|
18
21
|
interface PrismaAirsConfig {
|
|
19
22
|
profile_name?: string;
|
|
20
23
|
app_name?: string;
|
|
24
|
+
api_key?: string;
|
|
21
25
|
reminder_enabled?: boolean;
|
|
22
26
|
}
|
|
23
27
|
|
|
@@ -72,7 +76,8 @@ interface PluginApi {
|
|
|
72
76
|
execute: (_id: string, params: ScanRequest) => Promise<ToolResult>;
|
|
73
77
|
}) => void;
|
|
74
78
|
registerCli: (setup: (ctx: { program: unknown }) => void, opts: { commands: string[] }) => void;
|
|
75
|
-
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
80
|
+
on: (hookName: string, handler: (...args: any[]) => any, opts?: { priority?: number }) => void;
|
|
76
81
|
}
|
|
77
82
|
|
|
78
83
|
// Get plugin config from OpenClaw config
|
|
@@ -91,6 +96,7 @@ function buildScanRequest(params: ScanRequest | undefined, config: PrismaAirsCon
|
|
|
91
96
|
appName: params?.appName ?? config.app_name ?? "openclaw",
|
|
92
97
|
appUser: params?.appUser,
|
|
93
98
|
aiModel: params?.aiModel,
|
|
99
|
+
apiKey: config.api_key,
|
|
94
100
|
};
|
|
95
101
|
}
|
|
96
102
|
|
|
@@ -101,22 +107,71 @@ export default function register(api: PluginApi): void {
|
|
|
101
107
|
`Prisma AIRS plugin loaded (reminder_enabled=${config.reminder_enabled ?? true})`
|
|
102
108
|
);
|
|
103
109
|
|
|
104
|
-
// Register hooks
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
// Register lifecycle hooks via api.on()
|
|
111
|
+
// Each adapter injects api.config as cfg for handler compatibility.
|
|
112
|
+
|
|
113
|
+
// Guard: inject security scanning reminder at agent bootstrap
|
|
114
|
+
api.on(
|
|
115
|
+
"before_agent_start",
|
|
116
|
+
async () => {
|
|
117
|
+
const files: { path: string; content: string; source?: string }[] = [];
|
|
118
|
+
await guardHandler({
|
|
119
|
+
type: "agent",
|
|
120
|
+
action: "bootstrap",
|
|
121
|
+
context: { bootstrapFiles: files, cfg: api.config },
|
|
122
|
+
});
|
|
123
|
+
if (files.length > 0) {
|
|
124
|
+
return { systemPrompt: files.map((f) => f.content).join("\n\n") };
|
|
125
|
+
}
|
|
126
|
+
return undefined;
|
|
127
|
+
},
|
|
128
|
+
{ priority: 100 }
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
// Audit: fire-and-forget inbound message scan logging
|
|
132
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
133
|
+
api.on("message_received", async (event: any, ctx: any) => {
|
|
134
|
+
await auditHandler(event, { ...ctx, cfg: api.config });
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Context: inject security warnings before agent processes message
|
|
138
|
+
api.on(
|
|
139
|
+
"before_agent_start",
|
|
140
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
141
|
+
async (event: any, ctx: any) => {
|
|
142
|
+
return await contextHandler(
|
|
143
|
+
{
|
|
144
|
+
sessionKey: ctx.sessionKey,
|
|
145
|
+
message: { content: event.prompt },
|
|
146
|
+
messages: event.messages,
|
|
147
|
+
},
|
|
148
|
+
{ ...ctx, cfg: api.config }
|
|
149
|
+
);
|
|
150
|
+
},
|
|
151
|
+
{ priority: 50 }
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
// Outbound: scan and block/mask outgoing responses
|
|
155
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
156
|
+
api.on("message_sending", async (event: any, ctx: any) => {
|
|
157
|
+
return await outboundHandler(event, { ...ctx, cfg: api.config });
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Tools: block dangerous tool calls during active threats
|
|
161
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
162
|
+
api.on("before_tool_call", async (event: any, ctx: any) => {
|
|
163
|
+
return await toolsHandler(event, { ...ctx, cfg: api.config });
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
api.logger.info("Registered 5 lifecycle hooks");
|
|
112
167
|
|
|
113
168
|
// Register RPC method for status check
|
|
114
169
|
api.registerGatewayMethod("prisma-airs.status", ({ respond }) => {
|
|
115
170
|
const cfg = getPluginConfig(api);
|
|
116
|
-
const hasApiKey = isConfigured();
|
|
171
|
+
const hasApiKey = isConfigured(cfg.api_key);
|
|
117
172
|
respond(true, {
|
|
118
173
|
plugin: "prisma-airs",
|
|
119
|
-
version: "0.2.
|
|
174
|
+
version: "0.2.4",
|
|
120
175
|
config: {
|
|
121
176
|
profile_name: cfg.profile_name ?? "default",
|
|
122
177
|
app_name: cfg.app_name ?? "openclaw",
|
|
@@ -159,7 +214,8 @@ export default function register(api: PluginApi): void {
|
|
|
159
214
|
name: "prisma_airs_scan",
|
|
160
215
|
description:
|
|
161
216
|
"Scan content for security threats via Prisma AIRS. " +
|
|
162
|
-
"Detects prompt injection,
|
|
217
|
+
"Detects prompt injection, DLP, malicious URLs, toxic content, malicious code, " +
|
|
218
|
+
"agent threats, topic violations, DB security, and ungrounded responses. " +
|
|
163
219
|
"Returns action (allow/warn/block), severity, and detected categories.",
|
|
164
220
|
parameters: {
|
|
165
221
|
type: "object",
|
|
@@ -212,16 +268,16 @@ export default function register(api: PluginApi): void {
|
|
|
212
268
|
.description("Show Prisma AIRS plugin status")
|
|
213
269
|
.action(() => {
|
|
214
270
|
const cfg = getPluginConfig(api);
|
|
215
|
-
const hasKey = isConfigured();
|
|
271
|
+
const hasKey = isConfigured(cfg.api_key);
|
|
216
272
|
console.log("Prisma AIRS Plugin Status");
|
|
217
273
|
console.log("-------------------------");
|
|
218
|
-
console.log(`Version: 0.2.
|
|
274
|
+
console.log(`Version: 0.2.4`);
|
|
219
275
|
console.log(`Profile: ${cfg.profile_name ?? "default"}`);
|
|
220
276
|
console.log(`App Name: ${cfg.app_name ?? "openclaw"}`);
|
|
221
277
|
console.log(`Reminder: ${cfg.reminder_enabled ?? true}`);
|
|
222
278
|
console.log(`API Key: ${hasKey ? "configured" : "MISSING"}`);
|
|
223
279
|
if (!hasKey) {
|
|
224
|
-
console.log("\nSet
|
|
280
|
+
console.log("\nSet API key in plugin config");
|
|
225
281
|
}
|
|
226
282
|
});
|
|
227
283
|
|
|
@@ -266,7 +322,7 @@ export default function register(api: PluginApi): void {
|
|
|
266
322
|
// Export plugin metadata for discovery
|
|
267
323
|
export const id = "prisma-airs";
|
|
268
324
|
export const name = "Prisma AIRS Security";
|
|
269
|
-
export const version = "0.2.
|
|
325
|
+
export const version = "0.2.4";
|
|
270
326
|
|
|
271
327
|
// Re-export scanner types and functions
|
|
272
328
|
export { scan, isConfigured } from "./src/scanner";
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "prisma-airs",
|
|
3
3
|
"name": "Prisma AIRS Security",
|
|
4
4
|
"description": "AI Runtime Security - full AIRS detection suite with audit logging, context injection, outbound blocking, and tool gating",
|
|
5
|
-
"version": "0.2.
|
|
5
|
+
"version": "0.2.4",
|
|
6
6
|
"entrypoint": "index.ts",
|
|
7
7
|
"hooks": [
|
|
8
8
|
"hooks/prisma-airs-guard",
|
|
@@ -63,12 +63,32 @@
|
|
|
63
63
|
"high_risk_tools": {
|
|
64
64
|
"type": "array",
|
|
65
65
|
"items": { "type": "string" },
|
|
66
|
-
"default": [
|
|
66
|
+
"default": [
|
|
67
|
+
"exec",
|
|
68
|
+
"Bash",
|
|
69
|
+
"bash",
|
|
70
|
+
"write",
|
|
71
|
+
"Write",
|
|
72
|
+
"edit",
|
|
73
|
+
"Edit",
|
|
74
|
+
"gateway",
|
|
75
|
+
"message",
|
|
76
|
+
"cron"
|
|
77
|
+
],
|
|
67
78
|
"description": "Tools to block on any detected threat"
|
|
79
|
+
},
|
|
80
|
+
"api_key": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"description": "Prisma AIRS API key from Strata Cloud Manager"
|
|
68
83
|
}
|
|
69
84
|
}
|
|
70
85
|
},
|
|
71
86
|
"uiHints": {
|
|
87
|
+
"api_key": {
|
|
88
|
+
"label": "API Key",
|
|
89
|
+
"sensitive": true,
|
|
90
|
+
"placeholder": "Enter your PANW AI Security API key"
|
|
91
|
+
},
|
|
72
92
|
"profile_name": {
|
|
73
93
|
"label": "Profile Name",
|
|
74
94
|
"placeholder": "default"
|
|
@@ -109,8 +129,5 @@
|
|
|
109
129
|
"description": "Tools blocked on any threat detection"
|
|
110
130
|
}
|
|
111
131
|
},
|
|
112
|
-
"requires": {
|
|
113
|
-
"env": ["PANW_AI_SEC_API_KEY"],
|
|
114
|
-
"envOptional": ["PANW_AI_SEC_PROFILE_NAME"]
|
|
115
|
-
}
|
|
132
|
+
"requires": {}
|
|
116
133
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdot65/prisma-airs",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Prisma AIRS (AI Runtime Security) plugin for OpenClaw - Full security suite with audit logging, context injection, outbound blocking, and tool gating",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
package/src/scan-cache.test.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
stopCleanup,
|
|
14
14
|
startCleanup,
|
|
15
15
|
} from "./scan-cache";
|
|
16
|
+
import { defaultPromptDetected, defaultResponseDetected } from "./scanner";
|
|
16
17
|
import type { ScanResult } from "./scanner";
|
|
17
18
|
|
|
18
19
|
// Mock scan result
|
|
@@ -23,9 +24,12 @@ const mockScanResult: ScanResult = {
|
|
|
23
24
|
scanId: "scan_123",
|
|
24
25
|
reportId: "report_456",
|
|
25
26
|
profileName: "default",
|
|
26
|
-
promptDetected: { injection: true
|
|
27
|
-
responseDetected:
|
|
27
|
+
promptDetected: { ...defaultPromptDetected(), injection: true },
|
|
28
|
+
responseDetected: defaultResponseDetected(),
|
|
28
29
|
latencyMs: 100,
|
|
30
|
+
timeout: false,
|
|
31
|
+
hasError: false,
|
|
32
|
+
contentErrors: [],
|
|
29
33
|
};
|
|
30
34
|
|
|
31
35
|
describe("scan-cache", () => {
|