shroud-privacy 2.2.11 → 2.2.13

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 (40) hide show
  1. package/README.md +19 -10
  2. package/dist/hooks.js +246 -14
  3. package/openclaw.plugin.json +1 -1
  4. package/package.json +3 -2
  5. package/dist/agent-session.d.ts +0 -259
  6. package/dist/agent-session.js +0 -693
  7. package/dist/compliance.d.ts +0 -44
  8. package/dist/compliance.js +0 -76
  9. package/dist/dashboard.d.ts +0 -42
  10. package/dist/dashboard.js +0 -1558
  11. package/dist/detectors/injection-multilingual.d.ts +0 -27
  12. package/dist/detectors/injection-multilingual.js +0 -399
  13. package/dist/detectors/injection-signatures.d.ts +0 -26
  14. package/dist/detectors/injection-signatures.js +0 -508
  15. package/dist/detectors/injection.d.ts +0 -56
  16. package/dist/detectors/injection.js +0 -269
  17. package/dist/detectors/tool-guard.d.ts +0 -27
  18. package/dist/detectors/tool-guard.js +0 -418
  19. package/dist/event-grader.d.ts +0 -97
  20. package/dist/event-grader.js +0 -214
  21. package/dist/exposure.d.ts +0 -29
  22. package/dist/exposure.js +0 -72
  23. package/dist/policy.d.ts +0 -99
  24. package/dist/policy.js +0 -212
  25. package/dist/profiler-analysis.d.ts +0 -35
  26. package/dist/profiler-analysis.js +0 -230
  27. package/dist/profiler-store.d.ts +0 -33
  28. package/dist/profiler-store.js +0 -118
  29. package/dist/profiler-types.d.ts +0 -128
  30. package/dist/profiler-types.js +0 -16
  31. package/dist/profiler.d.ts +0 -81
  32. package/dist/profiler.js +0 -392
  33. package/dist/security-event.d.ts +0 -70
  34. package/dist/security-event.js +0 -80
  35. package/dist/siem.d.ts +0 -49
  36. package/dist/siem.js +0 -113
  37. package/dist/signature-loader.d.ts +0 -113
  38. package/dist/signature-loader.js +0 -255
  39. package/dist/store-file.d.ts +0 -26
  40. package/dist/store-file.js +0 -79
package/dist/profiler.js DELETED
@@ -1,392 +0,0 @@
1
- /**
2
- * Behavioural profiler for LLM agent sessions (Track 3).
3
- *
4
- * Extracts per-turn feature vectors from request/response text,
5
- * maintains session aggregates, and detects anomalies against
6
- * cross-session baselines. All feature extraction is synchronous
7
- * and zero-dependency.
8
- */
9
- import { createHash } from "node:crypto";
10
- import { detectAnomalies } from "./profiler-analysis.js";
11
- /** ~30 common imperative/directive verbs. */
12
- const DIRECTIVE_VERBS = /\b(?:do|create|run|execute|build|make|write|read|delete|remove|update|modify|change|set|get|fetch|send|post|deploy|install|configure|enable|disable|start|stop|kill|restart|migrate|revert|rollback)\b/gi;
13
- /**
14
- * Behavioural profiler. One instance per session.
15
- */
16
- export class BehaviouralProfiler {
17
- _config;
18
- _store;
19
- _sessionId;
20
- _agentBuildId = "";
21
- _turns = [];
22
- _alerts = [];
23
- _previousTurnBigrams = new Set();
24
- _allSessionWords = new Set();
25
- _startedAt;
26
- // Partial features accumulated during a turn (request side fills, response side completes)
27
- _pendingRequest = null;
28
- constructor(config, store) {
29
- this._config = config;
30
- this._store = store;
31
- this._sessionId = createHash("sha256")
32
- .update(`profile:${Date.now()}:${Math.random()}`)
33
- .digest("hex")
34
- .slice(0, 12);
35
- this._startedAt = Date.now();
36
- }
37
- /** Set the agent build ID (from system prompt hash). */
38
- setAgentBuildId(buildId) {
39
- this._agentBuildId = buildId;
40
- }
41
- /**
42
- * Extract features from the outbound request text.
43
- * Called after obfuscation, before sending to LLM.
44
- */
45
- extractRequestFeatures(text, entityCategoryCounts, toolCalls, imagePayloads) {
46
- const tokenEstimate = Math.ceil(text.length / 4);
47
- const totalEntities = Object.values(entityCategoryCounts).reduce((a, b) => a + b, 0);
48
- // Entity density
49
- const entityDensityPer1k = tokenEstimate > 0
50
- ? (totalEntities / tokenEstimate) * 1000
51
- : 0;
52
- // Shannon entropy of entity category distribution
53
- const entityCategoryEntropy = shannonEntropy(entityCategoryCounts);
54
- // Directive verbs
55
- const directiveVerbCount = (text.match(DIRECTIVE_VERBS) || []).length;
56
- // Questions
57
- const questionCount = (text.match(/\?\s/g) || []).length + (text.endsWith("?") ? 1 : 0);
58
- // Command-to-question ratio
59
- const commandToQuestionRatio = questionCount > 0
60
- ? directiveVerbCount / questionCount
61
- : directiveVerbCount > 0 ? Infinity : 0;
62
- // Tool calls
63
- const toolCallCount = toolCalls?.length ?? 0;
64
- const toolNames = toolCalls?.map((tc) => tc.name) ?? [];
65
- // Lexical analysis
66
- const words = extractWords(text);
67
- const bigrams = computeBigrams(words);
68
- const lexicalOverlapWithPrevious = this._previousTurnBigrams.size > 0
69
- ? jaccardSimilarity(bigrams, this._previousTurnBigrams)
70
- : 1.0; // First turn: assume no discontinuity
71
- // New vocabulary rate
72
- const newWords = words.filter((w) => !this._allSessionWords.has(w));
73
- const newVocabularyRate = words.length > 0 ? newWords.length / words.length : 0;
74
- // Update cumulative word tracking
75
- for (const w of words)
76
- this._allSessionWords.add(w);
77
- this._previousTurnBigrams = bigrams;
78
- // Script/language detection
79
- const { script, nonLatinRatio } = detectScript(text);
80
- this._pendingRequest = {
81
- entityCategoryCounts,
82
- entityDensityPer1k,
83
- entityCategoryEntropy,
84
- toolCallCount,
85
- toolNames,
86
- directiveVerbCount,
87
- questionCount,
88
- commandToQuestionRatio,
89
- lexicalOverlapWithPrevious,
90
- newVocabularyRate,
91
- turnIndex: this._turns.length,
92
- timestamp: Date.now(),
93
- tokenEstimate,
94
- detectedScript: script,
95
- nonLatinRatio,
96
- imagePayloadCount: imagePayloads?.count ?? 0,
97
- imagePayloadBytes: imagePayloads?.totalBytes ?? 0,
98
- // Cache metrics filled in from response (extractResponseFeatures)
99
- inputTokens: 0,
100
- outputTokens: 0,
101
- cacheReadTokens: 0,
102
- cacheWriteTokens: 0,
103
- cacheHitRatio: 0,
104
- };
105
- }
106
- /**
107
- * Complete the feature vector with response-side features.
108
- * Called after deobfuscation of the LLM response.
109
- */
110
- extractResponseFeatures(responseText, requestEntities, cacheUsage) {
111
- if (!this._pendingRequest)
112
- return null;
113
- const responseLength = responseText.length;
114
- // Entity echo rate: how many request entities appear in the response
115
- let echoCount = 0;
116
- for (const entity of requestEntities) {
117
- if (responseText.includes(entity))
118
- echoCount++;
119
- }
120
- const entityEchoRate = requestEntities.length > 0
121
- ? echoCount / requestEntities.length
122
- : 0;
123
- const features = {
124
- entityCategoryCounts: this._pendingRequest.entityCategoryCounts ?? {},
125
- entityDensityPer1k: this._pendingRequest.entityDensityPer1k ?? 0,
126
- entityCategoryEntropy: this._pendingRequest.entityCategoryEntropy ?? 0,
127
- toolCallCount: this._pendingRequest.toolCallCount ?? 0,
128
- toolNames: this._pendingRequest.toolNames ?? [],
129
- directiveVerbCount: this._pendingRequest.directiveVerbCount ?? 0,
130
- questionCount: this._pendingRequest.questionCount ?? 0,
131
- commandToQuestionRatio: this._pendingRequest.commandToQuestionRatio ?? 0,
132
- responseLength,
133
- entityEchoRate,
134
- lexicalOverlapWithPrevious: this._pendingRequest.lexicalOverlapWithPrevious ?? 1,
135
- newVocabularyRate: this._pendingRequest.newVocabularyRate ?? 0,
136
- turnIndex: this._pendingRequest.turnIndex ?? this._turns.length,
137
- timestamp: this._pendingRequest.timestamp ?? Date.now(),
138
- tokenEstimate: this._pendingRequest.tokenEstimate ?? 0,
139
- detectedScript: this._pendingRequest.detectedScript ?? "latin",
140
- nonLatinRatio: this._pendingRequest.nonLatinRatio ?? 0,
141
- imagePayloadCount: this._pendingRequest.imagePayloadCount ?? 0,
142
- imagePayloadBytes: this._pendingRequest.imagePayloadBytes ?? 0,
143
- inputTokens: cacheUsage?.inputTokens ?? 0,
144
- outputTokens: cacheUsage?.outputTokens ?? 0,
145
- cacheReadTokens: cacheUsage?.cacheReadTokens ?? 0,
146
- cacheWriteTokens: cacheUsage?.cacheWriteTokens ?? 0,
147
- cacheHitRatio: cacheUsage && cacheUsage.inputTokens > 0
148
- ? cacheUsage.cacheReadTokens / cacheUsage.inputTokens
149
- : 0,
150
- };
151
- this._turns.push(features);
152
- this._pendingRequest = null;
153
- return features;
154
- }
155
- /**
156
- * Analyze a completed turn against the baseline.
157
- * Returns anomaly alerts (empty if in learning mode or insufficient baseline).
158
- */
159
- analyzeTurn(features) {
160
- if (this._config.mode === "learning")
161
- return [];
162
- if (!this._agentBuildId)
163
- return [];
164
- const baseline = this._store.load(this._agentBuildId);
165
- if (!baseline)
166
- return [];
167
- if (baseline.sessionCount < this._config.minBaseline)
168
- return [];
169
- const knownTools = new Set(baseline.toolProfile);
170
- const knownCategories = new Set(baseline.categoryProfile);
171
- const alerts = detectAnomalies(features, baseline.features, this._config.sigma, knownTools, knownCategories);
172
- this._alerts.push(...alerts);
173
- return alerts;
174
- }
175
- /**
176
- * Finalize the session: compute aggregates, update baseline store.
177
- */
178
- finalizeSession() {
179
- const aggregates = this._computeAggregates();
180
- const profile = {
181
- sessionId: this._sessionId,
182
- agentBuildId: this._agentBuildId,
183
- startedAt: this._startedAt,
184
- turns: this._turns,
185
- aggregates,
186
- };
187
- // Update the persistent baseline
188
- if (this._agentBuildId && this._turns.length > 0) {
189
- this._store.updateFromSession(this._agentBuildId, profile);
190
- }
191
- return profile;
192
- }
193
- /** Get all anomaly alerts from this session. */
194
- getAlerts() {
195
- return this._alerts;
196
- }
197
- /** Get LLM cache usage summary for the current session. */
198
- getCacheStats() {
199
- let totalInput = 0, totalOutput = 0, totalCacheRead = 0, totalCacheWrite = 0;
200
- for (const t of this._turns) {
201
- totalInput += t.inputTokens;
202
- totalOutput += t.outputTokens;
203
- totalCacheRead += t.cacheReadTokens;
204
- totalCacheWrite += t.cacheWriteTokens;
205
- }
206
- return {
207
- totalInput, totalOutput, totalCacheRead, totalCacheWrite,
208
- hitRatio: totalInput > 0 ? totalCacheRead / totalInput : 0,
209
- turns: this._turns.length,
210
- };
211
- }
212
- /** Get current session profile (without finalizing). */
213
- getSessionProfile() {
214
- return {
215
- sessionId: this._sessionId,
216
- agentBuildId: this._agentBuildId,
217
- startedAt: this._startedAt,
218
- turns: [...this._turns],
219
- aggregates: this._computeAggregates(),
220
- };
221
- }
222
- _computeAggregates() {
223
- if (this._turns.length === 0) {
224
- return {
225
- dominantCategories: [],
226
- toolSequenceFingerprint: "",
227
- averageEntityDensity: 0,
228
- averageDirectiveVerbCount: 0,
229
- averageResponseLength: 0,
230
- averageLexicalOverlap: 0,
231
- turnCount: 0,
232
- };
233
- }
234
- // Category frequency
235
- const catFreq = {};
236
- const allTools = new Set();
237
- let totalDensity = 0;
238
- let totalDirective = 0;
239
- let totalResponse = 0;
240
- let totalOverlap = 0;
241
- for (const turn of this._turns) {
242
- for (const [cat, count] of Object.entries(turn.entityCategoryCounts)) {
243
- catFreq[cat] = (catFreq[cat] ?? 0) + count;
244
- }
245
- for (const tool of turn.toolNames)
246
- allTools.add(tool);
247
- totalDensity += turn.entityDensityPer1k;
248
- totalDirective += turn.directiveVerbCount;
249
- totalResponse += turn.responseLength;
250
- totalOverlap += turn.lexicalOverlapWithPrevious;
251
- }
252
- const n = this._turns.length;
253
- const dominantCategories = Object.entries(catFreq)
254
- .sort((a, b) => b[1] - a[1])
255
- .slice(0, 3)
256
- .map(([cat]) => cat);
257
- const toolList = [...allTools].sort();
258
- const toolSequenceFingerprint = createHash("sha256")
259
- .update(toolList.join(","))
260
- .digest("hex")
261
- .slice(0, 12);
262
- return {
263
- dominantCategories,
264
- toolSequenceFingerprint,
265
- averageEntityDensity: totalDensity / n,
266
- averageDirectiveVerbCount: totalDirective / n,
267
- averageResponseLength: totalResponse / n,
268
- averageLexicalOverlap: totalOverlap / n,
269
- turnCount: n,
270
- };
271
- }
272
- }
273
- // ===================================================================
274
- // Script/language detection (zero-dep, Unicode range based)
275
- // ===================================================================
276
- /** Detect the dominant script and non-Latin ratio of text. */
277
- function detectScript(text) {
278
- let latin = 0;
279
- let cjk = 0;
280
- let cyrillic = 0;
281
- let arabic = 0;
282
- let devanagari = 0;
283
- let hangul = 0;
284
- let total = 0;
285
- for (const ch of text) {
286
- const cp = ch.codePointAt(0);
287
- if (cp < 0x20)
288
- continue; // control chars
289
- if (cp < 0x7F) {
290
- latin++;
291
- total++;
292
- continue;
293
- } // Basic Latin (ASCII)
294
- if (cp >= 0x00C0 && cp <= 0x024F) {
295
- latin++;
296
- total++;
297
- continue;
298
- } // Latin Extended
299
- if (cp >= 0x4E00 && cp <= 0x9FFF) {
300
- cjk++;
301
- total++;
302
- continue;
303
- } // CJK Unified
304
- if (cp >= 0x3040 && cp <= 0x30FF) {
305
- cjk++;
306
- total++;
307
- continue;
308
- } // Hiragana + Katakana
309
- if (cp >= 0x3400 && cp <= 0x4DBF) {
310
- cjk++;
311
- total++;
312
- continue;
313
- } // CJK Extension A
314
- if (cp >= 0x0400 && cp <= 0x04FF) {
315
- cyrillic++;
316
- total++;
317
- continue;
318
- } // Cyrillic
319
- if (cp >= 0x0600 && cp <= 0x06FF) {
320
- arabic++;
321
- total++;
322
- continue;
323
- } // Arabic
324
- if (cp >= 0x0900 && cp <= 0x097F) {
325
- devanagari++;
326
- total++;
327
- continue;
328
- } // Devanagari
329
- if (cp >= 0xAC00 && cp <= 0xD7AF) {
330
- hangul++;
331
- total++;
332
- continue;
333
- } // Hangul
334
- total++;
335
- }
336
- if (total === 0)
337
- return { script: "latin", nonLatinRatio: 0 };
338
- const nonLatin = total - latin;
339
- const nonLatinRatio = nonLatin / total;
340
- // Determine dominant script
341
- const counts = [
342
- ["latin", latin],
343
- ["cjk", cjk],
344
- ["cyrillic", cyrillic],
345
- ["arabic", arabic],
346
- ["devanagari", devanagari],
347
- ["hangul", hangul],
348
- ];
349
- counts.sort((a, b) => b[1] - a[1]);
350
- const dominant = counts[0][1] > 0 ? counts[0][0] : "unknown";
351
- return { script: dominant, nonLatinRatio };
352
- }
353
- // ===================================================================
354
- // Text analysis utilities
355
- // ===================================================================
356
- /** Extract lowercase words from text. */
357
- function extractWords(text) {
358
- return text.toLowerCase().match(/\b[a-z]{2,}\b/g) || [];
359
- }
360
- /** Compute word bigrams from a word array. */
361
- function computeBigrams(words) {
362
- const bigrams = new Set();
363
- for (let i = 0; i < words.length - 1; i++) {
364
- bigrams.add(`${words[i]} ${words[i + 1]}`);
365
- }
366
- return bigrams;
367
- }
368
- /** Jaccard similarity between two sets. */
369
- function jaccardSimilarity(a, b) {
370
- if (a.size === 0 && b.size === 0)
371
- return 1;
372
- let intersection = 0;
373
- for (const item of a) {
374
- if (b.has(item))
375
- intersection++;
376
- }
377
- const union = a.size + b.size - intersection;
378
- return union > 0 ? intersection / union : 0;
379
- }
380
- /** Shannon entropy of a category count distribution. */
381
- function shannonEntropy(counts) {
382
- const values = Object.values(counts).filter((v) => v > 0);
383
- if (values.length === 0)
384
- return 0;
385
- const total = values.reduce((a, b) => a + b, 0);
386
- let entropy = 0;
387
- for (const v of values) {
388
- const p = v / total;
389
- entropy -= p * Math.log2(p);
390
- }
391
- return entropy;
392
- }
@@ -1,70 +0,0 @@
1
- /**
2
- * Security event types and in-memory event bus.
3
- *
4
- * Shared by all security tracks: injection detection, canary monitoring,
5
- * and behavioural profiling. Events are accumulated in memory and
6
- * queryable during the session.
7
- */
8
- /** Threat classes for injection signature detection. */
9
- export declare enum ThreatClass {
10
- INSTRUCTION_OVERRIDE = "instruction_override",
11
- ROLE_SWITCH = "role_switch",
12
- PROMPT_EXTRACTION = "prompt_extraction",
13
- CONVERSATION_MOCKUP = "conversation_mockup",
14
- ENCODING_BYPASS = "encoding_bypass",
15
- DATA_EXFILTRATION = "data_exfiltration",
16
- PRIVILEGE_ESCALATION = "privilege_escalation",
17
- MCP_TOOL_POISONING = "mcp_tool_poisoning"
18
- }
19
- /** Severity levels for security events. */
20
- export type SecuritySeverity = "low" | "medium" | "high";
21
- /** Actions taken in response to a security event. */
22
- export type SecurityAction = "flagged" | "blocked";
23
- /** A security event emitted by any of the three tracks. */
24
- export interface SecurityEvent {
25
- timestamp: number;
26
- eventType: "injection_detected" | "canary_triggered" | "anomaly_detected";
27
- direction: "request" | "response";
28
- threatClass: ThreatClass;
29
- signatureId: string;
30
- severity: SecuritySeverity;
31
- matchedText: string;
32
- matchStart: number;
33
- matchEnd: number;
34
- textLength: number;
35
- action: SecurityAction;
36
- description: string;
37
- /** Agent identity — which agent's session produced this event. */
38
- agentBuildId?: string;
39
- /** Human-readable agent label. */
40
- agentLabel?: string;
41
- /** Agent session ID. */
42
- agentSessionId?: string;
43
- /** Channel the event originated from (slack, whatsapp, tui, etc.). */
44
- channel?: string;
45
- }
46
- /** Aggregate statistics for security events. */
47
- export interface SecurityStats {
48
- totalEvents: number;
49
- byThreatClass: Record<string, number>;
50
- bySeverity: Record<string, number>;
51
- byDirection: Record<string, number>;
52
- blockedCount: number;
53
- flaggedCount: number;
54
- }
55
- /**
56
- * In-memory accumulator for security events. Bounded by maxEvents
57
- * to prevent unbounded growth during long sessions.
58
- */
59
- export declare class SecurityEventBus {
60
- private _events;
61
- private _maxEvents;
62
- private _listeners;
63
- constructor(maxEvents?: number);
64
- emit(event: SecurityEvent): void;
65
- /** Subscribe to real-time events. Returns unsubscribe function. */
66
- onEvent(listener: (event: SecurityEvent) => void): () => void;
67
- getEvents(): readonly SecurityEvent[];
68
- getStats(): SecurityStats;
69
- clear(): void;
70
- }
@@ -1,80 +0,0 @@
1
- /**
2
- * Security event types and in-memory event bus.
3
- *
4
- * Shared by all security tracks: injection detection, canary monitoring,
5
- * and behavioural profiling. Events are accumulated in memory and
6
- * queryable during the session.
7
- */
8
- /** Threat classes for injection signature detection. */
9
- export var ThreatClass;
10
- (function (ThreatClass) {
11
- ThreatClass["INSTRUCTION_OVERRIDE"] = "instruction_override";
12
- ThreatClass["ROLE_SWITCH"] = "role_switch";
13
- ThreatClass["PROMPT_EXTRACTION"] = "prompt_extraction";
14
- ThreatClass["CONVERSATION_MOCKUP"] = "conversation_mockup";
15
- ThreatClass["ENCODING_BYPASS"] = "encoding_bypass";
16
- ThreatClass["DATA_EXFILTRATION"] = "data_exfiltration";
17
- ThreatClass["PRIVILEGE_ESCALATION"] = "privilege_escalation";
18
- ThreatClass["MCP_TOOL_POISONING"] = "mcp_tool_poisoning";
19
- })(ThreatClass || (ThreatClass = {}));
20
- /**
21
- * In-memory accumulator for security events. Bounded by maxEvents
22
- * to prevent unbounded growth during long sessions.
23
- */
24
- export class SecurityEventBus {
25
- _events = [];
26
- _maxEvents;
27
- _listeners = [];
28
- constructor(maxEvents = 500) {
29
- this._maxEvents = maxEvents;
30
- }
31
- emit(event) {
32
- this._events.push(event);
33
- // Evict oldest if over capacity
34
- if (this._events.length > this._maxEvents) {
35
- this._events.splice(0, this._events.length - this._maxEvents);
36
- }
37
- // Notify listeners (SSE dashboard, SIEM, etc.)
38
- for (const listener of this._listeners) {
39
- try {
40
- listener(event);
41
- }
42
- catch { /* listeners must not break emit */ }
43
- }
44
- }
45
- /** Subscribe to real-time events. Returns unsubscribe function. */
46
- onEvent(listener) {
47
- this._listeners.push(listener);
48
- return () => {
49
- const idx = this._listeners.indexOf(listener);
50
- if (idx >= 0)
51
- this._listeners.splice(idx, 1);
52
- };
53
- }
54
- getEvents() {
55
- return this._events;
56
- }
57
- getStats() {
58
- const stats = {
59
- totalEvents: this._events.length,
60
- byThreatClass: {},
61
- bySeverity: {},
62
- byDirection: {},
63
- blockedCount: 0,
64
- flaggedCount: 0,
65
- };
66
- for (const e of this._events) {
67
- stats.byThreatClass[e.threatClass] = (stats.byThreatClass[e.threatClass] ?? 0) + 1;
68
- stats.bySeverity[e.severity] = (stats.bySeverity[e.severity] ?? 0) + 1;
69
- stats.byDirection[e.direction] = (stats.byDirection[e.direction] ?? 0) + 1;
70
- if (e.action === "blocked")
71
- stats.blockedCount++;
72
- else
73
- stats.flaggedCount++;
74
- }
75
- return stats;
76
- }
77
- clear() {
78
- this._events = [];
79
- }
80
- }
package/dist/siem.d.ts DELETED
@@ -1,49 +0,0 @@
1
- /**
2
- * SIEM integration — ships security events to external systems.
3
- *
4
- * Two output modes:
5
- * 1. Webhook POST: sends events as JSON to a configured URL (Splunk HEC, Grafana Loki, custom)
6
- * 2. JSONL file: appends events as newline-delimited JSON to a local file
7
- *
8
- * Both are async fire-and-forget — never blocks the request pipeline.
9
- * Zero runtime dependencies.
10
- */
11
- import type { SecurityEvent } from "./security-event.js";
12
- /** SIEM output configuration. */
13
- export interface SiemConfig {
14
- /** Webhook URL to POST events to. Null = disabled. */
15
- webhookUrl: string | null;
16
- /** Optional auth header value (e.g. "Bearer xxx" or "Splunk xxx"). */
17
- webhookAuth: string | null;
18
- /** JSONL file path to append events. Null = disabled. */
19
- jsonlPath: string | null;
20
- /** Batch size — buffer events and flush at this count. 1 = immediate. */
21
- batchSize: number;
22
- /** Max flush interval in ms. Events flushed even if batch not full. */
23
- flushIntervalMs: number;
24
- }
25
- /**
26
- * SIEM event shipper. Subscribes to SecurityEventBus and ships events
27
- * to configured destinations.
28
- */
29
- export declare class SiemShipper {
30
- private _config;
31
- private _buffer;
32
- private _flushTimer;
33
- private _stats;
34
- constructor(config: SiemConfig);
35
- /** Called for each security event. Buffers and flushes. */
36
- onEvent(event: SecurityEvent): void;
37
- /** Flush buffered events to all configured destinations. */
38
- flush(): void;
39
- /** Stop the flush timer and flush remaining events. */
40
- stop(): void;
41
- /** Get shipping stats. */
42
- getStats(): {
43
- buffered: number;
44
- shipped: number;
45
- webhookErrors: number;
46
- fileErrors: number;
47
- };
48
- private _postWebhook;
49
- }