@panguard-ai/panguard-guard 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/agent/analyze-agent.d.ts +62 -0
- package/dist/agent/analyze-agent.d.ts.map +1 -0
- package/dist/agent/analyze-agent.js +327 -0
- package/dist/agent/analyze-agent.js.map +1 -0
- package/dist/agent/detect-agent.d.ts +59 -0
- package/dist/agent/detect-agent.d.ts.map +1 -0
- package/dist/agent/detect-agent.js +214 -0
- package/dist/agent/detect-agent.js.map +1 -0
- package/dist/agent/index.d.ts +15 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +14 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/report-agent.d.ts +122 -0
- package/dist/agent/report-agent.d.ts.map +1 -0
- package/dist/agent/report-agent.js +468 -0
- package/dist/agent/report-agent.js.map +1 -0
- package/dist/agent/respond-agent.d.ts +113 -0
- package/dist/agent/respond-agent.d.ts.map +1 -0
- package/dist/agent/respond-agent.js +749 -0
- package/dist/agent/respond-agent.js.map +1 -0
- package/dist/agent-client/index.d.ts +81 -0
- package/dist/agent-client/index.d.ts.map +1 -0
- package/dist/agent-client/index.js +170 -0
- package/dist/agent-client/index.js.map +1 -0
- package/dist/cli/index.d.ts +17 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +295 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config.d.ts +23 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +108 -0
- package/dist/config.js.map +1 -0
- package/dist/daemon/index.d.ts +66 -0
- package/dist/daemon/index.d.ts.map +1 -0
- package/dist/daemon/index.js +284 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/dashboard/index.d.ts +78 -0
- package/dist/dashboard/index.d.ts.map +1 -0
- package/dist/dashboard/index.js +455 -0
- package/dist/dashboard/index.js.map +1 -0
- package/dist/guard-engine.d.ts +108 -0
- package/dist/guard-engine.d.ts.map +1 -0
- package/dist/guard-engine.js +740 -0
- package/dist/guard-engine.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/install/index.d.ts +23 -0
- package/dist/install/index.d.ts.map +1 -0
- package/dist/install/index.js +216 -0
- package/dist/install/index.js.map +1 -0
- package/dist/investigation/index.d.ts +80 -0
- package/dist/investigation/index.d.ts.map +1 -0
- package/dist/investigation/index.js +570 -0
- package/dist/investigation/index.js.map +1 -0
- package/dist/license/index.d.ts +46 -0
- package/dist/license/index.d.ts.map +1 -0
- package/dist/license/index.js +145 -0
- package/dist/license/index.js.map +1 -0
- package/dist/memory/baseline.d.ts +34 -0
- package/dist/memory/baseline.d.ts.map +1 -0
- package/dist/memory/baseline.js +224 -0
- package/dist/memory/baseline.js.map +1 -0
- package/dist/memory/index.d.ts +32 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +58 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/learning.d.ts +35 -0
- package/dist/memory/learning.d.ts.map +1 -0
- package/dist/memory/learning.js +60 -0
- package/dist/memory/learning.js.map +1 -0
- package/dist/monitors/falco-monitor.d.ts +62 -0
- package/dist/monitors/falco-monitor.d.ts.map +1 -0
- package/dist/monitors/falco-monitor.js +226 -0
- package/dist/monitors/falco-monitor.js.map +1 -0
- package/dist/monitors/suricata-monitor.d.ts +80 -0
- package/dist/monitors/suricata-monitor.d.ts.map +1 -0
- package/dist/monitors/suricata-monitor.js +227 -0
- package/dist/monitors/suricata-monitor.js.map +1 -0
- package/dist/notify/email.d.ts +23 -0
- package/dist/notify/email.d.ts.map +1 -0
- package/dist/notify/email.js +124 -0
- package/dist/notify/email.js.map +1 -0
- package/dist/notify/index.d.ts +31 -0
- package/dist/notify/index.d.ts.map +1 -0
- package/dist/notify/index.js +70 -0
- package/dist/notify/index.js.map +1 -0
- package/dist/notify/line-notify.d.ts.map +1 -0
- package/dist/notify/slack.d.ts +21 -0
- package/dist/notify/slack.d.ts.map +1 -0
- package/dist/notify/slack.js +92 -0
- package/dist/notify/slack.js.map +1 -0
- package/dist/notify/telegram.d.ts +21 -0
- package/dist/notify/telegram.d.ts.map +1 -0
- package/dist/notify/telegram.js +89 -0
- package/dist/notify/telegram.js.map +1 -0
- package/dist/response/file-quarantine.d.ts +63 -0
- package/dist/response/file-quarantine.d.ts.map +1 -0
- package/dist/response/file-quarantine.js +137 -0
- package/dist/response/file-quarantine.js.map +1 -0
- package/dist/response/index.d.ts +4 -0
- package/dist/response/index.d.ts.map +1 -0
- package/dist/response/index.js +4 -0
- package/dist/response/index.js.map +1 -0
- package/dist/response/ip-blocker.d.ts +69 -0
- package/dist/response/ip-blocker.d.ts.map +1 -0
- package/dist/response/ip-blocker.js +191 -0
- package/dist/response/ip-blocker.js.map +1 -0
- package/dist/response/process-killer.d.ts +49 -0
- package/dist/response/process-killer.d.ts.map +1 -0
- package/dist/response/process-killer.js +230 -0
- package/dist/response/process-killer.js.map +1 -0
- package/dist/rules/builtin-rules.d.ts +12 -0
- package/dist/rules/builtin-rules.d.ts.map +1 -0
- package/dist/rules/builtin-rules.js +471 -0
- package/dist/rules/builtin-rules.js.map +1 -0
- package/dist/threat-cloud/client-id.d.ts +13 -0
- package/dist/threat-cloud/client-id.d.ts.map +1 -0
- package/dist/threat-cloud/client-id.js +38 -0
- package/dist/threat-cloud/client-id.js.map +1 -0
- package/dist/threat-cloud/index.d.ts +103 -0
- package/dist/threat-cloud/index.d.ts.map +1 -0
- package/dist/threat-cloud/index.js +386 -0
- package/dist/threat-cloud/index.js.map +1 -0
- package/dist/types.d.ts +336 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +42 -0
- package/dist/types.js.map +1 -0
- package/package.json +35 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email SMTP notification integration
|
|
3
|
+
* Email SMTP 通知整合
|
|
4
|
+
*
|
|
5
|
+
* Sends threat alerts via raw SMTP without external dependencies.
|
|
6
|
+
* Uses Node.js net/tls modules for direct SMTP communication.
|
|
7
|
+
* 透過原始 SMTP 發送威脅警報,不使用外部相依套件。
|
|
8
|
+
* 使用 Node.js net/tls 模組進行直接 SMTP 通訊。
|
|
9
|
+
*
|
|
10
|
+
* @module @panguard-ai/panguard-guard/notify/email
|
|
11
|
+
*/
|
|
12
|
+
import * as net from 'node:net';
|
|
13
|
+
import * as tls from 'node:tls';
|
|
14
|
+
import { createLogger } from '@panguard-ai/core';
|
|
15
|
+
const logger = createLogger('panguard-guard:notify:email');
|
|
16
|
+
/**
|
|
17
|
+
* Send a threat alert via Email (raw SMTP)
|
|
18
|
+
* 透過 Email (原始 SMTP) 發送威脅警報
|
|
19
|
+
*
|
|
20
|
+
* @param config - Email SMTP configuration / Email SMTP 配置
|
|
21
|
+
* @param verdict - The threat verdict / 威脅判決
|
|
22
|
+
* @param eventDescription - Event description / 事件描述
|
|
23
|
+
* @returns Notification result / 通知結果
|
|
24
|
+
*/
|
|
25
|
+
export async function sendEmailNotify(config, verdict, eventDescription) {
|
|
26
|
+
const subject = `[PanguardGuard] ${verdict.conclusion.toUpperCase()} Alert - Confidence ${verdict.confidence}%`;
|
|
27
|
+
const body = `PanguardGuard Security Alert\n` +
|
|
28
|
+
`========================\n\n` +
|
|
29
|
+
`Conclusion: ${verdict.conclusion}\n` +
|
|
30
|
+
`Confidence: ${verdict.confidence}%\n` +
|
|
31
|
+
`Recommended Action: ${verdict.recommendedAction}\n` +
|
|
32
|
+
`${verdict.mitreTechnique ? `MITRE ATT&CK: ${verdict.mitreTechnique}\n` : ''}` +
|
|
33
|
+
`\nEvent Description:\n${eventDescription}\n` +
|
|
34
|
+
`\nEvidence:\n${verdict.evidence.map((e) => ` - [${e.source}] ${e.description} (${e.confidence}%)`).join('\n')}\n` +
|
|
35
|
+
`\nReasoning:\n${verdict.reasoning}\n` +
|
|
36
|
+
`\nTimestamp: ${new Date().toISOString()}\n`;
|
|
37
|
+
try {
|
|
38
|
+
for (const recipient of config.to) {
|
|
39
|
+
await sendSMTP(config, recipient, subject, body);
|
|
40
|
+
}
|
|
41
|
+
logger.info(`Email notification sent to ${config.to.length} recipients / Email 通知已發送`);
|
|
42
|
+
return { channel: 'email', success: true };
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
46
|
+
logger.error(`Email notification failed: ${msg} / Email 通知失敗: ${msg}`);
|
|
47
|
+
return { channel: 'email', success: false, error: msg };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Send email via raw SMTP protocol / 透過原始 SMTP 協定發送電子郵件
|
|
52
|
+
*/
|
|
53
|
+
function sendSMTP(config, to, subject, body) {
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
const commands = [
|
|
56
|
+
`EHLO panguard-guard`,
|
|
57
|
+
`AUTH LOGIN`,
|
|
58
|
+
Buffer.from(config.auth.user).toString('base64'),
|
|
59
|
+
Buffer.from(config.auth.pass).toString('base64'),
|
|
60
|
+
`MAIL FROM:<${config.from}>`,
|
|
61
|
+
`RCPT TO:<${to}>`,
|
|
62
|
+
`DATA`,
|
|
63
|
+
`From: ${config.from}\r\nTo: ${to}\r\nSubject: ${subject}\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n${body}\r\n.`,
|
|
64
|
+
`QUIT`,
|
|
65
|
+
];
|
|
66
|
+
let commandIndex = 0;
|
|
67
|
+
let resolved = false;
|
|
68
|
+
const onConnect = (socket) => {
|
|
69
|
+
let buffer = '';
|
|
70
|
+
socket.on('data', (data) => {
|
|
71
|
+
buffer += data.toString();
|
|
72
|
+
// Process complete SMTP responses (ending with \r\n)
|
|
73
|
+
// 處理完整的 SMTP 回應(以 \r\n 結尾)
|
|
74
|
+
while (buffer.includes('\r\n')) {
|
|
75
|
+
const lineEnd = buffer.indexOf('\r\n');
|
|
76
|
+
const line = buffer.slice(0, lineEnd);
|
|
77
|
+
buffer = buffer.slice(lineEnd + 2);
|
|
78
|
+
const code = parseInt(line.slice(0, 3), 10);
|
|
79
|
+
// Multi-line responses (code followed by dash) / 多行回應
|
|
80
|
+
if (line[3] === '-')
|
|
81
|
+
continue;
|
|
82
|
+
// Check for errors / 檢查錯誤
|
|
83
|
+
if (code >= 400) {
|
|
84
|
+
if (!resolved) {
|
|
85
|
+
resolved = true;
|
|
86
|
+
socket.destroy();
|
|
87
|
+
reject(new Error(`SMTP error ${code}: ${line}`));
|
|
88
|
+
}
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// Send next command / 發送下一個命令
|
|
92
|
+
if (commandIndex < commands.length) {
|
|
93
|
+
socket.write(commands[commandIndex] + '\r\n');
|
|
94
|
+
commandIndex++;
|
|
95
|
+
}
|
|
96
|
+
else if (!resolved) {
|
|
97
|
+
resolved = true;
|
|
98
|
+
socket.destroy();
|
|
99
|
+
resolve();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
socket.on('error', (err) => {
|
|
104
|
+
if (!resolved) {
|
|
105
|
+
resolved = true;
|
|
106
|
+
reject(err);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
socket.on('close', () => {
|
|
110
|
+
if (!resolved) {
|
|
111
|
+
resolved = true;
|
|
112
|
+
resolve();
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
if (config.secure) {
|
|
117
|
+
const socket = tls.connect({ host: config.host, port: config.port }, () => onConnect(socket));
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
const socket = net.connect({ host: config.host, port: config.port }, () => onConnect(socket));
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=email.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.js","sourceRoot":"","sources":["../../src/notify/email.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification System
|
|
3
|
+
* 通知系統
|
|
4
|
+
*
|
|
5
|
+
* Dispatches threat alerts to configured notification channels:
|
|
6
|
+
* Telegram Bot, Slack Webhook, Email SMTP.
|
|
7
|
+
* 將威脅警報派發至已配置的通知通道:
|
|
8
|
+
* Telegram Bot、Slack Webhook、Email SMTP。
|
|
9
|
+
*
|
|
10
|
+
* @module @panguard-ai/panguard-guard/notify
|
|
11
|
+
*/
|
|
12
|
+
import type { NotificationConfig, NotificationResult, ThreatVerdict } from '../types.js';
|
|
13
|
+
export { sendTelegramNotify } from './telegram.js';
|
|
14
|
+
export { sendSlackNotify } from './slack.js';
|
|
15
|
+
export { sendEmailNotify } from './email.js';
|
|
16
|
+
/**
|
|
17
|
+
* Send notifications to all configured channels
|
|
18
|
+
* 向所有已配置的通道發送通知
|
|
19
|
+
*
|
|
20
|
+
* Dispatches alerts in parallel to all configured channels and
|
|
21
|
+
* collects results. Never throws - all errors are captured in results.
|
|
22
|
+
* 平行向所有已配置的通道派發警報並收集結果。
|
|
23
|
+
* 永不拋出錯誤 - 所有錯誤都會被捕獲在結果中。
|
|
24
|
+
*
|
|
25
|
+
* @param config - Notification configuration / 通知配置
|
|
26
|
+
* @param verdict - The threat verdict to notify about / 要通知的威脅判決
|
|
27
|
+
* @param eventDescription - Human-readable event description / 人類可讀的事件描述
|
|
28
|
+
* @returns Array of notification results for each channel / 各通道的通知結果陣列
|
|
29
|
+
*/
|
|
30
|
+
export declare function sendNotifications(config: NotificationConfig, verdict: ThreatVerdict, eventDescription: string): Promise<NotificationResult[]>;
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/notify/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAOzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,kBAAkB,EAC1B,OAAO,EAAE,aAAa,EACtB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,kBAAkB,EAAE,CAAC,CA0C/B"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification System
|
|
3
|
+
* 通知系統
|
|
4
|
+
*
|
|
5
|
+
* Dispatches threat alerts to configured notification channels:
|
|
6
|
+
* Telegram Bot, Slack Webhook, Email SMTP.
|
|
7
|
+
* 將威脅警報派發至已配置的通知通道:
|
|
8
|
+
* Telegram Bot、Slack Webhook、Email SMTP。
|
|
9
|
+
*
|
|
10
|
+
* @module @panguard-ai/panguard-guard/notify
|
|
11
|
+
*/
|
|
12
|
+
import { createLogger } from '@panguard-ai/core';
|
|
13
|
+
import { sendTelegramNotify } from './telegram.js';
|
|
14
|
+
import { sendSlackNotify } from './slack.js';
|
|
15
|
+
import { sendEmailNotify } from './email.js';
|
|
16
|
+
const logger = createLogger('panguard-guard:notify');
|
|
17
|
+
export { sendTelegramNotify } from './telegram.js';
|
|
18
|
+
export { sendSlackNotify } from './slack.js';
|
|
19
|
+
export { sendEmailNotify } from './email.js';
|
|
20
|
+
/**
|
|
21
|
+
* Send notifications to all configured channels
|
|
22
|
+
* 向所有已配置的通道發送通知
|
|
23
|
+
*
|
|
24
|
+
* Dispatches alerts in parallel to all configured channels and
|
|
25
|
+
* collects results. Never throws - all errors are captured in results.
|
|
26
|
+
* 平行向所有已配置的通道派發警報並收集結果。
|
|
27
|
+
* 永不拋出錯誤 - 所有錯誤都會被捕獲在結果中。
|
|
28
|
+
*
|
|
29
|
+
* @param config - Notification configuration / 通知配置
|
|
30
|
+
* @param verdict - The threat verdict to notify about / 要通知的威脅判決
|
|
31
|
+
* @param eventDescription - Human-readable event description / 人類可讀的事件描述
|
|
32
|
+
* @returns Array of notification results for each channel / 各通道的通知結果陣列
|
|
33
|
+
*/
|
|
34
|
+
export async function sendNotifications(config, verdict, eventDescription) {
|
|
35
|
+
const promises = [];
|
|
36
|
+
if (config.telegram) {
|
|
37
|
+
promises.push(sendTelegramNotify(config.telegram, verdict, eventDescription));
|
|
38
|
+
}
|
|
39
|
+
if (config.slack) {
|
|
40
|
+
promises.push(sendSlackNotify(config.slack, verdict, eventDescription));
|
|
41
|
+
}
|
|
42
|
+
if (config.email) {
|
|
43
|
+
promises.push(sendEmailNotify(config.email, verdict, eventDescription));
|
|
44
|
+
}
|
|
45
|
+
if (promises.length === 0) {
|
|
46
|
+
logger.info('No notification channels configured / 未配置通知通道');
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
const results = await Promise.allSettled(promises);
|
|
50
|
+
const notificationResults = [];
|
|
51
|
+
for (const result of results) {
|
|
52
|
+
if (result.status === 'fulfilled') {
|
|
53
|
+
notificationResults.push(result.value);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
const msg = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
|
57
|
+
logger.error(`Notification failed: ${msg} / 通知失敗: ${msg}`);
|
|
58
|
+
notificationResults.push({
|
|
59
|
+
channel: 'telegram', // fallback channel name
|
|
60
|
+
success: false,
|
|
61
|
+
error: msg,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const successCount = notificationResults.filter((r) => r.success).length;
|
|
66
|
+
logger.info(`Notifications sent: ${successCount}/${notificationResults.length} successful / ` +
|
|
67
|
+
`通知發送: ${successCount}/${notificationResults.length} 成功`);
|
|
68
|
+
return notificationResults;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/notify/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,MAAM,GAAG,YAAY,CAAC,uBAAuB,CAAC,CAAC;AAErD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAA0B,EAC1B,OAAsB,EACtB,gBAAwB;IAExB,MAAM,QAAQ,GAAkC,EAAE,CAAC;IAEnD,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,QAAQ,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAChF,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAC1E,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAC1E,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;QAC7D,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACnD,MAAM,mBAAmB,GAAyB,EAAE,CAAC;IAErD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC3F,MAAM,CAAC,KAAK,CAAC,wBAAwB,GAAG,YAAY,GAAG,EAAE,CAAC,CAAC;YAC3D,mBAAmB,CAAC,IAAI,CAAC;gBACvB,OAAO,EAAE,UAAU,EAAE,wBAAwB;gBAC7C,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,GAAG;aACX,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,MAAM,YAAY,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IACzE,MAAM,CAAC,IAAI,CACT,uBAAuB,YAAY,IAAI,mBAAmB,CAAC,MAAM,gBAAgB;QAC/E,SAAS,YAAY,IAAI,mBAAmB,CAAC,MAAM,KAAK,CAC3D,CAAC;IAEF,OAAO,mBAAmB,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"line-notify.d.ts","sourceRoot":"","sources":["../../src/notify/line-notify.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAMvF;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,aAAa,EACtB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,kBAAkB,CAAC,CAmB7B"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack Webhook notification integration
|
|
3
|
+
* Slack Webhook 通知整合
|
|
4
|
+
*
|
|
5
|
+
* Sends threat alerts to Slack channels via Incoming Webhooks.
|
|
6
|
+
* 透過 Incoming Webhooks 將威脅警報發送至 Slack 頻道。
|
|
7
|
+
*
|
|
8
|
+
* @module @panguard-ai/panguard-guard/notify/slack
|
|
9
|
+
*/
|
|
10
|
+
import type { SlackConfig, NotificationResult, ThreatVerdict } from '../types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Send a threat alert via Slack Webhook
|
|
13
|
+
* 透過 Slack Webhook 發送威脅警報
|
|
14
|
+
*
|
|
15
|
+
* @param config - Slack webhook configuration / Slack webhook 配置
|
|
16
|
+
* @param verdict - The threat verdict / 威脅判決
|
|
17
|
+
* @param eventDescription - Event description / 事件描述
|
|
18
|
+
* @returns Notification result / 通知結果
|
|
19
|
+
*/
|
|
20
|
+
export declare function sendSlackNotify(config: SlackConfig, verdict: ThreatVerdict, eventDescription: string): Promise<NotificationResult>;
|
|
21
|
+
//# sourceMappingURL=slack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../src/notify/slack.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIlF;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,aAAa,EACtB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,kBAAkB,CAAC,CAoC7B"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack Webhook notification integration
|
|
3
|
+
* Slack Webhook 通知整合
|
|
4
|
+
*
|
|
5
|
+
* Sends threat alerts to Slack channels via Incoming Webhooks.
|
|
6
|
+
* 透過 Incoming Webhooks 將威脅警報發送至 Slack 頻道。
|
|
7
|
+
*
|
|
8
|
+
* @module @panguard-ai/panguard-guard/notify/slack
|
|
9
|
+
*/
|
|
10
|
+
import { request } from 'node:https';
|
|
11
|
+
import { createLogger } from '@panguard-ai/core';
|
|
12
|
+
const logger = createLogger('panguard-guard:notify:slack');
|
|
13
|
+
/**
|
|
14
|
+
* Send a threat alert via Slack Webhook
|
|
15
|
+
* 透過 Slack Webhook 發送威脅警報
|
|
16
|
+
*
|
|
17
|
+
* @param config - Slack webhook configuration / Slack webhook 配置
|
|
18
|
+
* @param verdict - The threat verdict / 威脅判決
|
|
19
|
+
* @param eventDescription - Event description / 事件描述
|
|
20
|
+
* @returns Notification result / 通知結果
|
|
21
|
+
*/
|
|
22
|
+
export async function sendSlackNotify(config, verdict, eventDescription) {
|
|
23
|
+
const color = verdict.conclusion === 'malicious'
|
|
24
|
+
? '#dc3545'
|
|
25
|
+
: verdict.conclusion === 'suspicious'
|
|
26
|
+
? '#ffc107'
|
|
27
|
+
: '#28a745';
|
|
28
|
+
const payload = {
|
|
29
|
+
attachments: [
|
|
30
|
+
{
|
|
31
|
+
color,
|
|
32
|
+
title: `PanguardGuard Alert: ${verdict.conclusion.toUpperCase()}`,
|
|
33
|
+
fields: [
|
|
34
|
+
{ title: 'Confidence', value: `${verdict.confidence}%`, short: true },
|
|
35
|
+
{ title: 'Action', value: verdict.recommendedAction, short: true },
|
|
36
|
+
{ title: 'Event', value: eventDescription, short: false },
|
|
37
|
+
...(verdict.mitreTechnique
|
|
38
|
+
? [{ title: 'MITRE ATT&CK', value: verdict.mitreTechnique, short: true }]
|
|
39
|
+
: []),
|
|
40
|
+
],
|
|
41
|
+
footer: 'PanguardGuard Security',
|
|
42
|
+
ts: Math.floor(Date.now() / 1000).toString(),
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
try {
|
|
47
|
+
await postSlack(config.webhookUrl, payload);
|
|
48
|
+
logger.info('Slack notification sent / Slack 通知已發送');
|
|
49
|
+
return { channel: 'slack', success: true };
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
53
|
+
logger.error(`Slack notification failed: ${msg} / Slack 通知失敗: ${msg}`);
|
|
54
|
+
return { channel: 'slack', success: false, error: msg };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* POST to Slack Webhook URL / 向 Slack Webhook URL 發送 POST
|
|
59
|
+
*/
|
|
60
|
+
function postSlack(webhookUrl, payload) {
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
const body = JSON.stringify(payload);
|
|
63
|
+
const url = new URL(webhookUrl);
|
|
64
|
+
const req = request({
|
|
65
|
+
hostname: url.hostname,
|
|
66
|
+
port: 443,
|
|
67
|
+
path: url.pathname,
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: {
|
|
70
|
+
'Content-Type': 'application/json',
|
|
71
|
+
'Content-Length': Buffer.byteLength(body),
|
|
72
|
+
},
|
|
73
|
+
}, (res) => {
|
|
74
|
+
let data = '';
|
|
75
|
+
res.on('data', (chunk) => {
|
|
76
|
+
data += chunk.toString();
|
|
77
|
+
});
|
|
78
|
+
res.on('end', () => {
|
|
79
|
+
if (res.statusCode === 200) {
|
|
80
|
+
resolve();
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
reject(new Error(`Slack HTTP ${res.statusCode}: ${data}`));
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
req.on('error', reject);
|
|
88
|
+
req.write(body);
|
|
89
|
+
req.end();
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=slack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack.js","sourceRoot":"","sources":["../../src/notify/slack.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram Bot notification integration
|
|
3
|
+
* Telegram Bot 通知整合
|
|
4
|
+
*
|
|
5
|
+
* Sends threat alerts to Telegram chats via Bot API.
|
|
6
|
+
* 透過 Bot API 將威脅警報發送至 Telegram 聊天。
|
|
7
|
+
*
|
|
8
|
+
* @module @panguard-ai/panguard-guard/notify/telegram
|
|
9
|
+
*/
|
|
10
|
+
import type { TelegramConfig, NotificationResult, ThreatVerdict } from '../types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Send a threat alert via Telegram Bot
|
|
13
|
+
* 透過 Telegram Bot 發送威脅警報
|
|
14
|
+
*
|
|
15
|
+
* @param config - Telegram Bot configuration / Telegram Bot 配置
|
|
16
|
+
* @param verdict - The threat verdict / 威脅判決
|
|
17
|
+
* @param eventDescription - Event description / 事件描述
|
|
18
|
+
* @returns Notification result / 通知結果
|
|
19
|
+
*/
|
|
20
|
+
export declare function sendTelegramNotify(config: TelegramConfig, verdict: ThreatVerdict, eventDescription: string): Promise<NotificationResult>;
|
|
21
|
+
//# sourceMappingURL=telegram.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegram.d.ts","sourceRoot":"","sources":["../../src/notify/telegram.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIrF;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,aAAa,EACtB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,kBAAkB,CAAC,CA0B7B"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram Bot notification integration
|
|
3
|
+
* Telegram Bot 通知整合
|
|
4
|
+
*
|
|
5
|
+
* Sends threat alerts to Telegram chats via Bot API.
|
|
6
|
+
* 透過 Bot API 將威脅警報發送至 Telegram 聊天。
|
|
7
|
+
*
|
|
8
|
+
* @module @panguard-ai/panguard-guard/notify/telegram
|
|
9
|
+
*/
|
|
10
|
+
import { request } from 'node:https';
|
|
11
|
+
import { createLogger } from '@panguard-ai/core';
|
|
12
|
+
const logger = createLogger('panguard-guard:notify:telegram');
|
|
13
|
+
/**
|
|
14
|
+
* Send a threat alert via Telegram Bot
|
|
15
|
+
* 透過 Telegram Bot 發送威脅警報
|
|
16
|
+
*
|
|
17
|
+
* @param config - Telegram Bot configuration / Telegram Bot 配置
|
|
18
|
+
* @param verdict - The threat verdict / 威脅判決
|
|
19
|
+
* @param eventDescription - Event description / 事件描述
|
|
20
|
+
* @returns Notification result / 通知結果
|
|
21
|
+
*/
|
|
22
|
+
export async function sendTelegramNotify(config, verdict, eventDescription) {
|
|
23
|
+
const conclusionLabel = verdict.conclusion === 'malicious'
|
|
24
|
+
? 'MALICIOUS'
|
|
25
|
+
: verdict.conclusion === 'suspicious'
|
|
26
|
+
? 'SUSPICIOUS'
|
|
27
|
+
: 'BENIGN';
|
|
28
|
+
const text = `*[PanguardGuard Alert]*\n\n` +
|
|
29
|
+
`*Conclusion:* ${conclusionLabel}\n` +
|
|
30
|
+
`*Confidence:* ${verdict.confidence}%\n` +
|
|
31
|
+
`*Action:* ${verdict.recommendedAction}\n` +
|
|
32
|
+
`*Event:* ${escapeMarkdown(eventDescription)}\n` +
|
|
33
|
+
`${verdict.mitreTechnique ? `*MITRE:* ${verdict.mitreTechnique}\n` : ''}` +
|
|
34
|
+
`*Time:* ${new Date().toISOString()}`;
|
|
35
|
+
try {
|
|
36
|
+
await postTelegram(config.botToken, config.chatId, text);
|
|
37
|
+
logger.info('Telegram notification sent / Telegram 通知已發送');
|
|
38
|
+
return { channel: 'telegram', success: true };
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
42
|
+
logger.error(`Telegram notification failed: ${msg} / Telegram 通知失敗: ${msg}`);
|
|
43
|
+
return { channel: 'telegram', success: false, error: msg };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* POST to Telegram Bot API sendMessage
|
|
48
|
+
* 向 Telegram Bot API sendMessage 發送 POST
|
|
49
|
+
*/
|
|
50
|
+
function postTelegram(botToken, chatId, text) {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
const payload = JSON.stringify({
|
|
53
|
+
chat_id: chatId,
|
|
54
|
+
text,
|
|
55
|
+
parse_mode: 'Markdown',
|
|
56
|
+
});
|
|
57
|
+
const req = request({
|
|
58
|
+
hostname: 'api.telegram.org',
|
|
59
|
+
port: 443,
|
|
60
|
+
path: `/bot${botToken}/sendMessage`,
|
|
61
|
+
method: 'POST',
|
|
62
|
+
headers: {
|
|
63
|
+
'Content-Type': 'application/json',
|
|
64
|
+
'Content-Length': Buffer.byteLength(payload),
|
|
65
|
+
},
|
|
66
|
+
}, (res) => {
|
|
67
|
+
let data = '';
|
|
68
|
+
res.on('data', (chunk) => {
|
|
69
|
+
data += chunk.toString();
|
|
70
|
+
});
|
|
71
|
+
res.on('end', () => {
|
|
72
|
+
if (res.statusCode === 200) {
|
|
73
|
+
resolve();
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
reject(new Error(`Telegram HTTP ${res.statusCode}: ${data}`));
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
req.on('error', reject);
|
|
81
|
+
req.write(payload);
|
|
82
|
+
req.end();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/** Escape Markdown special characters / 逸出 Markdown 特殊字元 */
|
|
86
|
+
function escapeMarkdown(text) {
|
|
87
|
+
return text.replace(/[_*[\]()~`>#+\-=|{}.!]/g, '\\$&');
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=telegram.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegram.js","sourceRoot":"","sources":["../../src/notify/telegram.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Quarantine - Isolate suspicious files with metadata and restore
|
|
3
|
+
* 檔案隔離 - 隔離可疑檔案(含中繼資料與還原功能)
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Move files to quarantine directory with restricted permissions
|
|
7
|
+
* - SHA-256 hash recording for evidence
|
|
8
|
+
* - Quarantine manifest (JSON) for tracking
|
|
9
|
+
* - Restore functionality to return files to original location
|
|
10
|
+
*
|
|
11
|
+
* @module @panguard-ai/panguard-guard/response/file-quarantine
|
|
12
|
+
*/
|
|
13
|
+
/** Quarantine record for a single file / 單一檔案的隔離紀錄 */
|
|
14
|
+
export interface QuarantineRecord {
|
|
15
|
+
id: string;
|
|
16
|
+
originalPath: string;
|
|
17
|
+
quarantinePath: string;
|
|
18
|
+
sha256: string;
|
|
19
|
+
fileSize: number;
|
|
20
|
+
reason: string;
|
|
21
|
+
quarantinedAt: string;
|
|
22
|
+
restoredAt?: string;
|
|
23
|
+
}
|
|
24
|
+
/** Quarantine manifest stored as JSON / 隔離清單(JSON 格式) */
|
|
25
|
+
export interface QuarantineManifest {
|
|
26
|
+
version: 1;
|
|
27
|
+
records: QuarantineRecord[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Manages file quarantine with metadata tracking and restore
|
|
31
|
+
* 管理檔案隔離(含中繼資料追蹤與還原功能)
|
|
32
|
+
*/
|
|
33
|
+
export declare class FileQuarantine {
|
|
34
|
+
private readonly quarantineDir;
|
|
35
|
+
private readonly manifestPath;
|
|
36
|
+
private manifest;
|
|
37
|
+
constructor(quarantineDir?: string);
|
|
38
|
+
/** Initialize quarantine directory / 初始化隔離目錄 */
|
|
39
|
+
initialize(): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Quarantine a file
|
|
42
|
+
* 隔離檔案
|
|
43
|
+
*/
|
|
44
|
+
quarantine(filePath: string, reason: string): Promise<QuarantineRecord>;
|
|
45
|
+
/**
|
|
46
|
+
* Restore a quarantined file to its original location
|
|
47
|
+
* 還原隔離檔案到原始位置
|
|
48
|
+
*/
|
|
49
|
+
restore(id: string): Promise<{
|
|
50
|
+
success: boolean;
|
|
51
|
+
message: string;
|
|
52
|
+
}>;
|
|
53
|
+
/** Get all quarantine records / 取得所有隔離紀錄 */
|
|
54
|
+
getRecords(): QuarantineRecord[];
|
|
55
|
+
/** Get active (not restored) quarantine records / 取得未還原的隔離紀錄 */
|
|
56
|
+
getActiveRecords(): QuarantineRecord[];
|
|
57
|
+
/** Find record by ID / 以 ID 搜尋紀錄 */
|
|
58
|
+
findRecord(id: string): QuarantineRecord | undefined;
|
|
59
|
+
/** Count files in quarantine / 隔離區檔案數量 */
|
|
60
|
+
getQuarantineCount(): Promise<number>;
|
|
61
|
+
private saveManifest;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=file-quarantine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-quarantine.d.ts","sourceRoot":"","sources":["../../src/response/file-quarantine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAUH,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,yDAAyD;AACzD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,gBAAgB,EAAE,CAAC;CAC7B;AAED;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAmD;gBAEvD,aAAa,CAAC,EAAE,MAAM;IAKlC,gDAAgD;IAC1C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAWjC;;;OAGG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA+C7E;;;OAGG;IACG,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IA8BzE,4CAA4C;IAC5C,UAAU,IAAI,gBAAgB,EAAE;IAIhC,gEAAgE;IAChE,gBAAgB,IAAI,gBAAgB,EAAE;IAItC,oCAAoC;IACpC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAIpD,0CAA0C;IACpC,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC;YAS7B,YAAY;CAG3B"}
|