@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.
- package/dist/analyzers/code-analyzer.d.ts +24 -0
- package/dist/analyzers/code-analyzer.d.ts.map +1 -0
- package/dist/analyzers/code-analyzer.js +73 -0
- package/dist/analyzers/code-analyzer.js.map +1 -0
- package/dist/analyzers/exfiltration-detector.d.ts +40 -0
- package/dist/analyzers/exfiltration-detector.d.ts.map +1 -0
- package/dist/analyzers/exfiltration-detector.js +220 -0
- package/dist/analyzers/exfiltration-detector.js.map +1 -0
- package/dist/analyzers/index.d.ts +26 -0
- package/dist/analyzers/index.d.ts.map +1 -0
- package/dist/analyzers/index.js +61 -0
- package/dist/analyzers/index.js.map +1 -0
- package/dist/analyzers/injection-detector.d.ts +36 -0
- package/dist/analyzers/injection-detector.d.ts.map +1 -0
- package/dist/analyzers/injection-detector.js +165 -0
- package/dist/analyzers/injection-detector.js.map +1 -0
- package/dist/analyzers/pii-detector.d.ts +44 -0
- package/dist/analyzers/pii-detector.d.ts.map +1 -0
- package/dist/analyzers/pii-detector.js +190 -0
- package/dist/analyzers/pii-detector.js.map +1 -0
- package/dist/analyzers/secret-scanner.d.ts +32 -0
- package/dist/analyzers/secret-scanner.d.ts.map +1 -0
- package/dist/analyzers/secret-scanner.js +119 -0
- package/dist/analyzers/secret-scanner.js.map +1 -0
- package/dist/constants.d.ts +18 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +580 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +242 -0
- package/dist/index.js.map +1 -0
- package/dist/notifications/email.d.ts +18 -0
- package/dist/notifications/email.d.ts.map +1 -0
- package/dist/notifications/email.js +192 -0
- package/dist/notifications/email.js.map +1 -0
- package/dist/notifications/index.d.ts +51 -0
- package/dist/notifications/index.d.ts.map +1 -0
- package/dist/notifications/index.js +107 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/slack.d.ts +17 -0
- package/dist/notifications/slack.d.ts.map +1 -0
- package/dist/notifications/slack.js +128 -0
- package/dist/notifications/slack.js.map +1 -0
- package/dist/notifications/teams.d.ts +17 -0
- package/dist/notifications/teams.d.ts.map +1 -0
- package/dist/notifications/teams.js +136 -0
- package/dist/notifications/teams.js.map +1 -0
- package/dist/notifications/webhook.d.ts +15 -0
- package/dist/notifications/webhook.d.ts.map +1 -0
- package/dist/notifications/webhook.js +67 -0
- package/dist/notifications/webhook.js.map +1 -0
- package/dist/storage/index.d.ts +11 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +26 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/memory.d.ts +35 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +144 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/tools/configuration.d.ts +8 -0
- package/dist/tools/configuration.d.ts.map +1 -0
- package/dist/tools/configuration.js +399 -0
- package/dist/tools/configuration.js.map +1 -0
- package/dist/tools/index.d.ts +15 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +16 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/monitoring.d.ts +10 -0
- package/dist/tools/monitoring.d.ts.map +1 -0
- package/dist/tools/monitoring.js +476 -0
- package/dist/tools/monitoring.js.map +1 -0
- package/dist/types.d.ts +207 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +62 -0
- package/rules/default.yaml +189 -0
- package/src/analyzers/code-analyzer.ts +88 -0
- package/src/analyzers/exfiltration-detector.ts +257 -0
- package/src/analyzers/index.ts +74 -0
- package/src/analyzers/injection-detector.ts +191 -0
- package/src/analyzers/pii-detector.ts +220 -0
- package/src/analyzers/secret-scanner.ts +142 -0
- package/src/constants.ts +603 -0
- package/src/index.ts +285 -0
- package/src/notifications/email.ts +211 -0
- package/src/notifications/index.ts +136 -0
- package/src/notifications/slack.ts +170 -0
- package/src/notifications/teams.ts +200 -0
- package/src/notifications/webhook.ts +80 -0
- package/src/storage/index.ts +29 -0
- package/src/storage/memory.ts +181 -0
- package/src/tools/configuration.ts +478 -0
- package/src/tools/index.ts +29 -0
- package/src/tools/monitoring.ts +559 -0
- package/src/types.ts +300 -0
- 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;
|