@panguard-ai/core 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/adapters/adapter-registry.d.ts +150 -0
- package/dist/adapters/adapter-registry.d.ts.map +1 -0
- package/dist/adapters/adapter-registry.js +271 -0
- package/dist/adapters/adapter-registry.js.map +1 -0
- package/dist/adapters/base-adapter.d.ts +101 -0
- package/dist/adapters/base-adapter.d.ts.map +1 -0
- package/dist/adapters/base-adapter.js +160 -0
- package/dist/adapters/base-adapter.js.map +1 -0
- package/dist/adapters/defender-adapter.d.ts +90 -0
- package/dist/adapters/defender-adapter.d.ts.map +1 -0
- package/dist/adapters/defender-adapter.js +227 -0
- package/dist/adapters/defender-adapter.js.map +1 -0
- package/dist/adapters/index.d.ts +22 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +23 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/syslog-adapter.d.ts +207 -0
- package/dist/adapters/syslog-adapter.d.ts.map +1 -0
- package/dist/adapters/syslog-adapter.js +432 -0
- package/dist/adapters/syslog-adapter.js.map +1 -0
- package/dist/adapters/types.d.ts +135 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +13 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/adapters/wazuh-adapter.d.ts +120 -0
- package/dist/adapters/wazuh-adapter.d.ts.map +1 -0
- package/dist/adapters/wazuh-adapter.js +266 -0
- package/dist/adapters/wazuh-adapter.js.map +1 -0
- package/dist/ai/claude-provider.d.ts +66 -0
- package/dist/ai/claude-provider.d.ts.map +1 -0
- package/dist/ai/claude-provider.js +166 -0
- package/dist/ai/claude-provider.js.map +1 -0
- package/dist/ai/funnel-router.d.ts +75 -0
- package/dist/ai/funnel-router.d.ts.map +1 -0
- package/dist/ai/funnel-router.js +173 -0
- package/dist/ai/funnel-router.js.map +1 -0
- package/dist/ai/index.d.ts +77 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +95 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/ollama-provider.d.ts +73 -0
- package/dist/ai/ollama-provider.d.ts.map +1 -0
- package/dist/ai/ollama-provider.js +200 -0
- package/dist/ai/ollama-provider.js.map +1 -0
- package/dist/ai/openai-provider.d.ts +70 -0
- package/dist/ai/openai-provider.d.ts.map +1 -0
- package/dist/ai/openai-provider.js +175 -0
- package/dist/ai/openai-provider.js.map +1 -0
- package/dist/ai/prompts/event-classifier.d.ts +25 -0
- package/dist/ai/prompts/event-classifier.d.ts.map +1 -0
- package/dist/ai/prompts/event-classifier.js +94 -0
- package/dist/ai/prompts/event-classifier.js.map +1 -0
- package/dist/ai/prompts/index.d.ts +13 -0
- package/dist/ai/prompts/index.d.ts.map +1 -0
- package/dist/ai/prompts/index.js +13 -0
- package/dist/ai/prompts/index.js.map +1 -0
- package/dist/ai/prompts/report-generator.d.ts +25 -0
- package/dist/ai/prompts/report-generator.d.ts.map +1 -0
- package/dist/ai/prompts/report-generator.js +131 -0
- package/dist/ai/prompts/report-generator.js.map +1 -0
- package/dist/ai/prompts/threat-analyzer.d.ts +26 -0
- package/dist/ai/prompts/threat-analyzer.d.ts.map +1 -0
- package/dist/ai/prompts/threat-analyzer.js +75 -0
- package/dist/ai/prompts/threat-analyzer.js.map +1 -0
- package/dist/ai/provider-base.d.ts +100 -0
- package/dist/ai/provider-base.d.ts.map +1 -0
- package/dist/ai/provider-base.js +166 -0
- package/dist/ai/provider-base.js.map +1 -0
- package/dist/ai/response-parser.d.ts +36 -0
- package/dist/ai/response-parser.d.ts.map +1 -0
- package/dist/ai/response-parser.js +195 -0
- package/dist/ai/response-parser.js.map +1 -0
- package/dist/ai/token-tracker.d.ts +72 -0
- package/dist/ai/token-tracker.d.ts.map +1 -0
- package/dist/ai/token-tracker.js +145 -0
- package/dist/ai/token-tracker.js.map +1 -0
- package/dist/ai/types.d.ts +138 -0
- package/dist/ai/types.d.ts.map +1 -0
- package/dist/ai/types.js +12 -0
- package/dist/ai/types.js.map +1 -0
- package/dist/cli/index.d.ts +146 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +515 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/prompts.d.ts +58 -0
- package/dist/cli/prompts.d.ts.map +1 -0
- package/dist/cli/prompts.js +327 -0
- package/dist/cli/prompts.js.map +1 -0
- package/dist/cli/wizard.d.ts +58 -0
- package/dist/cli/wizard.d.ts.map +1 -0
- package/dist/cli/wizard.js +200 -0
- package/dist/cli/wizard.js.map +1 -0
- package/dist/discovery/firewall-checker.d.ts +28 -0
- package/dist/discovery/firewall-checker.d.ts.map +1 -0
- package/dist/discovery/firewall-checker.js +379 -0
- package/dist/discovery/firewall-checker.js.map +1 -0
- package/dist/discovery/index.d.ts +23 -0
- package/dist/discovery/index.d.ts.map +1 -0
- package/dist/discovery/index.js +29 -0
- package/dist/discovery/index.js.map +1 -0
- package/dist/discovery/network-scanner.d.ts +60 -0
- package/dist/discovery/network-scanner.d.ts.map +1 -0
- package/dist/discovery/network-scanner.js +640 -0
- package/dist/discovery/network-scanner.js.map +1 -0
- package/dist/discovery/os-detector.d.ts +24 -0
- package/dist/discovery/os-detector.d.ts.map +1 -0
- package/dist/discovery/os-detector.js +253 -0
- package/dist/discovery/os-detector.js.map +1 -0
- package/dist/discovery/osquery-provider.d.ts +127 -0
- package/dist/discovery/osquery-provider.d.ts.map +1 -0
- package/dist/discovery/osquery-provider.js +214 -0
- package/dist/discovery/osquery-provider.js.map +1 -0
- package/dist/discovery/risk-scorer.d.ts +66 -0
- package/dist/discovery/risk-scorer.d.ts.map +1 -0
- package/dist/discovery/risk-scorer.js +294 -0
- package/dist/discovery/risk-scorer.js.map +1 -0
- package/dist/discovery/security-tools.d.ts +31 -0
- package/dist/discovery/security-tools.d.ts.map +1 -0
- package/dist/discovery/security-tools.js +346 -0
- package/dist/discovery/security-tools.js.map +1 -0
- package/dist/discovery/service-detector.d.ts +28 -0
- package/dist/discovery/service-detector.d.ts.map +1 -0
- package/dist/discovery/service-detector.js +300 -0
- package/dist/discovery/service-detector.js.map +1 -0
- package/dist/discovery/types.d.ts +502 -0
- package/dist/discovery/types.d.ts.map +1 -0
- package/dist/discovery/types.js +12 -0
- package/dist/discovery/types.js.map +1 -0
- package/dist/discovery/user-auditor.d.ts +28 -0
- package/dist/discovery/user-auditor.d.ts.map +1 -0
- package/dist/discovery/user-auditor.js +385 -0
- package/dist/discovery/user-auditor.js.map +1 -0
- package/dist/i18n/config.d.ts +45 -0
- package/dist/i18n/config.d.ts.map +1 -0
- package/dist/i18n/config.js +135 -0
- package/dist/i18n/config.js.map +1 -0
- package/dist/i18n/index.d.ts +8 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +8 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/monitor/event-normalizer.d.ts +102 -0
- package/dist/monitor/event-normalizer.d.ts.map +1 -0
- package/dist/monitor/event-normalizer.js +195 -0
- package/dist/monitor/event-normalizer.js.map +1 -0
- package/dist/monitor/file-monitor.d.ts +90 -0
- package/dist/monitor/file-monitor.d.ts.map +1 -0
- package/dist/monitor/file-monitor.js +222 -0
- package/dist/monitor/file-monitor.js.map +1 -0
- package/dist/monitor/index.d.ts +147 -0
- package/dist/monitor/index.d.ts.map +1 -0
- package/dist/monitor/index.js +293 -0
- package/dist/monitor/index.js.map +1 -0
- package/dist/monitor/log-monitor.d.ts +102 -0
- package/dist/monitor/log-monitor.d.ts.map +1 -0
- package/dist/monitor/log-monitor.js +245 -0
- package/dist/monitor/log-monitor.js.map +1 -0
- package/dist/monitor/network-monitor.d.ts +103 -0
- package/dist/monitor/network-monitor.d.ts.map +1 -0
- package/dist/monitor/network-monitor.js +336 -0
- package/dist/monitor/network-monitor.js.map +1 -0
- package/dist/monitor/process-monitor.d.ts +108 -0
- package/dist/monitor/process-monitor.d.ts.map +1 -0
- package/dist/monitor/process-monitor.js +245 -0
- package/dist/monitor/process-monitor.js.map +1 -0
- package/dist/monitor/threat-intel-feeds.d.ts +141 -0
- package/dist/monitor/threat-intel-feeds.d.ts.map +1 -0
- package/dist/monitor/threat-intel-feeds.js +430 -0
- package/dist/monitor/threat-intel-feeds.js.map +1 -0
- package/dist/monitor/threat-intel.d.ts +83 -0
- package/dist/monitor/threat-intel.d.ts.map +1 -0
- package/dist/monitor/threat-intel.js +215 -0
- package/dist/monitor/threat-intel.js.map +1 -0
- package/dist/monitor/types.d.ts +65 -0
- package/dist/monitor/types.d.ts.map +1 -0
- package/dist/monitor/types.js +20 -0
- package/dist/monitor/types.js.map +1 -0
- package/dist/rules/index.d.ts +115 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +244 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/rule-loader.d.ts +54 -0
- package/dist/rules/rule-loader.d.ts.map +1 -0
- package/dist/rules/rule-loader.js +167 -0
- package/dist/rules/rule-loader.js.map +1 -0
- package/dist/rules/sigma-matcher.d.ts +40 -0
- package/dist/rules/sigma-matcher.d.ts.map +1 -0
- package/dist/rules/sigma-matcher.js +447 -0
- package/dist/rules/sigma-matcher.js.map +1 -0
- package/dist/rules/sigma-parser.d.ts +36 -0
- package/dist/rules/sigma-parser.d.ts.map +1 -0
- package/dist/rules/sigma-parser.js +180 -0
- package/dist/rules/sigma-parser.js.map +1 -0
- package/dist/rules/types.d.ts +112 -0
- package/dist/rules/types.d.ts.map +1 -0
- package/dist/rules/types.js +11 -0
- package/dist/rules/types.js.map +1 -0
- package/dist/rules/yara-scanner.d.ts +103 -0
- package/dist/rules/yara-scanner.d.ts.map +1 -0
- package/dist/rules/yara-scanner.js +421 -0
- package/dist/rules/yara-scanner.js.map +1 -0
- package/dist/scoring/achievements.d.ts +76 -0
- package/dist/scoring/achievements.d.ts.map +1 -0
- package/dist/scoring/achievements.js +211 -0
- package/dist/scoring/achievements.js.map +1 -0
- package/dist/scoring/index.d.ts +3 -0
- package/dist/scoring/index.d.ts.map +1 -0
- package/dist/scoring/index.js +3 -0
- package/dist/scoring/index.js.map +1 -0
- package/dist/scoring/security-score.d.ts +60 -0
- package/dist/scoring/security-score.d.ts.map +1 -0
- package/dist/scoring/security-score.js +211 -0
- package/dist/scoring/security-score.js.map +1 -0
- package/dist/types.d.ts +71 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +38 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +71 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/validation.d.ts +35 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +56 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Syslog receiver adapter (RFC 5424)
|
|
3
|
+
* Syslog 接收器對接器 (RFC 5424)
|
|
4
|
+
*
|
|
5
|
+
* Receives syslog messages over UDP, parses RFC 5424 format, and converts
|
|
6
|
+
* them to standardized AdapterAlerts. Supports buffered alert retrieval
|
|
7
|
+
* and callback-based real-time alert notification.
|
|
8
|
+
* 透過 UDP 接收 syslog 訊息,解析 RFC 5424 格式,並將其轉換為
|
|
9
|
+
* 標準化的 AdapterAlert。支援緩衝告警取得和基於回呼的即時告警通知。
|
|
10
|
+
*
|
|
11
|
+
* @module @panguard-ai/core/adapters/syslog-adapter
|
|
12
|
+
*/
|
|
13
|
+
import type { AdapterConfig, AdapterAlert } from './types.js';
|
|
14
|
+
import { BaseAdapter } from './base-adapter.js';
|
|
15
|
+
/**
|
|
16
|
+
* Parsed RFC 5424 syslog message fields
|
|
17
|
+
* 已解析的 RFC 5424 syslog 訊息欄位
|
|
18
|
+
*/
|
|
19
|
+
interface ParsedSyslogMessage {
|
|
20
|
+
/** Syslog facility code (0-23) / Syslog 設施碼 (0-23) */
|
|
21
|
+
facility: number;
|
|
22
|
+
/** Syslog severity code (0-7) / Syslog 嚴重等級碼 (0-7) */
|
|
23
|
+
severityCode: number;
|
|
24
|
+
/** RFC 5424 version / RFC 5424 版本 */
|
|
25
|
+
version: number;
|
|
26
|
+
/** Message timestamp (ISO 8601 or NILVALUE) / 訊息時間戳(ISO 8601 或 NILVALUE) */
|
|
27
|
+
timestamp: string;
|
|
28
|
+
/** Hostname / 主機名稱 */
|
|
29
|
+
hostname: string;
|
|
30
|
+
/** Application name / 應用程式名稱 */
|
|
31
|
+
appName: string;
|
|
32
|
+
/** Process ID / 行程 ID */
|
|
33
|
+
procId: string;
|
|
34
|
+
/** Message ID / 訊息 ID */
|
|
35
|
+
msgId: string;
|
|
36
|
+
/** Structured data / 結構化資料 */
|
|
37
|
+
structuredData: string;
|
|
38
|
+
/** Message body / 訊息本體 */
|
|
39
|
+
message: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Parse a raw syslog message string into structured fields
|
|
43
|
+
* 將原始 syslog 訊息字串解析為結構化欄位
|
|
44
|
+
*
|
|
45
|
+
* Supports RFC 5424 format. Falls back to a best-effort parse for
|
|
46
|
+
* non-conforming messages by treating the entire string as the message body.
|
|
47
|
+
* 支援 RFC 5424 格式。對於不符合格式的訊息,會以盡力解析方式
|
|
48
|
+
* 將整個字串視為訊息本體。
|
|
49
|
+
*
|
|
50
|
+
* @param raw - Raw syslog message string / 原始 syslog 訊息字串
|
|
51
|
+
* @returns Parsed syslog message fields / 已解析的 syslog 訊息欄位
|
|
52
|
+
*/
|
|
53
|
+
export declare function parseSyslogMessage(raw: string): ParsedSyslogMessage;
|
|
54
|
+
/**
|
|
55
|
+
* Callback type for real-time alert notification
|
|
56
|
+
* 即時告警通知的回呼型別
|
|
57
|
+
*/
|
|
58
|
+
export type SyslogAlertCallback = (alert: AdapterAlert) => void;
|
|
59
|
+
/**
|
|
60
|
+
* Syslog receiver security adapter (RFC 5424)
|
|
61
|
+
* Syslog 接收器安全對接器 (RFC 5424)
|
|
62
|
+
*
|
|
63
|
+
* Listens for syslog messages on a UDP socket, parses RFC 5424 format,
|
|
64
|
+
* buffers parsed alerts, and provides both callback-based real-time
|
|
65
|
+
* notification and pull-based alert retrieval.
|
|
66
|
+
*
|
|
67
|
+
* 在 UDP socket 上監聽 syslog 訊息,解析 RFC 5424 格式,
|
|
68
|
+
* 緩衝已解析的告警,並提供基於回呼的即時通知和拉取式告警取得。
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const adapter = new SyslogAdapter({ enabled: true });
|
|
73
|
+
*
|
|
74
|
+
* // Set up real-time callback / 設定即時回呼
|
|
75
|
+
* adapter.onAlert((alert) => {
|
|
76
|
+
* console.log('New syslog alert:', alert);
|
|
77
|
+
* });
|
|
78
|
+
*
|
|
79
|
+
* // Start listening on port 514 / 開始在埠 514 上監聽
|
|
80
|
+
* await adapter.start(514);
|
|
81
|
+
*
|
|
82
|
+
* // Later: retrieve buffered alerts / 稍後:取得緩衝的告警
|
|
83
|
+
* const alerts = await adapter.getAlerts();
|
|
84
|
+
*
|
|
85
|
+
* // Stop listening / 停止監聽
|
|
86
|
+
* adapter.stop();
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export declare class SyslogAdapter extends BaseAdapter {
|
|
90
|
+
/** @inheritdoc */
|
|
91
|
+
readonly name = "Syslog Receiver";
|
|
92
|
+
/** @inheritdoc */
|
|
93
|
+
readonly type = "syslog";
|
|
94
|
+
/**
|
|
95
|
+
* UDP socket for receiving syslog messages
|
|
96
|
+
* 用於接收 syslog 訊息的 UDP socket
|
|
97
|
+
*/
|
|
98
|
+
private socket;
|
|
99
|
+
/**
|
|
100
|
+
* Buffer of alerts received since last getAlerts() call
|
|
101
|
+
* 自上次 getAlerts() 呼叫以來接收的告警緩衝區
|
|
102
|
+
*/
|
|
103
|
+
private alertBuffer;
|
|
104
|
+
/**
|
|
105
|
+
* Real-time alert callback
|
|
106
|
+
* 即時告警回呼
|
|
107
|
+
*/
|
|
108
|
+
private alertCallback;
|
|
109
|
+
/**
|
|
110
|
+
* Whether the socket is currently listening
|
|
111
|
+
* socket 是否正在監聽
|
|
112
|
+
*/
|
|
113
|
+
private listening;
|
|
114
|
+
/**
|
|
115
|
+
* Port the socket is listening on
|
|
116
|
+
* socket 正在監聽的埠
|
|
117
|
+
*/
|
|
118
|
+
private listenPort;
|
|
119
|
+
/**
|
|
120
|
+
* Create a new SyslogAdapter instance
|
|
121
|
+
* 建立新的 SyslogAdapter 實例
|
|
122
|
+
*
|
|
123
|
+
* @param config - Adapter configuration / 對接器配置
|
|
124
|
+
*/
|
|
125
|
+
constructor(config?: AdapterConfig);
|
|
126
|
+
/**
|
|
127
|
+
* Register a callback for real-time alert notification
|
|
128
|
+
* 註冊即時告警通知的回呼
|
|
129
|
+
*
|
|
130
|
+
* The callback is invoked for each parsed syslog message as it arrives.
|
|
131
|
+
* Only one callback can be registered at a time; setting a new one
|
|
132
|
+
* replaces the previous.
|
|
133
|
+
* 每收到一筆已解析的 syslog 訊息就會呼叫此回呼。
|
|
134
|
+
* 同一時間只能註冊一個回呼;設定新的會取代前一個。
|
|
135
|
+
*
|
|
136
|
+
* @param callback - Alert callback function / 告警回呼函式
|
|
137
|
+
*/
|
|
138
|
+
onAlert(callback: SyslogAlertCallback): void;
|
|
139
|
+
/**
|
|
140
|
+
* Check if the syslog receiver is available (i.e. listening)
|
|
141
|
+
* 檢查 syslog 接收器是否可用(即正在監聽)
|
|
142
|
+
*
|
|
143
|
+
* @returns True if the UDP socket is bound and listening / 若 UDP socket 已綁定且正在監聽則回傳 true
|
|
144
|
+
*/
|
|
145
|
+
isAvailable(): Promise<boolean>;
|
|
146
|
+
/**
|
|
147
|
+
* Start the UDP syslog receiver
|
|
148
|
+
* 啟動 UDP syslog 接收器
|
|
149
|
+
*
|
|
150
|
+
* Binds a UDP4 socket to the specified port and begins listening
|
|
151
|
+
* for incoming syslog messages. Each message is parsed according to
|
|
152
|
+
* RFC 5424 and added to the alert buffer.
|
|
153
|
+
* 將 UDP4 socket 綁定到指定埠並開始監聽傳入的 syslog 訊息。
|
|
154
|
+
* 每筆訊息都會依 RFC 5424 解析並新增到告警緩衝區。
|
|
155
|
+
*
|
|
156
|
+
* @param port - UDP port to listen on (default: 514) / 要監聽的 UDP 埠(預設:514)
|
|
157
|
+
* @returns Promise that resolves when the socket is bound / socket 綁定後 resolve 的 Promise
|
|
158
|
+
*/
|
|
159
|
+
start(port?: number): Promise<void>;
|
|
160
|
+
/**
|
|
161
|
+
* Stop the UDP syslog receiver
|
|
162
|
+
* 停止 UDP syslog 接收器
|
|
163
|
+
*
|
|
164
|
+
* Closes the UDP socket and stops listening for messages.
|
|
165
|
+
* The alert buffer is preserved and can still be read via getAlerts().
|
|
166
|
+
* 關閉 UDP socket 並停止監聽訊息。
|
|
167
|
+
* 告警緩衝區會被保留,仍可透過 getAlerts() 讀取。
|
|
168
|
+
*/
|
|
169
|
+
stop(): void;
|
|
170
|
+
/**
|
|
171
|
+
* Retrieve buffered alerts and clear the buffer
|
|
172
|
+
* 取得緩衝的告警並清除緩衝區
|
|
173
|
+
*
|
|
174
|
+
* Returns all alerts accumulated since the last call to getAlerts().
|
|
175
|
+
* Optionally filters by a cutoff date. The buffer is cleared after
|
|
176
|
+
* retrieval to prevent duplicate processing.
|
|
177
|
+
* 回傳自上次呼叫 getAlerts() 以來累積的所有告警。
|
|
178
|
+
* 可選依截止日期過濾。取得後會清除緩衝區以防止重複處理。
|
|
179
|
+
*
|
|
180
|
+
* @param since - Optional cutoff date / 可選截止日期
|
|
181
|
+
* @returns Array of buffered adapter alerts / 緩衝的對接器告警陣列
|
|
182
|
+
*/
|
|
183
|
+
getAlerts(since?: Date): Promise<AdapterAlert[]>;
|
|
184
|
+
/**
|
|
185
|
+
* Get the current buffer size (number of pending alerts)
|
|
186
|
+
* 取得目前緩衝區大小(待處理告警數量)
|
|
187
|
+
*
|
|
188
|
+
* @returns Number of alerts in the buffer / 緩衝區中的告警數量
|
|
189
|
+
*/
|
|
190
|
+
getBufferSize(): number;
|
|
191
|
+
/**
|
|
192
|
+
* Check if the receiver is currently listening
|
|
193
|
+
* 檢查接收器是否正在監聽
|
|
194
|
+
*
|
|
195
|
+
* @returns True if listening / 若正在監聽則回傳 true
|
|
196
|
+
*/
|
|
197
|
+
isListening(): boolean;
|
|
198
|
+
/**
|
|
199
|
+
* Get the port the receiver is listening on
|
|
200
|
+
* 取得接收器正在監聽的埠
|
|
201
|
+
*
|
|
202
|
+
* @returns Port number, or 0 if not listening / 埠號,若未監聽則為 0
|
|
203
|
+
*/
|
|
204
|
+
getPort(): number;
|
|
205
|
+
}
|
|
206
|
+
export {};
|
|
207
|
+
//# sourceMappingURL=syslog-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syslog-adapter.d.ts","sourceRoot":"","sources":["../../src/adapters/syslog-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA8DhD;;;GAGG;AACH,UAAU,mBAAmB;IAC3B,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,YAAY,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,yBAAyB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB;AAcD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAkEnE;AA4BD;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;AAEhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,aAAc,SAAQ,WAAW;IAC5C,kBAAkB;IAClB,QAAQ,CAAC,IAAI,qBAAqB;IAElC,kBAAkB;IAClB,QAAQ,CAAC,IAAI,YAAY;IAEzB;;;OAGG;IACH,OAAO,CAAC,MAAM,CAA6B;IAE3C;;;OAGG;IACH,OAAO,CAAC,WAAW,CAAsB;IAEzC;;;OAGG;IACH,OAAO,CAAC,aAAa,CAAoC;IAEzD;;;OAGG;IACH,OAAO,CAAC,SAAS,CAAS;IAE1B;;;OAGG;IACH,OAAO,CAAC,UAAU,CAAa;IAE/B;;;;;OAKG;gBACS,MAAM,GAAE,aAAiC;IAIrD;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IAI5C;;;;;OAKG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAIrC;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,IAAI,GAAE,MAA4B,GAAG,OAAO,CAAC,IAAI,CAAC;IA6ExD;;;;;;;;OAQG;IACH,IAAI,IAAI,IAAI;IAcZ;;;;;;;;;;;;OAYG;IACG,SAAS,CAAC,KAAK,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAyBtD;;;;;OAKG;IACH,aAAa,IAAI,MAAM;IAIvB;;;;;OAKG;IACH,WAAW,IAAI,OAAO;IAItB;;;;;OAKG;IACH,OAAO,IAAI,MAAM;CAGlB"}
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Syslog receiver adapter (RFC 5424)
|
|
3
|
+
* Syslog 接收器對接器 (RFC 5424)
|
|
4
|
+
*
|
|
5
|
+
* Receives syslog messages over UDP, parses RFC 5424 format, and converts
|
|
6
|
+
* them to standardized AdapterAlerts. Supports buffered alert retrieval
|
|
7
|
+
* and callback-based real-time alert notification.
|
|
8
|
+
* 透過 UDP 接收 syslog 訊息,解析 RFC 5424 格式,並將其轉換為
|
|
9
|
+
* 標準化的 AdapterAlert。支援緩衝告警取得和基於回呼的即時告警通知。
|
|
10
|
+
*
|
|
11
|
+
* @module @panguard-ai/core/adapters/syslog-adapter
|
|
12
|
+
*/
|
|
13
|
+
import dgram from 'node:dgram';
|
|
14
|
+
import { randomUUID } from 'node:crypto';
|
|
15
|
+
import { BaseAdapter } from './base-adapter.js';
|
|
16
|
+
/**
|
|
17
|
+
* Default UDP port for syslog reception
|
|
18
|
+
* syslog 接收的預設 UDP 埠
|
|
19
|
+
*/
|
|
20
|
+
const DEFAULT_SYSLOG_PORT = 514;
|
|
21
|
+
/**
|
|
22
|
+
* Maximum buffer size for stored alerts before oldest are discarded
|
|
23
|
+
* 在丟棄最舊告警前的最大緩衝區大小
|
|
24
|
+
*/
|
|
25
|
+
const MAX_BUFFER_SIZE = 10000;
|
|
26
|
+
/**
|
|
27
|
+
* RFC 5424 syslog severity levels (0-7)
|
|
28
|
+
* RFC 5424 syslog 嚴重等級 (0-7)
|
|
29
|
+
*
|
|
30
|
+
* See https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1
|
|
31
|
+
*/
|
|
32
|
+
const SYSLOG_SEVERITY_MAP = {
|
|
33
|
+
0: 'critical', // Emergency / 緊急
|
|
34
|
+
1: 'critical', // Alert / 警報
|
|
35
|
+
2: 'critical', // Critical / 重大
|
|
36
|
+
3: 'high', // Error / 錯誤
|
|
37
|
+
4: 'medium', // Warning / 警告
|
|
38
|
+
5: 'low', // Notice / 通知
|
|
39
|
+
6: 'info', // Informational / 資訊
|
|
40
|
+
7: 'info', // Debug / 除錯
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* RFC 5424 syslog facility names
|
|
44
|
+
* RFC 5424 syslog 設施名稱
|
|
45
|
+
*/
|
|
46
|
+
const SYSLOG_FACILITY_NAMES = {
|
|
47
|
+
0: 'kern',
|
|
48
|
+
1: 'user',
|
|
49
|
+
2: 'mail',
|
|
50
|
+
3: 'daemon',
|
|
51
|
+
4: 'auth',
|
|
52
|
+
5: 'syslog',
|
|
53
|
+
6: 'lpr',
|
|
54
|
+
7: 'news',
|
|
55
|
+
8: 'uucp',
|
|
56
|
+
9: 'cron',
|
|
57
|
+
10: 'authpriv',
|
|
58
|
+
11: 'ftp',
|
|
59
|
+
12: 'ntp',
|
|
60
|
+
13: 'security',
|
|
61
|
+
14: 'console',
|
|
62
|
+
15: 'solaris-cron',
|
|
63
|
+
16: 'local0',
|
|
64
|
+
17: 'local1',
|
|
65
|
+
18: 'local2',
|
|
66
|
+
19: 'local3',
|
|
67
|
+
20: 'local4',
|
|
68
|
+
21: 'local5',
|
|
69
|
+
22: 'local6',
|
|
70
|
+
23: 'local7',
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Regular expression for parsing RFC 5424 syslog messages
|
|
74
|
+
* 用於解析 RFC 5424 syslog 訊息的正則表達式
|
|
75
|
+
*
|
|
76
|
+
* RFC 5424 format:
|
|
77
|
+
* <PRI>VERSION SP TIMESTAMP SP HOSTNAME SP APP-NAME SP PROCID SP MSGID SP STRUCTURED-DATA [SP MSG]
|
|
78
|
+
*
|
|
79
|
+
* Where PRI = facility * 8 + severity
|
|
80
|
+
*/
|
|
81
|
+
const RFC5424_REGEX = /^<(\d{1,3})>(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+((?:\[.*?\])+|-)\s*(.*)?$/s;
|
|
82
|
+
/**
|
|
83
|
+
* Parse a raw syslog message string into structured fields
|
|
84
|
+
* 將原始 syslog 訊息字串解析為結構化欄位
|
|
85
|
+
*
|
|
86
|
+
* Supports RFC 5424 format. Falls back to a best-effort parse for
|
|
87
|
+
* non-conforming messages by treating the entire string as the message body.
|
|
88
|
+
* 支援 RFC 5424 格式。對於不符合格式的訊息,會以盡力解析方式
|
|
89
|
+
* 將整個字串視為訊息本體。
|
|
90
|
+
*
|
|
91
|
+
* @param raw - Raw syslog message string / 原始 syslog 訊息字串
|
|
92
|
+
* @returns Parsed syslog message fields / 已解析的 syslog 訊息欄位
|
|
93
|
+
*/
|
|
94
|
+
export function parseSyslogMessage(raw) {
|
|
95
|
+
const match = RFC5424_REGEX.exec(raw.trim());
|
|
96
|
+
if (!match) {
|
|
97
|
+
// Attempt basic PRI extraction for BSD-style (RFC 3164) messages
|
|
98
|
+
// 嘗試對 BSD 風格 (RFC 3164) 訊息進行基本 PRI 擷取
|
|
99
|
+
const priMatch = /^<(\d{1,3})>(.*)$/s.exec(raw.trim());
|
|
100
|
+
if (priMatch) {
|
|
101
|
+
const pri = parseInt(priMatch[1] ?? '0', 10);
|
|
102
|
+
const facility = Math.floor(pri / 8);
|
|
103
|
+
const severityCode = pri % 8;
|
|
104
|
+
return {
|
|
105
|
+
facility,
|
|
106
|
+
severityCode,
|
|
107
|
+
version: 0,
|
|
108
|
+
timestamp: new Date().toISOString(),
|
|
109
|
+
hostname: '-',
|
|
110
|
+
appName: '-',
|
|
111
|
+
procId: '-',
|
|
112
|
+
msgId: '-',
|
|
113
|
+
structuredData: '-',
|
|
114
|
+
message: (priMatch[2] ?? '').trim(),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
// Completely unparseable - treat as info-level user message
|
|
118
|
+
// 完全無法解析 - 視為資訊等級的使用者訊息
|
|
119
|
+
return {
|
|
120
|
+
facility: 1,
|
|
121
|
+
severityCode: 6,
|
|
122
|
+
version: 0,
|
|
123
|
+
timestamp: new Date().toISOString(),
|
|
124
|
+
hostname: '-',
|
|
125
|
+
appName: '-',
|
|
126
|
+
procId: '-',
|
|
127
|
+
msgId: '-',
|
|
128
|
+
structuredData: '-',
|
|
129
|
+
message: raw.trim(),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const pri = parseInt(match[1] ?? '0', 10);
|
|
133
|
+
const facility = Math.floor(pri / 8);
|
|
134
|
+
const severityCode = pri % 8;
|
|
135
|
+
const m3 = match[3] ?? '';
|
|
136
|
+
const m4 = match[4] ?? '';
|
|
137
|
+
const m5 = match[5] ?? '';
|
|
138
|
+
const m6 = match[6] ?? '';
|
|
139
|
+
const m7 = match[7] ?? '';
|
|
140
|
+
const m8 = match[8] ?? '';
|
|
141
|
+
return {
|
|
142
|
+
facility,
|
|
143
|
+
severityCode,
|
|
144
|
+
version: parseInt(match[2] ?? '0', 10),
|
|
145
|
+
timestamp: m3 === '-' ? new Date().toISOString() : m3,
|
|
146
|
+
hostname: m4 === '-' ? '' : m4,
|
|
147
|
+
appName: m5 === '-' ? '' : m5,
|
|
148
|
+
procId: m6 === '-' ? '' : m6,
|
|
149
|
+
msgId: m7 === '-' ? '' : m7,
|
|
150
|
+
structuredData: m8 === '-' ? '' : m8,
|
|
151
|
+
message: (match[9] ?? '').trim(),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Convert a parsed syslog message to an AdapterAlert
|
|
156
|
+
* 將已解析的 syslog 訊息轉換為 AdapterAlert
|
|
157
|
+
*
|
|
158
|
+
* @param parsed - Parsed syslog message / 已解析的 syslog 訊息
|
|
159
|
+
* @param raw - Original raw message string / 原始訊息字串
|
|
160
|
+
* @returns AdapterAlert instance / AdapterAlert 實例
|
|
161
|
+
*/
|
|
162
|
+
function toAdapterAlert(parsed, raw) {
|
|
163
|
+
const facilityName = SYSLOG_FACILITY_NAMES[parsed.facility] ?? `facility-${parsed.facility}`;
|
|
164
|
+
const severityString = SYSLOG_SEVERITY_MAP[parsed.severityCode] ?? 'info';
|
|
165
|
+
return {
|
|
166
|
+
id: randomUUID(),
|
|
167
|
+
timestamp: parsed.timestamp,
|
|
168
|
+
severity: severityString,
|
|
169
|
+
title: `[${facilityName}] ${parsed.appName || 'syslog'}: ${parsed.msgId || 'message'}`,
|
|
170
|
+
description: parsed.message || raw,
|
|
171
|
+
source: 'syslog',
|
|
172
|
+
raw: {
|
|
173
|
+
parsed,
|
|
174
|
+
original: raw,
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Syslog receiver security adapter (RFC 5424)
|
|
180
|
+
* Syslog 接收器安全對接器 (RFC 5424)
|
|
181
|
+
*
|
|
182
|
+
* Listens for syslog messages on a UDP socket, parses RFC 5424 format,
|
|
183
|
+
* buffers parsed alerts, and provides both callback-based real-time
|
|
184
|
+
* notification and pull-based alert retrieval.
|
|
185
|
+
*
|
|
186
|
+
* 在 UDP socket 上監聽 syslog 訊息,解析 RFC 5424 格式,
|
|
187
|
+
* 緩衝已解析的告警,並提供基於回呼的即時通知和拉取式告警取得。
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```typescript
|
|
191
|
+
* const adapter = new SyslogAdapter({ enabled: true });
|
|
192
|
+
*
|
|
193
|
+
* // Set up real-time callback / 設定即時回呼
|
|
194
|
+
* adapter.onAlert((alert) => {
|
|
195
|
+
* console.log('New syslog alert:', alert);
|
|
196
|
+
* });
|
|
197
|
+
*
|
|
198
|
+
* // Start listening on port 514 / 開始在埠 514 上監聽
|
|
199
|
+
* await adapter.start(514);
|
|
200
|
+
*
|
|
201
|
+
* // Later: retrieve buffered alerts / 稍後:取得緩衝的告警
|
|
202
|
+
* const alerts = await adapter.getAlerts();
|
|
203
|
+
*
|
|
204
|
+
* // Stop listening / 停止監聽
|
|
205
|
+
* adapter.stop();
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
export class SyslogAdapter extends BaseAdapter {
|
|
209
|
+
/** @inheritdoc */
|
|
210
|
+
name = 'Syslog Receiver';
|
|
211
|
+
/** @inheritdoc */
|
|
212
|
+
type = 'syslog';
|
|
213
|
+
/**
|
|
214
|
+
* UDP socket for receiving syslog messages
|
|
215
|
+
* 用於接收 syslog 訊息的 UDP socket
|
|
216
|
+
*/
|
|
217
|
+
socket = null;
|
|
218
|
+
/**
|
|
219
|
+
* Buffer of alerts received since last getAlerts() call
|
|
220
|
+
* 自上次 getAlerts() 呼叫以來接收的告警緩衝區
|
|
221
|
+
*/
|
|
222
|
+
alertBuffer = [];
|
|
223
|
+
/**
|
|
224
|
+
* Real-time alert callback
|
|
225
|
+
* 即時告警回呼
|
|
226
|
+
*/
|
|
227
|
+
alertCallback = null;
|
|
228
|
+
/**
|
|
229
|
+
* Whether the socket is currently listening
|
|
230
|
+
* socket 是否正在監聽
|
|
231
|
+
*/
|
|
232
|
+
listening = false;
|
|
233
|
+
/**
|
|
234
|
+
* Port the socket is listening on
|
|
235
|
+
* socket 正在監聽的埠
|
|
236
|
+
*/
|
|
237
|
+
listenPort = 0;
|
|
238
|
+
/**
|
|
239
|
+
* Create a new SyslogAdapter instance
|
|
240
|
+
* 建立新的 SyslogAdapter 實例
|
|
241
|
+
*
|
|
242
|
+
* @param config - Adapter configuration / 對接器配置
|
|
243
|
+
*/
|
|
244
|
+
constructor(config = { enabled: true }) {
|
|
245
|
+
super('adapter-syslog', config);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Register a callback for real-time alert notification
|
|
249
|
+
* 註冊即時告警通知的回呼
|
|
250
|
+
*
|
|
251
|
+
* The callback is invoked for each parsed syslog message as it arrives.
|
|
252
|
+
* Only one callback can be registered at a time; setting a new one
|
|
253
|
+
* replaces the previous.
|
|
254
|
+
* 每收到一筆已解析的 syslog 訊息就會呼叫此回呼。
|
|
255
|
+
* 同一時間只能註冊一個回呼;設定新的會取代前一個。
|
|
256
|
+
*
|
|
257
|
+
* @param callback - Alert callback function / 告警回呼函式
|
|
258
|
+
*/
|
|
259
|
+
onAlert(callback) {
|
|
260
|
+
this.alertCallback = callback;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Check if the syslog receiver is available (i.e. listening)
|
|
264
|
+
* 檢查 syslog 接收器是否可用(即正在監聽)
|
|
265
|
+
*
|
|
266
|
+
* @returns True if the UDP socket is bound and listening / 若 UDP socket 已綁定且正在監聽則回傳 true
|
|
267
|
+
*/
|
|
268
|
+
async isAvailable() {
|
|
269
|
+
return this.listening;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Start the UDP syslog receiver
|
|
273
|
+
* 啟動 UDP syslog 接收器
|
|
274
|
+
*
|
|
275
|
+
* Binds a UDP4 socket to the specified port and begins listening
|
|
276
|
+
* for incoming syslog messages. Each message is parsed according to
|
|
277
|
+
* RFC 5424 and added to the alert buffer.
|
|
278
|
+
* 將 UDP4 socket 綁定到指定埠並開始監聽傳入的 syslog 訊息。
|
|
279
|
+
* 每筆訊息都會依 RFC 5424 解析並新增到告警緩衝區。
|
|
280
|
+
*
|
|
281
|
+
* @param port - UDP port to listen on (default: 514) / 要監聽的 UDP 埠(預設:514)
|
|
282
|
+
* @returns Promise that resolves when the socket is bound / socket 綁定後 resolve 的 Promise
|
|
283
|
+
*/
|
|
284
|
+
start(port = DEFAULT_SYSLOG_PORT) {
|
|
285
|
+
return new Promise((resolve, reject) => {
|
|
286
|
+
if (this.listening) {
|
|
287
|
+
this.logger.warn('Syslog receiver is already listening', { port: this.listenPort });
|
|
288
|
+
resolve();
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
this.socket = dgram.createSocket('udp4');
|
|
292
|
+
this.socket.on('message', (msg, rinfo) => {
|
|
293
|
+
try {
|
|
294
|
+
const raw = msg.toString('utf8');
|
|
295
|
+
const parsed = parseSyslogMessage(raw);
|
|
296
|
+
const alert = toAdapterAlert(parsed, raw);
|
|
297
|
+
// Add source IP to alert metadata / 新增來源 IP 到告警中繼資料
|
|
298
|
+
alert.raw['remoteAddress'] = rinfo.address;
|
|
299
|
+
alert.raw['remotePort'] = rinfo.port;
|
|
300
|
+
// Buffer the alert / 緩衝告警
|
|
301
|
+
this.alertBuffer.push(alert);
|
|
302
|
+
// Enforce buffer size limit / 強制緩衝區大小限制
|
|
303
|
+
if (this.alertBuffer.length > MAX_BUFFER_SIZE) {
|
|
304
|
+
const discarded = this.alertBuffer.length - MAX_BUFFER_SIZE;
|
|
305
|
+
this.alertBuffer = this.alertBuffer.slice(discarded);
|
|
306
|
+
this.logger.warn(`Alert buffer overflow, discarded ${discarded} oldest alerts`);
|
|
307
|
+
}
|
|
308
|
+
// Invoke real-time callback if registered / 若已註冊則呼叫即時回呼
|
|
309
|
+
if (this.alertCallback) {
|
|
310
|
+
this.alertCallback(alert);
|
|
311
|
+
}
|
|
312
|
+
this.logger.debug('Parsed syslog message', {
|
|
313
|
+
from: `${rinfo.address}:${rinfo.port}`,
|
|
314
|
+
facility: parsed.facility,
|
|
315
|
+
severity: parsed.severityCode,
|
|
316
|
+
hostname: parsed.hostname,
|
|
317
|
+
appName: parsed.appName,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
catch (err) {
|
|
321
|
+
this.logger.error('Failed to parse syslog message', {
|
|
322
|
+
from: `${rinfo.address}:${rinfo.port}`,
|
|
323
|
+
error: err instanceof Error ? err.message : String(err),
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
this.socket.on('error', (err) => {
|
|
328
|
+
this.logger.error('Syslog socket error', { error: err.message });
|
|
329
|
+
if (!this.listening) {
|
|
330
|
+
// Error during binding / 綁定期間的錯誤
|
|
331
|
+
reject(err);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
this.socket.on('close', () => {
|
|
335
|
+
this.listening = false;
|
|
336
|
+
this.listenPort = 0;
|
|
337
|
+
this.logger.info('Syslog socket closed');
|
|
338
|
+
});
|
|
339
|
+
this.socket.bind(port, () => {
|
|
340
|
+
this.listening = true;
|
|
341
|
+
this.listenPort = port;
|
|
342
|
+
const address = this.socket?.address();
|
|
343
|
+
this.logger.info('Syslog receiver started', {
|
|
344
|
+
port: address?.port ?? port,
|
|
345
|
+
address: address?.address ?? '0.0.0.0',
|
|
346
|
+
});
|
|
347
|
+
resolve();
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Stop the UDP syslog receiver
|
|
353
|
+
* 停止 UDP syslog 接收器
|
|
354
|
+
*
|
|
355
|
+
* Closes the UDP socket and stops listening for messages.
|
|
356
|
+
* The alert buffer is preserved and can still be read via getAlerts().
|
|
357
|
+
* 關閉 UDP socket 並停止監聽訊息。
|
|
358
|
+
* 告警緩衝區會被保留,仍可透過 getAlerts() 讀取。
|
|
359
|
+
*/
|
|
360
|
+
stop() {
|
|
361
|
+
if (this.socket) {
|
|
362
|
+
try {
|
|
363
|
+
this.socket.close();
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
// Socket may already be closed / Socket 可能已經關閉
|
|
367
|
+
}
|
|
368
|
+
this.socket = null;
|
|
369
|
+
}
|
|
370
|
+
this.listening = false;
|
|
371
|
+
this.listenPort = 0;
|
|
372
|
+
this.logger.info('Syslog receiver stopped');
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Retrieve buffered alerts and clear the buffer
|
|
376
|
+
* 取得緩衝的告警並清除緩衝區
|
|
377
|
+
*
|
|
378
|
+
* Returns all alerts accumulated since the last call to getAlerts().
|
|
379
|
+
* Optionally filters by a cutoff date. The buffer is cleared after
|
|
380
|
+
* retrieval to prevent duplicate processing.
|
|
381
|
+
* 回傳自上次呼叫 getAlerts() 以來累積的所有告警。
|
|
382
|
+
* 可選依截止日期過濾。取得後會清除緩衝區以防止重複處理。
|
|
383
|
+
*
|
|
384
|
+
* @param since - Optional cutoff date / 可選截止日期
|
|
385
|
+
* @returns Array of buffered adapter alerts / 緩衝的對接器告警陣列
|
|
386
|
+
*/
|
|
387
|
+
async getAlerts(since) {
|
|
388
|
+
// Drain the buffer / 排空緩衝區
|
|
389
|
+
const buffered = this.alertBuffer.splice(0, this.alertBuffer.length);
|
|
390
|
+
if (!since) {
|
|
391
|
+
this.logger.debug(`Returning ${buffered.length} buffered syslog alerts`);
|
|
392
|
+
return buffered;
|
|
393
|
+
}
|
|
394
|
+
// Filter by cutoff date / 依截止日期過濾
|
|
395
|
+
const filtered = buffered.filter((alert) => {
|
|
396
|
+
const alertTime = new Date(alert.timestamp);
|
|
397
|
+
return alertTime >= since;
|
|
398
|
+
});
|
|
399
|
+
this.logger.debug(`Returning ${filtered.length} syslog alerts (${buffered.length} total buffered)`, {
|
|
400
|
+
since: since.toISOString(),
|
|
401
|
+
});
|
|
402
|
+
return filtered;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Get the current buffer size (number of pending alerts)
|
|
406
|
+
* 取得目前緩衝區大小(待處理告警數量)
|
|
407
|
+
*
|
|
408
|
+
* @returns Number of alerts in the buffer / 緩衝區中的告警數量
|
|
409
|
+
*/
|
|
410
|
+
getBufferSize() {
|
|
411
|
+
return this.alertBuffer.length;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Check if the receiver is currently listening
|
|
415
|
+
* 檢查接收器是否正在監聽
|
|
416
|
+
*
|
|
417
|
+
* @returns True if listening / 若正在監聽則回傳 true
|
|
418
|
+
*/
|
|
419
|
+
isListening() {
|
|
420
|
+
return this.listening;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Get the port the receiver is listening on
|
|
424
|
+
* 取得接收器正在監聽的埠
|
|
425
|
+
*
|
|
426
|
+
* @returns Port number, or 0 if not listening / 埠號,若未監聽則為 0
|
|
427
|
+
*/
|
|
428
|
+
getPort() {
|
|
429
|
+
return this.listenPort;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
//# sourceMappingURL=syslog-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syslog-adapter.js","sourceRoot":"","sources":["../../src/adapters/syslog-adapter.ts"],"names":[],"mappings":"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"}
|