@weave_protocol/mund 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 (98) hide show
  1. package/dist/analyzers/code-analyzer.d.ts +24 -0
  2. package/dist/analyzers/code-analyzer.d.ts.map +1 -0
  3. package/dist/analyzers/code-analyzer.js +73 -0
  4. package/dist/analyzers/code-analyzer.js.map +1 -0
  5. package/dist/analyzers/exfiltration-detector.d.ts +40 -0
  6. package/dist/analyzers/exfiltration-detector.d.ts.map +1 -0
  7. package/dist/analyzers/exfiltration-detector.js +220 -0
  8. package/dist/analyzers/exfiltration-detector.js.map +1 -0
  9. package/dist/analyzers/index.d.ts +26 -0
  10. package/dist/analyzers/index.d.ts.map +1 -0
  11. package/dist/analyzers/index.js +61 -0
  12. package/dist/analyzers/index.js.map +1 -0
  13. package/dist/analyzers/injection-detector.d.ts +36 -0
  14. package/dist/analyzers/injection-detector.d.ts.map +1 -0
  15. package/dist/analyzers/injection-detector.js +165 -0
  16. package/dist/analyzers/injection-detector.js.map +1 -0
  17. package/dist/analyzers/pii-detector.d.ts +44 -0
  18. package/dist/analyzers/pii-detector.d.ts.map +1 -0
  19. package/dist/analyzers/pii-detector.js +190 -0
  20. package/dist/analyzers/pii-detector.js.map +1 -0
  21. package/dist/analyzers/secret-scanner.d.ts +32 -0
  22. package/dist/analyzers/secret-scanner.d.ts.map +1 -0
  23. package/dist/analyzers/secret-scanner.js +119 -0
  24. package/dist/analyzers/secret-scanner.js.map +1 -0
  25. package/dist/constants.d.ts +18 -0
  26. package/dist/constants.d.ts.map +1 -0
  27. package/dist/constants.js +580 -0
  28. package/dist/constants.js.map +1 -0
  29. package/dist/index.d.ts +9 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +242 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/notifications/email.d.ts +18 -0
  34. package/dist/notifications/email.d.ts.map +1 -0
  35. package/dist/notifications/email.js +192 -0
  36. package/dist/notifications/email.js.map +1 -0
  37. package/dist/notifications/index.d.ts +51 -0
  38. package/dist/notifications/index.d.ts.map +1 -0
  39. package/dist/notifications/index.js +107 -0
  40. package/dist/notifications/index.js.map +1 -0
  41. package/dist/notifications/slack.d.ts +17 -0
  42. package/dist/notifications/slack.d.ts.map +1 -0
  43. package/dist/notifications/slack.js +128 -0
  44. package/dist/notifications/slack.js.map +1 -0
  45. package/dist/notifications/teams.d.ts +17 -0
  46. package/dist/notifications/teams.d.ts.map +1 -0
  47. package/dist/notifications/teams.js +136 -0
  48. package/dist/notifications/teams.js.map +1 -0
  49. package/dist/notifications/webhook.d.ts +15 -0
  50. package/dist/notifications/webhook.d.ts.map +1 -0
  51. package/dist/notifications/webhook.js +67 -0
  52. package/dist/notifications/webhook.js.map +1 -0
  53. package/dist/storage/index.d.ts +11 -0
  54. package/dist/storage/index.d.ts.map +1 -0
  55. package/dist/storage/index.js +26 -0
  56. package/dist/storage/index.js.map +1 -0
  57. package/dist/storage/memory.d.ts +35 -0
  58. package/dist/storage/memory.d.ts.map +1 -0
  59. package/dist/storage/memory.js +144 -0
  60. package/dist/storage/memory.js.map +1 -0
  61. package/dist/tools/configuration.d.ts +8 -0
  62. package/dist/tools/configuration.d.ts.map +1 -0
  63. package/dist/tools/configuration.js +399 -0
  64. package/dist/tools/configuration.js.map +1 -0
  65. package/dist/tools/index.d.ts +15 -0
  66. package/dist/tools/index.d.ts.map +1 -0
  67. package/dist/tools/index.js +16 -0
  68. package/dist/tools/index.js.map +1 -0
  69. package/dist/tools/monitoring.d.ts +10 -0
  70. package/dist/tools/monitoring.d.ts.map +1 -0
  71. package/dist/tools/monitoring.js +476 -0
  72. package/dist/tools/monitoring.js.map +1 -0
  73. package/dist/types.d.ts +207 -0
  74. package/dist/types.d.ts.map +1 -0
  75. package/dist/types.js +6 -0
  76. package/dist/types.js.map +1 -0
  77. package/package.json +62 -0
  78. package/rules/default.yaml +189 -0
  79. package/src/analyzers/code-analyzer.ts +88 -0
  80. package/src/analyzers/exfiltration-detector.ts +257 -0
  81. package/src/analyzers/index.ts +74 -0
  82. package/src/analyzers/injection-detector.ts +191 -0
  83. package/src/analyzers/pii-detector.ts +220 -0
  84. package/src/analyzers/secret-scanner.ts +142 -0
  85. package/src/constants.ts +603 -0
  86. package/src/index.ts +285 -0
  87. package/src/notifications/email.ts +211 -0
  88. package/src/notifications/index.ts +136 -0
  89. package/src/notifications/slack.ts +170 -0
  90. package/src/notifications/teams.ts +200 -0
  91. package/src/notifications/webhook.ts +80 -0
  92. package/src/storage/index.ts +29 -0
  93. package/src/storage/memory.ts +181 -0
  94. package/src/tools/configuration.ts +478 -0
  95. package/src/tools/index.ts +29 -0
  96. package/src/tools/monitoring.ts +559 -0
  97. package/src/types.ts +300 -0
  98. package/tsconfig.json +24 -0
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Mund - The Guardian Protocol
3
+ * Slack Notifier - Send alerts to Slack channels
4
+ */
5
+
6
+ import type { INotifier, NotificationPayload, SlackConfig, Severity } from '../types.js';
7
+
8
+ interface SlackMessage {
9
+ text: string;
10
+ attachments?: SlackAttachment[];
11
+ username?: string;
12
+ icon_emoji?: string;
13
+ channel?: string;
14
+ }
15
+
16
+ interface SlackAttachment {
17
+ color: string;
18
+ title: string;
19
+ text: string;
20
+ fields?: SlackField[];
21
+ footer?: string;
22
+ ts?: number;
23
+ }
24
+
25
+ interface SlackField {
26
+ title: string;
27
+ value: string;
28
+ short: boolean;
29
+ }
30
+
31
+ export class SlackNotifier implements INotifier {
32
+ name = 'SlackNotifier';
33
+ private config: SlackConfig;
34
+
35
+ constructor(config: SlackConfig) {
36
+ this.config = config;
37
+ }
38
+
39
+ async send(payload: NotificationPayload): Promise<boolean> {
40
+ const { event } = payload;
41
+
42
+ // Check minimum severity
43
+ if (this.config.min_severity && !this.meetsMinSeverity(event.severity, this.config.min_severity)) {
44
+ return true; // Successfully skipped
45
+ }
46
+
47
+ const message = this.formatMessage(payload);
48
+
49
+ try {
50
+ const response = await fetch(this.config.webhook_url, {
51
+ method: 'POST',
52
+ headers: { 'Content-Type': 'application/json' },
53
+ body: JSON.stringify(message)
54
+ });
55
+
56
+ if (!response.ok) {
57
+ console.error(`Slack notification failed: ${response.status} ${response.statusText}`);
58
+ return false;
59
+ }
60
+
61
+ return true;
62
+ } catch (error) {
63
+ console.error('Failed to send Slack notification:', error);
64
+ return false;
65
+ }
66
+ }
67
+
68
+ private formatMessage(payload: NotificationPayload): SlackMessage {
69
+ const { event } = payload;
70
+
71
+ const color = this.getSeverityColor(event.severity);
72
+ const emoji = this.getSeverityEmoji(event.severity);
73
+
74
+ const message: SlackMessage = {
75
+ text: `${emoji} *Mund Security Alert* - ${event.rule_name}`,
76
+ attachments: [
77
+ {
78
+ color,
79
+ title: `${event.severity.toUpperCase()}: ${event.rule_name}`,
80
+ text: `\`\`\`${event.content_snippet}\`\`\``,
81
+ fields: [
82
+ {
83
+ title: 'Type',
84
+ value: event.type,
85
+ short: true
86
+ },
87
+ {
88
+ title: 'Action',
89
+ value: event.action_taken,
90
+ short: true
91
+ },
92
+ {
93
+ title: 'Rule ID',
94
+ value: event.rule_id,
95
+ short: true
96
+ },
97
+ {
98
+ title: 'Event ID',
99
+ value: event.id,
100
+ short: true
101
+ }
102
+ ],
103
+ footer: 'Mund Guardian Protocol',
104
+ ts: Math.floor(event.timestamp.getTime() / 1000)
105
+ }
106
+ ]
107
+ };
108
+
109
+ // Add context fields if available
110
+ if (event.context.tool_name) {
111
+ message.attachments![0].fields!.push({
112
+ title: 'Tool',
113
+ value: event.context.tool_name,
114
+ short: true
115
+ });
116
+ }
117
+
118
+ if (event.context.agent_id) {
119
+ message.attachments![0].fields!.push({
120
+ title: 'Agent',
121
+ value: event.context.agent_id,
122
+ short: true
123
+ });
124
+ }
125
+
126
+ // Apply custom settings
127
+ if (this.config.username) {
128
+ message.username = this.config.username;
129
+ }
130
+ if (this.config.icon_emoji) {
131
+ message.icon_emoji = this.config.icon_emoji;
132
+ }
133
+ if (this.config.channel) {
134
+ message.channel = this.config.channel;
135
+ }
136
+
137
+ return message;
138
+ }
139
+
140
+ private getSeverityColor(severity: Severity): string {
141
+ const colors: Record<Severity, string> = {
142
+ critical: '#dc3545', // Red
143
+ high: '#fd7e14', // Orange
144
+ medium: '#ffc107', // Yellow
145
+ low: '#17a2b8', // Cyan
146
+ info: '#6c757d' // Gray
147
+ };
148
+ return colors[severity] || colors.info;
149
+ }
150
+
151
+ private getSeverityEmoji(severity: Severity): string {
152
+ const emojis: Record<Severity, string> = {
153
+ critical: '🚨',
154
+ high: '⚠️',
155
+ medium: '⚡',
156
+ low: 'ℹ️',
157
+ info: '📝'
158
+ };
159
+ return emojis[severity] || '📝';
160
+ }
161
+
162
+ private meetsMinSeverity(eventSeverity: Severity, minSeverity: Severity): boolean {
163
+ const order: Severity[] = ['critical', 'high', 'medium', 'low', 'info'];
164
+ const eventIndex = order.indexOf(eventSeverity);
165
+ const minIndex = order.indexOf(minSeverity);
166
+ return eventIndex <= minIndex;
167
+ }
168
+ }
169
+
170
+ export default SlackNotifier;
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Mund - The Guardian Protocol
3
+ * Teams Notifier - Send alerts to Microsoft Teams channels
4
+ */
5
+
6
+ import type { INotifier, NotificationPayload, TeamsConfig, Severity } from '../types.js';
7
+
8
+ interface TeamsAdaptiveCard {
9
+ type: string;
10
+ attachments: TeamsAttachment[];
11
+ }
12
+
13
+ interface TeamsAttachment {
14
+ contentType: string;
15
+ content: AdaptiveCardContent;
16
+ }
17
+
18
+ interface AdaptiveCardContent {
19
+ $schema: string;
20
+ type: string;
21
+ version: string;
22
+ body: AdaptiveCardElement[];
23
+ actions?: AdaptiveCardAction[];
24
+ }
25
+
26
+ interface AdaptiveCardElement {
27
+ type: string;
28
+ text?: string;
29
+ size?: string;
30
+ weight?: string;
31
+ color?: string;
32
+ wrap?: boolean;
33
+ spacing?: string;
34
+ items?: AdaptiveCardElement[];
35
+ columns?: AdaptiveCardColumn[];
36
+ facts?: AdaptiveCardFact[];
37
+ }
38
+
39
+ interface AdaptiveCardColumn {
40
+ type: string;
41
+ width: string;
42
+ items: AdaptiveCardElement[];
43
+ }
44
+
45
+ interface AdaptiveCardFact {
46
+ title: string;
47
+ value: string;
48
+ }
49
+
50
+ interface AdaptiveCardAction {
51
+ type: string;
52
+ title: string;
53
+ url?: string;
54
+ }
55
+
56
+ export class TeamsNotifier implements INotifier {
57
+ name = 'TeamsNotifier';
58
+ private config: TeamsConfig;
59
+
60
+ constructor(config: TeamsConfig) {
61
+ this.config = config;
62
+ }
63
+
64
+ async send(payload: NotificationPayload): Promise<boolean> {
65
+ const { event } = payload;
66
+
67
+ // Check minimum severity
68
+ if (this.config.min_severity && !this.meetsMinSeverity(event.severity, this.config.min_severity)) {
69
+ return true; // Successfully skipped
70
+ }
71
+
72
+ const card = this.formatAdaptiveCard(payload);
73
+
74
+ try {
75
+ const response = await fetch(this.config.webhook_url, {
76
+ method: 'POST',
77
+ headers: { 'Content-Type': 'application/json' },
78
+ body: JSON.stringify(card)
79
+ });
80
+
81
+ if (!response.ok) {
82
+ console.error(`Teams notification failed: ${response.status} ${response.statusText}`);
83
+ return false;
84
+ }
85
+
86
+ return true;
87
+ } catch (error) {
88
+ console.error('Failed to send Teams notification:', error);
89
+ return false;
90
+ }
91
+ }
92
+
93
+ private formatAdaptiveCard(payload: NotificationPayload): TeamsAdaptiveCard {
94
+ const { event } = payload;
95
+ const color = this.getSeverityColor(event.severity);
96
+ const emoji = this.getSeverityEmoji(event.severity);
97
+
98
+ const facts: AdaptiveCardFact[] = [
99
+ { title: 'Severity', value: event.severity.toUpperCase() },
100
+ { title: 'Type', value: event.type },
101
+ { title: 'Action', value: event.action_taken },
102
+ { title: 'Rule ID', value: event.rule_id },
103
+ { title: 'Event ID', value: event.id },
104
+ { title: 'Time', value: event.timestamp.toISOString() }
105
+ ];
106
+
107
+ if (event.context.tool_name) {
108
+ facts.push({ title: 'Tool', value: event.context.tool_name });
109
+ }
110
+ if (event.context.agent_id) {
111
+ facts.push({ title: 'Agent', value: event.context.agent_id });
112
+ }
113
+
114
+ return {
115
+ type: 'message',
116
+ attachments: [
117
+ {
118
+ contentType: 'application/vnd.microsoft.card.adaptive',
119
+ content: {
120
+ $schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
121
+ type: 'AdaptiveCard',
122
+ version: '1.4',
123
+ body: [
124
+ {
125
+ type: 'Container',
126
+ items: [
127
+ {
128
+ type: 'TextBlock',
129
+ text: `${emoji} Mund Security Alert`,
130
+ size: 'Large',
131
+ weight: 'Bolder',
132
+ color: color
133
+ },
134
+ {
135
+ type: 'TextBlock',
136
+ text: event.rule_name,
137
+ size: 'Medium',
138
+ weight: 'Bolder',
139
+ wrap: true
140
+ }
141
+ ]
142
+ },
143
+ {
144
+ type: 'Container',
145
+ items: [
146
+ {
147
+ type: 'TextBlock',
148
+ text: 'Detected Content:',
149
+ weight: 'Bolder',
150
+ spacing: 'Medium'
151
+ },
152
+ {
153
+ type: 'TextBlock',
154
+ text: `\`${event.content_snippet}\``,
155
+ wrap: true
156
+ }
157
+ ]
158
+ },
159
+ {
160
+ type: 'FactSet',
161
+ facts
162
+ }
163
+ ]
164
+ }
165
+ }
166
+ ]
167
+ };
168
+ }
169
+
170
+ private getSeverityColor(severity: Severity): 'attention' | 'warning' | 'good' | 'default' {
171
+ const colors: Record<Severity, 'attention' | 'warning' | 'good' | 'default'> = {
172
+ critical: 'attention',
173
+ high: 'attention',
174
+ medium: 'warning',
175
+ low: 'good',
176
+ info: 'default'
177
+ };
178
+ return colors[severity] || 'default';
179
+ }
180
+
181
+ private getSeverityEmoji(severity: Severity): string {
182
+ const emojis: Record<Severity, string> = {
183
+ critical: '🚨',
184
+ high: '⚠️',
185
+ medium: '⚡',
186
+ low: 'ℹ️',
187
+ info: '📝'
188
+ };
189
+ return emojis[severity] || '📝';
190
+ }
191
+
192
+ private meetsMinSeverity(eventSeverity: Severity, minSeverity: Severity): boolean {
193
+ const order: Severity[] = ['critical', 'high', 'medium', 'low', 'info'];
194
+ const eventIndex = order.indexOf(eventSeverity);
195
+ const minIndex = order.indexOf(minSeverity);
196
+ return eventIndex <= minIndex;
197
+ }
198
+ }
199
+
200
+ export default TeamsNotifier;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Mund - The Guardian Protocol
3
+ * Webhook Notifier - Send alerts to generic webhooks
4
+ */
5
+
6
+ import type { INotifier, NotificationPayload, WebhookConfig, Severity } from '../types.js';
7
+
8
+ export class WebhookNotifier implements INotifier {
9
+ name = 'WebhookNotifier';
10
+ private config: WebhookConfig;
11
+
12
+ constructor(config: WebhookConfig) {
13
+ this.config = config;
14
+ }
15
+
16
+ async send(payload: NotificationPayload): Promise<boolean> {
17
+ const { event } = payload;
18
+
19
+ // Check minimum severity
20
+ if (this.config.min_severity && !this.meetsMinSeverity(event.severity, this.config.min_severity)) {
21
+ return true; // Successfully skipped
22
+ }
23
+
24
+ const webhookPayload = this.formatPayload(payload);
25
+
26
+ try {
27
+ const headers: Record<string, string> = {
28
+ 'Content-Type': 'application/json',
29
+ ...this.config.headers
30
+ };
31
+
32
+ const response = await fetch(this.config.url, {
33
+ method: this.config.method,
34
+ headers,
35
+ body: JSON.stringify(webhookPayload)
36
+ });
37
+
38
+ if (!response.ok) {
39
+ console.error(`Webhook notification failed: ${response.status} ${response.statusText}`);
40
+ return false;
41
+ }
42
+
43
+ return true;
44
+ } catch (error) {
45
+ console.error('Failed to send webhook notification:', error);
46
+ return false;
47
+ }
48
+ }
49
+
50
+ private formatPayload(payload: NotificationPayload): object {
51
+ const { event } = payload;
52
+
53
+ return {
54
+ source: 'mund',
55
+ version: '1.0',
56
+ event: {
57
+ id: event.id,
58
+ timestamp: event.timestamp.toISOString(),
59
+ rule_id: event.rule_id,
60
+ rule_name: event.rule_name,
61
+ severity: event.severity,
62
+ type: event.type,
63
+ action_taken: event.action_taken,
64
+ content_snippet: event.content_snippet,
65
+ content_hash: event.full_content_hash,
66
+ context: event.context,
67
+ acknowledged: event.acknowledged
68
+ }
69
+ };
70
+ }
71
+
72
+ private meetsMinSeverity(eventSeverity: Severity, minSeverity: Severity): boolean {
73
+ const order: Severity[] = ['critical', 'high', 'medium', 'low', 'info'];
74
+ const eventIndex = order.indexOf(eventSeverity);
75
+ const minIndex = order.indexOf(minSeverity);
76
+ return eventIndex <= minIndex;
77
+ }
78
+ }
79
+
80
+ export default WebhookNotifier;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Mund - The Guardian Protocol
3
+ * Storage Index - Exports storage implementations
4
+ */
5
+
6
+ export { MemoryStorage } from './memory.js';
7
+
8
+ import { MemoryStorage } from './memory.js';
9
+ import type { IStorage, MundConfig } from '../types.js';
10
+
11
+ /**
12
+ * Create storage instance based on configuration
13
+ */
14
+ export function createStorage(config: MundConfig): IStorage {
15
+ switch (config.storage_type) {
16
+ case 'memory':
17
+ return new MemoryStorage();
18
+ case 'sqlite':
19
+ // SQLite implementation would go here
20
+ console.warn('SQLite storage not yet implemented, falling back to memory');
21
+ return new MemoryStorage();
22
+ case 'postgres':
23
+ // PostgreSQL implementation would go here
24
+ console.warn('PostgreSQL storage not yet implemented, falling back to memory');
25
+ return new MemoryStorage();
26
+ default:
27
+ return new MemoryStorage();
28
+ }
29
+ }
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Mund - The Guardian Protocol
3
+ * Memory Storage - In-memory storage for development and testing
4
+ */
5
+
6
+ import type {
7
+ IStorage,
8
+ SecurityEvent,
9
+ DetectionRule,
10
+ GetEventsInput,
11
+ DetectorType
12
+ } from '../types.js';
13
+
14
+ export class MemoryStorage implements IStorage {
15
+ private events: Map<string, SecurityEvent> = new Map();
16
+ private rules: Map<string, DetectionRule> = new Map();
17
+ private allowlist: Map<string, { pattern: string; type: DetectorType }> = new Map();
18
+ private blocklist: Map<string, { pattern: string; type: DetectorType }> = new Map();
19
+
20
+ // ============================================================================
21
+ // Events
22
+ // ============================================================================
23
+
24
+ async saveEvent(event: SecurityEvent): Promise<void> {
25
+ this.events.set(event.id, event);
26
+
27
+ // Keep only last 10000 events in memory
28
+ if (this.events.size > 10000) {
29
+ const oldestKey = this.events.keys().next().value;
30
+ if (oldestKey) {
31
+ this.events.delete(oldestKey);
32
+ }
33
+ }
34
+ }
35
+
36
+ async getEvent(id: string): Promise<SecurityEvent | null> {
37
+ return this.events.get(id) || null;
38
+ }
39
+
40
+ async getEvents(query: GetEventsInput): Promise<SecurityEvent[]> {
41
+ let events = Array.from(this.events.values());
42
+
43
+ // Apply filters
44
+ if (query.severity) {
45
+ events = events.filter(e => e.severity === query.severity);
46
+ }
47
+ if (query.type) {
48
+ events = events.filter(e => e.type === query.type);
49
+ }
50
+ if (query.acknowledged !== undefined) {
51
+ events = events.filter(e => e.acknowledged === query.acknowledged);
52
+ }
53
+ if (query.start_time) {
54
+ events = events.filter(e => e.timestamp >= query.start_time!);
55
+ }
56
+ if (query.end_time) {
57
+ events = events.filter(e => e.timestamp <= query.end_time!);
58
+ }
59
+
60
+ // Sort by timestamp descending (newest first)
61
+ events.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
62
+
63
+ // Apply pagination
64
+ const offset = query.offset || 0;
65
+ const limit = query.limit || 50;
66
+ return events.slice(offset, offset + limit);
67
+ }
68
+
69
+ async countEvents(query: GetEventsInput): Promise<number> {
70
+ let events = Array.from(this.events.values());
71
+
72
+ if (query.severity) {
73
+ events = events.filter(e => e.severity === query.severity);
74
+ }
75
+ if (query.type) {
76
+ events = events.filter(e => e.type === query.type);
77
+ }
78
+ if (query.acknowledged !== undefined) {
79
+ events = events.filter(e => e.acknowledged === query.acknowledged);
80
+ }
81
+ if (query.start_time) {
82
+ events = events.filter(e => e.timestamp >= query.start_time!);
83
+ }
84
+ if (query.end_time) {
85
+ events = events.filter(e => e.timestamp <= query.end_time!);
86
+ }
87
+
88
+ return events.length;
89
+ }
90
+
91
+ async acknowledgeEvent(id: string, by?: string): Promise<void> {
92
+ const event = this.events.get(id);
93
+ if (event) {
94
+ event.acknowledged = true;
95
+ event.acknowledged_by = by;
96
+ event.acknowledged_at = new Date();
97
+ }
98
+ }
99
+
100
+ // ============================================================================
101
+ // Rules
102
+ // ============================================================================
103
+
104
+ async saveRule(rule: DetectionRule): Promise<void> {
105
+ this.rules.set(rule.id, rule);
106
+ }
107
+
108
+ async getRule(id: string): Promise<DetectionRule | null> {
109
+ return this.rules.get(id) || null;
110
+ }
111
+
112
+ async getRules(): Promise<DetectionRule[]> {
113
+ return Array.from(this.rules.values());
114
+ }
115
+
116
+ async deleteRule(id: string): Promise<void> {
117
+ this.rules.delete(id);
118
+ }
119
+
120
+ // ============================================================================
121
+ // Allowlist/Blocklist
122
+ // ============================================================================
123
+
124
+ async addToAllowlist(pattern: string, type: DetectorType): Promise<void> {
125
+ this.allowlist.set(pattern, { pattern, type });
126
+ }
127
+
128
+ async addToBlocklist(pattern: string, type: DetectorType): Promise<void> {
129
+ this.blocklist.set(pattern, { pattern, type });
130
+ }
131
+
132
+ async getAllowlist(type?: DetectorType): Promise<string[]> {
133
+ const entries = Array.from(this.allowlist.values());
134
+ if (type) {
135
+ return entries.filter(e => e.type === type).map(e => e.pattern);
136
+ }
137
+ return entries.map(e => e.pattern);
138
+ }
139
+
140
+ async getBlocklist(type?: DetectorType): Promise<string[]> {
141
+ const entries = Array.from(this.blocklist.values());
142
+ if (type) {
143
+ return entries.filter(e => e.type === type).map(e => e.pattern);
144
+ }
145
+ return entries.map(e => e.pattern);
146
+ }
147
+
148
+ async removeFromAllowlist(pattern: string): Promise<void> {
149
+ this.allowlist.delete(pattern);
150
+ }
151
+
152
+ async removeFromBlocklist(pattern: string): Promise<void> {
153
+ this.blocklist.delete(pattern);
154
+ }
155
+
156
+ // ============================================================================
157
+ // Cleanup
158
+ // ============================================================================
159
+
160
+ async close(): Promise<void> {
161
+ this.events.clear();
162
+ this.rules.clear();
163
+ this.allowlist.clear();
164
+ this.blocklist.clear();
165
+ }
166
+
167
+ // ============================================================================
168
+ // Utility Methods
169
+ // ============================================================================
170
+
171
+ getStats(): { events: number; rules: number; allowlist: number; blocklist: number } {
172
+ return {
173
+ events: this.events.size,
174
+ rules: this.rules.size,
175
+ allowlist: this.allowlist.size,
176
+ blocklist: this.blocklist.size
177
+ };
178
+ }
179
+ }
180
+
181
+ export default MemoryStorage;