@panguard-ai/threat-cloud 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/backup.d.ts +40 -0
  2. package/dist/backup.d.ts.map +1 -0
  3. package/dist/backup.js +123 -0
  4. package/dist/backup.js.map +1 -0
  5. package/dist/cli.js +24 -64
  6. package/dist/cli.js.map +1 -1
  7. package/dist/database.d.ts +58 -36
  8. package/dist/database.d.ts.map +1 -1
  9. package/dist/database.js +279 -328
  10. package/dist/database.js.map +1 -1
  11. package/dist/index.d.ts +4 -10
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +2 -9
  14. package/dist/index.js.map +1 -1
  15. package/dist/llm-reviewer.d.ts +47 -0
  16. package/dist/llm-reviewer.d.ts.map +1 -0
  17. package/dist/llm-reviewer.js +205 -0
  18. package/dist/llm-reviewer.js.map +1 -0
  19. package/dist/server.d.ts +43 -64
  20. package/dist/server.d.ts.map +1 -1
  21. package/dist/server.js +316 -647
  22. package/dist/server.js.map +1 -1
  23. package/dist/types.d.ts +36 -301
  24. package/dist/types.d.ts.map +1 -1
  25. package/package.json +20 -18
  26. package/LICENSE +0 -21
  27. package/dist/audit-logger.d.ts +0 -46
  28. package/dist/audit-logger.d.ts.map +0 -1
  29. package/dist/audit-logger.js +0 -105
  30. package/dist/audit-logger.js.map +0 -1
  31. package/dist/correlation-engine.d.ts +0 -41
  32. package/dist/correlation-engine.d.ts.map +0 -1
  33. package/dist/correlation-engine.js +0 -313
  34. package/dist/correlation-engine.js.map +0 -1
  35. package/dist/feed-distributor.d.ts +0 -36
  36. package/dist/feed-distributor.d.ts.map +0 -1
  37. package/dist/feed-distributor.js +0 -125
  38. package/dist/feed-distributor.js.map +0 -1
  39. package/dist/ioc-store.d.ts +0 -83
  40. package/dist/ioc-store.d.ts.map +0 -1
  41. package/dist/ioc-store.js +0 -278
  42. package/dist/ioc-store.js.map +0 -1
  43. package/dist/query-handlers.d.ts +0 -40
  44. package/dist/query-handlers.d.ts.map +0 -1
  45. package/dist/query-handlers.js +0 -211
  46. package/dist/query-handlers.js.map +0 -1
  47. package/dist/reputation-engine.d.ts +0 -44
  48. package/dist/reputation-engine.d.ts.map +0 -1
  49. package/dist/reputation-engine.js +0 -169
  50. package/dist/reputation-engine.js.map +0 -1
  51. package/dist/rule-generator.d.ts +0 -47
  52. package/dist/rule-generator.d.ts.map +0 -1
  53. package/dist/rule-generator.js +0 -238
  54. package/dist/rule-generator.js.map +0 -1
  55. package/dist/scheduler.d.ts +0 -52
  56. package/dist/scheduler.d.ts.map +0 -1
  57. package/dist/scheduler.js +0 -143
  58. package/dist/scheduler.js.map +0 -1
  59. package/dist/sighting-store.d.ts +0 -61
  60. package/dist/sighting-store.d.ts.map +0 -1
  61. package/dist/sighting-store.js +0 -191
  62. package/dist/sighting-store.js.map +0 -1
@@ -1,105 +0,0 @@
1
- /**
2
- * Audit Logger — provenance tracking for all write operations
3
- * 稽核日誌 — 所有寫入操作的溯源追蹤
4
- *
5
- * Every mutation (IoC create/update, sighting, threat upload, rule publish)
6
- * is logged with actor, IP, timestamp, and action details.
7
- *
8
- * @module @panguard-ai/threat-cloud/audit-logger
9
- */
10
- import { createHash } from 'node:crypto';
11
- function rowToEntry(row) {
12
- return {
13
- id: row.id,
14
- action: row.action,
15
- entityType: row.entity_type,
16
- entityId: row.entity_id,
17
- actorHash: row.actor_hash,
18
- ipAddress: row.ip_address,
19
- details: row.details,
20
- createdAt: row.created_at,
21
- };
22
- }
23
- export class AuditLogger {
24
- db;
25
- insertStmt;
26
- constructor(db) {
27
- this.db = db;
28
- this.insertStmt = this.db.prepare(`INSERT INTO audit_log (action, entity_type, entity_id, actor_hash, ip_address, details)
29
- VALUES (?, ?, ?, ?, ?, ?)`);
30
- }
31
- /**
32
- * Log an auditable action.
33
- * 記錄可稽核的操作
34
- */
35
- log(action, entityType, entityId, context = {}) {
36
- this.insertStmt.run(action, entityType, entityId, context.actorHash ?? '', context.ipAddress ?? '', JSON.stringify(context.details ?? {}));
37
- }
38
- /**
39
- * Hash an API key for audit logging (never log raw keys).
40
- * 將 API key 雜湊化用於稽核日誌(永遠不記錄原始 key)
41
- */
42
- static hashApiKey(apiKey) {
43
- if (!apiKey)
44
- return '';
45
- return createHash('sha256').update(apiKey).digest('hex').slice(0, 16);
46
- }
47
- /**
48
- * Query audit log with filters.
49
- * 查詢稽核日誌
50
- */
51
- query(params) {
52
- const conditions = [];
53
- const values = [];
54
- if (params.action) {
55
- conditions.push('action = ?');
56
- values.push(params.action);
57
- }
58
- if (params.entityType) {
59
- conditions.push('entity_type = ?');
60
- values.push(params.entityType);
61
- }
62
- if (params.entityId) {
63
- conditions.push('entity_id = ?');
64
- values.push(params.entityId);
65
- }
66
- if (params.since) {
67
- conditions.push('created_at > ?');
68
- values.push(params.since);
69
- }
70
- const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
71
- const safeLimit = Math.min(Math.max(1, params.limit ?? 50), 500);
72
- const total = this.db.prepare(`SELECT COUNT(*) as count FROM audit_log ${where}`).get(...values).count;
73
- const rows = this.db
74
- .prepare(`SELECT * FROM audit_log ${where} ORDER BY created_at DESC LIMIT ?`)
75
- .all(...values, safeLimit);
76
- return {
77
- items: rows.map(rowToEntry),
78
- total,
79
- page: 1,
80
- limit: safeLimit,
81
- hasMore: safeLimit < total,
82
- };
83
- }
84
- /**
85
- * Get audit trail for a specific entity.
86
- * 取得特定實體的稽核記錄
87
- */
88
- getEntityTrail(entityType, entityId) {
89
- const rows = this.db
90
- .prepare(`SELECT * FROM audit_log
91
- WHERE entity_type = ? AND entity_id = ?
92
- ORDER BY created_at ASC`)
93
- .all(entityType, entityId);
94
- return rows.map(rowToEntry);
95
- }
96
- /**
97
- * Purge old audit entries beyond retention.
98
- * 清除超齡的稽核記錄
99
- */
100
- purgeOldEntries(olderThan) {
101
- const result = this.db.prepare('DELETE FROM audit_log WHERE created_at < ?').run(olderThan);
102
- return result.changes;
103
- }
104
- }
105
- //# sourceMappingURL=audit-logger.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"audit-logger.js","sourceRoot":"","sources":["../src/audit-logger.ts"],"names":[],"mappings":"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"}
@@ -1,41 +0,0 @@
1
- /**
2
- * Threat Correlation Engine
3
- * 威脅關聯引擎
4
- *
5
- * Groups related threat events into campaigns based on:
6
- * 1. Same source IP within a time window (IP cluster)
7
- * 2. Same attack pattern across multiple IPs (pattern cluster)
8
- *
9
- * @module @panguard-ai/threat-cloud/correlation-engine
10
- */
11
- import type Database from 'better-sqlite3';
12
- import type { CorrelationConfig, Campaign, CampaignScanResult, CampaignStats, PaginationParams, PaginatedResponse, EnrichedThreatEvent } from './types.js';
13
- export declare class CorrelationEngine {
14
- private readonly db;
15
- private readonly config;
16
- constructor(db: Database.Database, config?: Partial<CorrelationConfig>);
17
- /** Create campaigns table if not exists / 建立 campaigns 表 */
18
- private ensureTable;
19
- /**
20
- * Scan for new campaigns in uncorrelated events.
21
- * 掃描未關聯的事件,建立新的攻擊活動
22
- */
23
- scanForCampaigns(): CampaignScanResult;
24
- /** Get campaign by ID / 取得 campaign */
25
- getCampaign(campaignId: string): Campaign | null;
26
- /** List campaigns / 列表 campaigns */
27
- listCampaigns(pagination: PaginationParams, status?: string): PaginatedResponse<Campaign>;
28
- /** Get campaign events / 取得 campaign 事件 */
29
- getCampaignEvents(campaignId: string): EnrichedThreatEvent[];
30
- /** Get campaign statistics / 取得攻擊活動統計 */
31
- getCampaignStats(): CampaignStats;
32
- /** Cluster events by time window / 依時間窗口聚類事件 */
33
- private clusterByTimeWindow;
34
- /** Generate deterministic campaign ID / 產生確定性 campaign ID */
35
- private generateCampaignId;
36
- /** Pick the maximum severity / 選擇最高嚴重度 */
37
- private pickMaxSeverity;
38
- /** Upsert campaign / 新增或更新攻擊活動 */
39
- private upsertCampaign;
40
- }
41
- //# sourceMappingURL=correlation-engine.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"correlation-engine.d.ts","sourceRoot":"","sources":["../src/correlation-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EACV,iBAAiB,EACjB,QAAQ,EACR,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAgDpB,qBAAa,iBAAiB;IAI1B,OAAO,CAAC,QAAQ,CAAC,EAAE;IAHrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;gBAGxB,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACtC,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC;IAMrC,4DAA4D;IAC5D,OAAO,CAAC,WAAW;IAuBnB;;;OAGG;IACH,gBAAgB,IAAI,kBAAkB;IAiJtC,uCAAuC;IACvC,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAOhD,oCAAoC;IACpC,aAAa,CAAC,UAAU,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAAC,QAAQ,CAAC;IAyBzF,2CAA2C;IAC3C,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,mBAAmB,EAAE;IA2B5D,yCAAyC;IACzC,gBAAgB,IAAI,aAAa;IAmCjC,gDAAgD;IAChD,OAAO,CAAC,mBAAmB;IAoB3B,6DAA6D;IAC7D,OAAO,CAAC,kBAAkB;IAO1B,0CAA0C;IAC1C,OAAO,CAAC,eAAe;IAQvB,kCAAkC;IAClC,OAAO,CAAC,cAAc;CA+BvB"}
@@ -1,313 +0,0 @@
1
- /**
2
- * Threat Correlation Engine
3
- * 威脅關聯引擎
4
- *
5
- * Groups related threat events into campaigns based on:
6
- * 1. Same source IP within a time window (IP cluster)
7
- * 2. Same attack pattern across multiple IPs (pattern cluster)
8
- *
9
- * @module @panguard-ai/threat-cloud/correlation-engine
10
- */
11
- import { createHash } from 'node:crypto';
12
- /** Default config / 預設配置 */
13
- const DEFAULT_CONFIG = {
14
- timeWindowMinutes: 60,
15
- minEventsForCampaign: 3,
16
- minIPsForPatternCampaign: 5,
17
- scanWindowHours: 24,
18
- };
19
- /** Convert DB row to Campaign / 轉換 DB 列 */
20
- function rowToCampaign(row) {
21
- return {
22
- campaignId: row.campaign_id,
23
- name: row.name,
24
- campaignType: row.campaign_type,
25
- firstSeen: row.first_seen,
26
- lastSeen: row.last_seen,
27
- eventCount: row.event_count,
28
- uniqueIPs: row.unique_ips,
29
- attackTypes: JSON.parse(row.attack_types),
30
- mitreTechniques: JSON.parse(row.mitre_techniques),
31
- regions: JSON.parse(row.regions),
32
- severity: row.severity,
33
- status: row.status,
34
- createdAt: row.created_at,
35
- updatedAt: row.updated_at,
36
- };
37
- }
38
- export class CorrelationEngine {
39
- db;
40
- config;
41
- constructor(db, config) {
42
- this.db = db;
43
- this.config = { ...DEFAULT_CONFIG, ...config };
44
- this.ensureTable();
45
- }
46
- /** Create campaigns table if not exists / 建立 campaigns 表 */
47
- ensureTable() {
48
- this.db.exec(`
49
- CREATE TABLE IF NOT EXISTS campaigns (
50
- campaign_id TEXT PRIMARY KEY,
51
- name TEXT NOT NULL,
52
- campaign_type TEXT NOT NULL CHECK(campaign_type IN ('ip_cluster','pattern_cluster','manual')),
53
- first_seen TEXT NOT NULL,
54
- last_seen TEXT NOT NULL,
55
- event_count INTEGER NOT NULL DEFAULT 0,
56
- unique_ips INTEGER NOT NULL DEFAULT 0,
57
- attack_types TEXT NOT NULL DEFAULT '[]',
58
- mitre_techniques TEXT NOT NULL DEFAULT '[]',
59
- regions TEXT NOT NULL DEFAULT '[]',
60
- severity TEXT NOT NULL DEFAULT 'medium',
61
- status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','resolved','false_positive')),
62
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
63
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
64
- );
65
- CREATE INDEX IF NOT EXISTS idx_campaigns_status ON campaigns(status);
66
- CREATE INDEX IF NOT EXISTS idx_campaigns_last_seen ON campaigns(last_seen);
67
- `);
68
- }
69
- /**
70
- * Scan for new campaigns in uncorrelated events.
71
- * 掃描未關聯的事件,建立新的攻擊活動
72
- */
73
- scanForCampaigns() {
74
- const startTime = Date.now();
75
- let newCampaigns = 0;
76
- const updatedCampaigns = 0;
77
- let eventsCorrelated = 0;
78
- const sinceDate = new Date(Date.now() - this.config.scanWindowHours * 60 * 60 * 1000).toISOString();
79
- // Fetch uncorrelated events within scan window
80
- const events = this.db
81
- .prepare(`SELECT id, attack_source_ip, attack_type, mitre_techniques, timestamp, region, severity
82
- FROM enriched_threats
83
- WHERE campaign_id IS NULL AND received_at > ?
84
- ORDER BY timestamp ASC`)
85
- .all(sinceDate);
86
- if (events.length === 0) {
87
- return {
88
- newCampaigns: 0,
89
- updatedCampaigns: 0,
90
- eventsCorrelated: 0,
91
- duration: Date.now() - startTime,
92
- };
93
- }
94
- // --- IP Cluster detection ---
95
- const byIP = new Map();
96
- for (const e of events) {
97
- const list = byIP.get(e.attack_source_ip) ?? [];
98
- list.push(e);
99
- byIP.set(e.attack_source_ip, list);
100
- }
101
- const assignedIds = new Set();
102
- const updateCampaignId = this.db.prepare('UPDATE enriched_threats SET campaign_id = ? WHERE id = ?');
103
- this.db.transaction(() => {
104
- for (const [ip, ipEvents] of byIP) {
105
- if (ipEvents.length < this.config.minEventsForCampaign)
106
- continue;
107
- // Check time window clustering
108
- const clustered = this.clusterByTimeWindow(ipEvents);
109
- for (const cluster of clustered) {
110
- if (cluster.length < this.config.minEventsForCampaign)
111
- continue;
112
- const campaignId = this.generateCampaignId(cluster.map((e) => e.id));
113
- const attackTypes = [...new Set(cluster.map((e) => e.attack_type))];
114
- const allTechniques = cluster.flatMap((e) => JSON.parse(e.mitre_techniques));
115
- const techniques = [...new Set(allTechniques)];
116
- const regions = [...new Set(cluster.map((e) => e.region))];
117
- const timestamps = cluster.map((e) => e.timestamp).sort();
118
- const maxSeverity = this.pickMaxSeverity(cluster.map((e) => e.severity));
119
- this.upsertCampaign({
120
- campaignId,
121
- name: `IP ${ip}: ${attackTypes.join(', ')}`,
122
- campaignType: 'ip_cluster',
123
- firstSeen: timestamps[0],
124
- lastSeen: timestamps[timestamps.length - 1],
125
- eventCount: cluster.length,
126
- uniqueIPs: 1,
127
- attackTypes,
128
- mitreTechniques: techniques,
129
- regions,
130
- severity: maxSeverity,
131
- });
132
- for (const e of cluster) {
133
- updateCampaignId.run(campaignId, e.id);
134
- assignedIds.add(e.id);
135
- }
136
- newCampaigns++;
137
- eventsCorrelated += cluster.length;
138
- }
139
- }
140
- // --- Pattern Cluster detection ---
141
- const unassigned = events.filter((e) => !assignedIds.has(e.id));
142
- const byPattern = new Map();
143
- for (const e of unassigned) {
144
- const techniques = JSON.parse(e.mitre_techniques).sort().join(',');
145
- const key = `${e.attack_type}|${techniques}`;
146
- const list = byPattern.get(key) ?? [];
147
- list.push(e);
148
- byPattern.set(key, list);
149
- }
150
- for (const [pattern, patternEvents] of byPattern) {
151
- const distinctIPs = new Set(patternEvents.map((e) => e.attack_source_ip));
152
- if (distinctIPs.size < this.config.minIPsForPatternCampaign)
153
- continue;
154
- const campaignId = this.generateCampaignId(patternEvents.map((e) => e.id));
155
- const [attackType] = pattern.split('|');
156
- const allTechniques = patternEvents.flatMap((e) => JSON.parse(e.mitre_techniques));
157
- const techniques = [...new Set(allTechniques)];
158
- const regions = [...new Set(patternEvents.map((e) => e.region))];
159
- const timestamps = patternEvents.map((e) => e.timestamp).sort();
160
- const maxSeverity = this.pickMaxSeverity(patternEvents.map((e) => e.severity));
161
- this.upsertCampaign({
162
- campaignId,
163
- name: `Pattern: ${attackType} from ${distinctIPs.size} IPs`,
164
- campaignType: 'pattern_cluster',
165
- firstSeen: timestamps[0],
166
- lastSeen: timestamps[timestamps.length - 1],
167
- eventCount: patternEvents.length,
168
- uniqueIPs: distinctIPs.size,
169
- attackTypes: [attackType],
170
- mitreTechniques: techniques,
171
- regions,
172
- severity: maxSeverity,
173
- });
174
- for (const e of patternEvents) {
175
- updateCampaignId.run(campaignId, e.id);
176
- }
177
- newCampaigns++;
178
- eventsCorrelated += patternEvents.length;
179
- }
180
- })();
181
- return {
182
- newCampaigns,
183
- updatedCampaigns,
184
- eventsCorrelated,
185
- duration: Date.now() - startTime,
186
- };
187
- }
188
- /** Get campaign by ID / 取得 campaign */
189
- getCampaign(campaignId) {
190
- const row = this.db.prepare('SELECT * FROM campaigns WHERE campaign_id = ?').get(campaignId);
191
- return row ? rowToCampaign(row) : null;
192
- }
193
- /** List campaigns / 列表 campaigns */
194
- listCampaigns(pagination, status) {
195
- const where = status ? 'WHERE status = ?' : '';
196
- const params = status ? [status] : [];
197
- const safeLimit = Math.min(Math.max(1, pagination.limit), 1000);
198
- const offset = (Math.max(1, pagination.page) - 1) * safeLimit;
199
- const total = this.db.prepare(`SELECT COUNT(*) as count FROM campaigns ${where}`).get(...params).count;
200
- const rows = this.db
201
- .prepare(`SELECT * FROM campaigns ${where} ORDER BY last_seen DESC LIMIT ? OFFSET ?`)
202
- .all(...params, safeLimit, offset);
203
- return {
204
- items: rows.map(rowToCampaign),
205
- total,
206
- page: pagination.page,
207
- limit: safeLimit,
208
- hasMore: offset + safeLimit < total,
209
- };
210
- }
211
- /** Get campaign events / 取得 campaign 事件 */
212
- getCampaignEvents(campaignId) {
213
- const rows = this.db
214
- .prepare('SELECT * FROM enriched_threats WHERE campaign_id = ? ORDER BY timestamp ASC')
215
- .all(campaignId);
216
- return rows.map((r) => ({
217
- id: r['id'],
218
- sourceType: r['source_type'],
219
- attackSourceIP: r['attack_source_ip'],
220
- attackType: r['attack_type'],
221
- mitreTechniques: JSON.parse(r['mitre_techniques']),
222
- sigmaRuleMatched: r['sigma_rule_matched'],
223
- timestamp: r['timestamp'],
224
- industry: r['industry'],
225
- region: r['region'],
226
- confidence: r['confidence'],
227
- severity: r['severity'],
228
- serviceType: r['service_type'],
229
- skillLevel: r['skill_level'],
230
- intent: r['intent'],
231
- tools: r['tools'] ? JSON.parse(r['tools']) : undefined,
232
- eventHash: r['event_hash'],
233
- receivedAt: r['received_at'],
234
- campaignId: r['campaign_id'],
235
- }));
236
- }
237
- /** Get campaign statistics / 取得攻擊活動統計 */
238
- getCampaignStats() {
239
- const total = this.db.prepare('SELECT COUNT(*) as count FROM campaigns').get().count;
240
- const active = this.db.prepare("SELECT COUNT(*) as count FROM campaigns WHERE status = 'active'").get().count;
241
- const correlated = this.db
242
- .prepare('SELECT COUNT(*) as count FROM enriched_threats WHERE campaign_id IS NOT NULL')
243
- .get().count;
244
- const topTypes = this.db
245
- .prepare(`SELECT attack_type as type, COUNT(*) as count
246
- FROM enriched_threats WHERE campaign_id IS NOT NULL
247
- GROUP BY attack_type ORDER BY count DESC LIMIT 10`)
248
- .all();
249
- return {
250
- totalCampaigns: total,
251
- activeCampaigns: active,
252
- totalCorrelatedEvents: correlated,
253
- topAttackTypes: topTypes,
254
- };
255
- }
256
- // -------------------------------------------------------------------------
257
- // Private helpers / 私有輔助方法
258
- // -------------------------------------------------------------------------
259
- /** Cluster events by time window / 依時間窗口聚類事件 */
260
- clusterByTimeWindow(events) {
261
- if (events.length === 0)
262
- return [];
263
- const sorted = [...events].sort((a, b) => a.timestamp.localeCompare(b.timestamp));
264
- const windowMs = this.config.timeWindowMinutes * 60 * 1000;
265
- const clusters = [[sorted[0]]];
266
- for (let i = 1; i < sorted.length; i++) {
267
- const current = new Date(sorted[i].timestamp).getTime();
268
- const clusterStart = new Date(clusters[clusters.length - 1][0].timestamp).getTime();
269
- if (current - clusterStart <= windowMs) {
270
- clusters[clusters.length - 1].push(sorted[i]);
271
- }
272
- else {
273
- clusters.push([sorted[i]]);
274
- }
275
- }
276
- return clusters;
277
- }
278
- /** Generate deterministic campaign ID / 產生確定性 campaign ID */
279
- generateCampaignId(eventIds) {
280
- const sorted = [...eventIds].sort((a, b) => a - b);
281
- const hash = createHash('sha256').update(sorted.join(',')).digest('hex').slice(0, 8);
282
- const date = new Date().toISOString().slice(0, 10).replace(/-/g, '');
283
- return `C-${date}-${hash}`;
284
- }
285
- /** Pick the maximum severity / 選擇最高嚴重度 */
286
- pickMaxSeverity(severities) {
287
- const order = ['critical', 'high', 'medium', 'low'];
288
- for (const s of order) {
289
- if (severities.includes(s))
290
- return s;
291
- }
292
- return 'medium';
293
- }
294
- /** Upsert campaign / 新增或更新攻擊活動 */
295
- upsertCampaign(c) {
296
- this.db
297
- .prepare(`INSERT INTO campaigns
298
- (campaign_id, name, campaign_type, first_seen, last_seen, event_count, unique_ips,
299
- attack_types, mitre_techniques, regions, severity)
300
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
301
- ON CONFLICT(campaign_id) DO UPDATE SET
302
- last_seen = excluded.last_seen,
303
- event_count = excluded.event_count,
304
- unique_ips = excluded.unique_ips,
305
- attack_types = excluded.attack_types,
306
- mitre_techniques = excluded.mitre_techniques,
307
- regions = excluded.regions,
308
- severity = excluded.severity,
309
- updated_at = datetime('now')`)
310
- .run(c.campaignId, c.name, c.campaignType, c.firstSeen, c.lastSeen, c.eventCount, c.uniqueIPs, JSON.stringify(c.attackTypes), JSON.stringify(c.mitreTechniques), JSON.stringify(c.regions), c.severity);
311
- }
312
- }
313
- //# sourceMappingURL=correlation-engine.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"correlation-engine.js","sourceRoot":"","sources":["../src/correlation-engine.ts"],"names":[],"mappings":"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"}
@@ -1,36 +0,0 @@
1
- /**
2
- * Feed Distributor
3
- * 情報分發模組
4
- *
5
- * Generates blocklists, IoC feeds, and agent update packages.
6
- *
7
- * @module @panguard-ai/threat-cloud/feed-distributor
8
- */
9
- import type Database from 'better-sqlite3';
10
- import type { IoCFeedResponse, AgentUpdatePackage } from './types.js';
11
- export declare class FeedDistributor {
12
- private readonly db;
13
- constructor(db: Database.Database);
14
- /**
15
- * Generate IP blocklist as plain text (one IP per line).
16
- * Only includes redistributable IoCs to comply with feed licenses.
17
- * 產生 IP 封鎖清單(僅包含可轉散佈的 IoC,遵守授權)
18
- */
19
- getIPBlocklist(minReputation?: number): string;
20
- /**
21
- * Generate domain blocklist as plain text.
22
- * 產生 Domain 封鎖清單
23
- */
24
- getDomainBlocklist(minReputation?: number): string;
25
- /**
26
- * Generate JSON IoC feed.
27
- * 產生 JSON IoC feed
28
- */
29
- getIoCFeed(minReputation?: number, limit?: number, since?: string): IoCFeedResponse;
30
- /**
31
- * Generate agent update package (rules + IoCs since a given timestamp).
32
- * 產生 Agent 更新包
33
- */
34
- getAgentUpdate(since?: string): AgentUpdatePackage;
35
- }
36
- //# sourceMappingURL=feed-distributor.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"feed-distributor.d.ts","sourceRoot":"","sources":["../src/feed-distributor.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAEV,eAAe,EACf,kBAAkB,EAEnB,MAAM,YAAY,CAAC;AAEpB,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,QAAQ,CAAC,QAAQ;IAElD;;;;OAIG;IACH,cAAc,CAAC,aAAa,GAAE,MAAW,GAAG,MAAM;IAgBlD;;;OAGG;IACH,kBAAkB,CAAC,aAAa,GAAE,MAAW,GAAG,MAAM;IAgBtD;;;OAGG;IACH,UAAU,CAAC,aAAa,GAAE,MAAW,EAAE,KAAK,GAAE,MAAa,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,eAAe;IA+C7F;;;OAGG;IACH,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,kBAAkB;CA2DnD"}
@@ -1,125 +0,0 @@
1
- /**
2
- * Feed Distributor
3
- * 情報分發模組
4
- *
5
- * Generates blocklists, IoC feeds, and agent update packages.
6
- *
7
- * @module @panguard-ai/threat-cloud/feed-distributor
8
- */
9
- export class FeedDistributor {
10
- db;
11
- constructor(db) {
12
- this.db = db;
13
- }
14
- /**
15
- * Generate IP blocklist as plain text (one IP per line).
16
- * Only includes redistributable IoCs to comply with feed licenses.
17
- * 產生 IP 封鎖清單(僅包含可轉散佈的 IoC,遵守授權)
18
- */
19
- getIPBlocklist(minReputation = 70) {
20
- const rows = this.db
21
- .prepare(`SELECT normalized_value
22
- FROM iocs
23
- WHERE type = 'ip' AND status = 'active' AND reputation_score >= ?
24
- AND (json_extract(metadata, '$.redistributable') != 'false'
25
- OR json_extract(metadata, '$.redistributable') IS NULL
26
- OR source NOT LIKE 'feed:%')
27
- ORDER BY reputation_score DESC`)
28
- .all(minReputation);
29
- return rows.map((r) => r.normalized_value).join('\n');
30
- }
31
- /**
32
- * Generate domain blocklist as plain text.
33
- * 產生 Domain 封鎖清單
34
- */
35
- getDomainBlocklist(minReputation = 70) {
36
- const rows = this.db
37
- .prepare(`SELECT normalized_value
38
- FROM iocs
39
- WHERE type = 'domain' AND status = 'active' AND reputation_score >= ?
40
- AND (json_extract(metadata, '$.redistributable') != 'false'
41
- OR json_extract(metadata, '$.redistributable') IS NULL
42
- OR source NOT LIKE 'feed:%')
43
- ORDER BY reputation_score DESC`)
44
- .all(minReputation);
45
- return rows.map((r) => r.normalized_value).join('\n');
46
- }
47
- /**
48
- * Generate JSON IoC feed.
49
- * 產生 JSON IoC feed
50
- */
51
- getIoCFeed(minReputation = 50, limit = 1000, since) {
52
- const conditions = ["status = 'active'", 'reputation_score >= ?'];
53
- const params = [minReputation];
54
- if (since) {
55
- conditions.push('last_seen > ?');
56
- params.push(since);
57
- }
58
- const safeLimit = Math.min(Math.max(1, limit), 10000);
59
- const where = conditions.join(' AND ');
60
- const rows = this.db
61
- .prepare(`SELECT type, normalized_value, threat_type, reputation_score, confidence, last_seen, tags
62
- FROM iocs
63
- WHERE ${where}
64
- ORDER BY reputation_score DESC
65
- LIMIT ?`)
66
- .all(...params, safeLimit);
67
- const entries = rows.map((r) => ({
68
- type: r.type,
69
- value: r.normalized_value,
70
- threatType: r.threat_type,
71
- reputation: r.reputation_score,
72
- confidence: r.confidence,
73
- lastSeen: r.last_seen,
74
- tags: JSON.parse(r.tags),
75
- }));
76
- return {
77
- generatedAt: new Date().toISOString(),
78
- totalEntries: entries.length,
79
- entries,
80
- };
81
- }
82
- /**
83
- * Generate agent update package (rules + IoCs since a given timestamp).
84
- * 產生 Agent 更新包
85
- */
86
- getAgentUpdate(since) {
87
- const sinceDate = since ?? new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
88
- // Fetch new rules
89
- const rules = this.db
90
- .prepare(`SELECT rule_id as ruleId, rule_content as ruleContent, published_at as publishedAt, source
91
- FROM rules
92
- WHERE published_at > ?
93
- ORDER BY published_at ASC`)
94
- .all(sinceDate);
95
- // Fetch high-reputation IoCs updated since
96
- const iocRows = this.db
97
- .prepare(`SELECT type, normalized_value, threat_type, reputation_score, confidence, last_seen, tags
98
- FROM iocs
99
- WHERE status = 'active' AND reputation_score >= 60 AND updated_at > ?
100
- ORDER BY reputation_score DESC
101
- LIMIT 500`)
102
- .all(sinceDate);
103
- const iocs = iocRows.map((r) => ({
104
- type: r.type,
105
- value: r.normalized_value,
106
- threatType: r.threat_type,
107
- reputation: r.reputation_score,
108
- confidence: r.confidence,
109
- lastSeen: r.last_seen,
110
- tags: JSON.parse(r.tags),
111
- }));
112
- const totalActiveIoCs = this.db.prepare("SELECT COUNT(*) as count FROM iocs WHERE status = 'active'").get().count;
113
- return {
114
- generatedAt: new Date().toISOString(),
115
- rules,
116
- iocs,
117
- stats: {
118
- newRules: rules.length,
119
- newIoCs: iocs.length,
120
- totalActiveIoCs,
121
- },
122
- };
123
- }
124
- }
125
- //# sourceMappingURL=feed-distributor.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"feed-distributor.js","sourceRoot":"","sources":["../src/feed-distributor.ts"],"names":[],"mappings":"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"}