@panguard-ai/threat-cloud 0.1.1 → 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.
- package/dist/backup.d.ts +40 -0
- package/dist/backup.d.ts.map +1 -0
- package/dist/backup.js +123 -0
- package/dist/backup.js.map +1 -0
- package/dist/cli.js +24 -64
- package/dist/cli.js.map +1 -1
- package/dist/database.d.ts +58 -36
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +279 -328
- package/dist/database.js.map +1 -1
- package/dist/index.d.ts +4 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -9
- package/dist/index.js.map +1 -1
- package/dist/llm-reviewer.d.ts +47 -0
- package/dist/llm-reviewer.d.ts.map +1 -0
- package/dist/llm-reviewer.js +205 -0
- package/dist/llm-reviewer.js.map +1 -0
- package/dist/server.d.ts +43 -64
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +316 -647
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +36 -301
- package/dist/types.d.ts.map +1 -1
- package/package.json +20 -18
- package/LICENSE +0 -21
- package/dist/audit-logger.d.ts +0 -46
- package/dist/audit-logger.d.ts.map +0 -1
- package/dist/audit-logger.js +0 -105
- package/dist/audit-logger.js.map +0 -1
- package/dist/correlation-engine.d.ts +0 -41
- package/dist/correlation-engine.d.ts.map +0 -1
- package/dist/correlation-engine.js +0 -313
- package/dist/correlation-engine.js.map +0 -1
- package/dist/feed-distributor.d.ts +0 -36
- package/dist/feed-distributor.d.ts.map +0 -1
- package/dist/feed-distributor.js +0 -125
- package/dist/feed-distributor.js.map +0 -1
- package/dist/ioc-store.d.ts +0 -83
- package/dist/ioc-store.d.ts.map +0 -1
- package/dist/ioc-store.js +0 -278
- package/dist/ioc-store.js.map +0 -1
- package/dist/query-handlers.d.ts +0 -40
- package/dist/query-handlers.d.ts.map +0 -1
- package/dist/query-handlers.js +0 -211
- package/dist/query-handlers.js.map +0 -1
- package/dist/reputation-engine.d.ts +0 -44
- package/dist/reputation-engine.d.ts.map +0 -1
- package/dist/reputation-engine.js +0 -169
- package/dist/reputation-engine.js.map +0 -1
- package/dist/rule-generator.d.ts +0 -47
- package/dist/rule-generator.d.ts.map +0 -1
- package/dist/rule-generator.js +0 -238
- package/dist/rule-generator.js.map +0 -1
- package/dist/scheduler.d.ts +0 -52
- package/dist/scheduler.d.ts.map +0 -1
- package/dist/scheduler.js +0 -143
- package/dist/scheduler.js.map +0 -1
- package/dist/sighting-store.d.ts +0 -61
- package/dist/sighting-store.d.ts.map +0 -1
- package/dist/sighting-store.js +0 -191
- package/dist/sighting-store.js.map +0 -1
package/dist/backup.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BackupManager - SQLite database backup with rotation
|
|
3
|
+
*
|
|
4
|
+
* Uses WAL checkpoint + file copy for a consistent backup.
|
|
5
|
+
* Keeps the last N backups (default 7) and rotates old ones automatically.
|
|
6
|
+
*/
|
|
7
|
+
export interface BackupResult {
|
|
8
|
+
readonly path: string;
|
|
9
|
+
readonly sizeBytes: number;
|
|
10
|
+
readonly timestamp: string;
|
|
11
|
+
}
|
|
12
|
+
export declare class BackupManager {
|
|
13
|
+
private readonly dbPath;
|
|
14
|
+
private readonly backupDir;
|
|
15
|
+
private readonly maxBackups;
|
|
16
|
+
private readonly dbName;
|
|
17
|
+
constructor(dbPath: string, backupDir: string, maxBackups?: number);
|
|
18
|
+
/**
|
|
19
|
+
* Create a consistent backup of the SQLite database.
|
|
20
|
+
*
|
|
21
|
+
* 1. Opens the DB in readonly mode
|
|
22
|
+
* 2. Runs a WAL checkpoint (TRUNCATE) to flush pending writes
|
|
23
|
+
* 3. Copies the database file to the backup directory
|
|
24
|
+
* 4. Rotates old backups beyond maxBackups
|
|
25
|
+
*/
|
|
26
|
+
backup(): BackupResult;
|
|
27
|
+
/**
|
|
28
|
+
* Remove old backups beyond maxBackups, keeping the newest ones.
|
|
29
|
+
*/
|
|
30
|
+
rotate(): void;
|
|
31
|
+
/**
|
|
32
|
+
* List existing backups for this database, sorted newest-first.
|
|
33
|
+
*/
|
|
34
|
+
listBackups(): string[];
|
|
35
|
+
/**
|
|
36
|
+
* Format a byte count as a human-readable string.
|
|
37
|
+
*/
|
|
38
|
+
static formatSize(bytes: number): string;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=backup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../src/backup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,SAAI;IAkB7D;;;;;;;OAOG;IACH,MAAM,IAAI,YAAY;IA0CtB;;OAEG;IACH,MAAM,IAAI,IAAI;IAsBd;;OAEG;IACH,WAAW,IAAI,MAAM,EAAE;IAYvB;;OAEG;IACH,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;CAKzC"}
|
package/dist/backup.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BackupManager - SQLite database backup with rotation
|
|
3
|
+
*
|
|
4
|
+
* Uses WAL checkpoint + file copy for a consistent backup.
|
|
5
|
+
* Keeps the last N backups (default 7) and rotates old ones automatically.
|
|
6
|
+
*/
|
|
7
|
+
import Database from 'better-sqlite3';
|
|
8
|
+
import { existsSync, mkdirSync, readdirSync, unlinkSync, copyFileSync, statSync } from 'node:fs';
|
|
9
|
+
import { join, basename } from 'node:path';
|
|
10
|
+
export class BackupManager {
|
|
11
|
+
dbPath;
|
|
12
|
+
backupDir;
|
|
13
|
+
maxBackups;
|
|
14
|
+
dbName;
|
|
15
|
+
constructor(dbPath, backupDir, maxBackups = 7) {
|
|
16
|
+
if (!dbPath || !backupDir) {
|
|
17
|
+
throw new Error('dbPath and backupDir are required');
|
|
18
|
+
}
|
|
19
|
+
if (maxBackups < 1) {
|
|
20
|
+
throw new Error('maxBackups must be at least 1');
|
|
21
|
+
}
|
|
22
|
+
this.dbPath = dbPath;
|
|
23
|
+
this.backupDir = backupDir;
|
|
24
|
+
this.maxBackups = maxBackups;
|
|
25
|
+
this.dbName = basename(dbPath, '.db');
|
|
26
|
+
if (!existsSync(backupDir)) {
|
|
27
|
+
mkdirSync(backupDir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Create a consistent backup of the SQLite database.
|
|
32
|
+
*
|
|
33
|
+
* 1. Opens the DB in readonly mode
|
|
34
|
+
* 2. Runs a WAL checkpoint (TRUNCATE) to flush pending writes
|
|
35
|
+
* 3. Copies the database file to the backup directory
|
|
36
|
+
* 4. Rotates old backups beyond maxBackups
|
|
37
|
+
*/
|
|
38
|
+
backup() {
|
|
39
|
+
if (!existsSync(this.dbPath)) {
|
|
40
|
+
throw new Error(`Database file not found: ${this.dbPath}`);
|
|
41
|
+
}
|
|
42
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
43
|
+
const backupFileName = `${this.dbName}-${timestamp}.db`;
|
|
44
|
+
const backupPath = join(this.backupDir, backupFileName);
|
|
45
|
+
// Checkpoint WAL to ensure all data is flushed to the main DB file
|
|
46
|
+
const db = new Database(this.dbPath);
|
|
47
|
+
try {
|
|
48
|
+
db.pragma('wal_checkpoint(TRUNCATE)');
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
db.close();
|
|
52
|
+
}
|
|
53
|
+
// Copy the database file
|
|
54
|
+
copyFileSync(this.dbPath, backupPath);
|
|
55
|
+
// Also copy WAL/SHM if they exist (belt and suspenders)
|
|
56
|
+
const walPath = `${this.dbPath}-wal`;
|
|
57
|
+
const shmPath = `${this.dbPath}-shm`;
|
|
58
|
+
if (existsSync(walPath)) {
|
|
59
|
+
copyFileSync(walPath, `${backupPath}-wal`);
|
|
60
|
+
}
|
|
61
|
+
if (existsSync(shmPath)) {
|
|
62
|
+
copyFileSync(shmPath, `${backupPath}-shm`);
|
|
63
|
+
}
|
|
64
|
+
const sizeBytes = statSync(backupPath).size;
|
|
65
|
+
// Rotate old backups
|
|
66
|
+
this.rotate();
|
|
67
|
+
return {
|
|
68
|
+
path: backupPath,
|
|
69
|
+
sizeBytes,
|
|
70
|
+
timestamp,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Remove old backups beyond maxBackups, keeping the newest ones.
|
|
75
|
+
*/
|
|
76
|
+
rotate() {
|
|
77
|
+
const backups = this.listBackups();
|
|
78
|
+
if (backups.length <= this.maxBackups) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const toDelete = backups.slice(this.maxBackups);
|
|
82
|
+
for (const fileName of toDelete) {
|
|
83
|
+
const filePath = join(this.backupDir, fileName);
|
|
84
|
+
try {
|
|
85
|
+
unlinkSync(filePath);
|
|
86
|
+
// Also clean up WAL/SHM companions
|
|
87
|
+
const walCompanion = `${filePath}-wal`;
|
|
88
|
+
const shmCompanion = `${filePath}-shm`;
|
|
89
|
+
if (existsSync(walCompanion))
|
|
90
|
+
unlinkSync(walCompanion);
|
|
91
|
+
if (existsSync(shmCompanion))
|
|
92
|
+
unlinkSync(shmCompanion);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// Best-effort deletion; log nothing to avoid noise
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* List existing backups for this database, sorted newest-first.
|
|
101
|
+
*/
|
|
102
|
+
listBackups() {
|
|
103
|
+
if (!existsSync(this.backupDir)) {
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
const prefix = `${this.dbName}-`;
|
|
107
|
+
return readdirSync(this.backupDir)
|
|
108
|
+
.filter((f) => f.startsWith(prefix) && f.endsWith('.db') && !f.endsWith('-wal') && !f.endsWith('-shm'))
|
|
109
|
+
.sort()
|
|
110
|
+
.reverse(); // newest first (ISO timestamps sort lexicographically)
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Format a byte count as a human-readable string.
|
|
114
|
+
*/
|
|
115
|
+
static formatSize(bytes) {
|
|
116
|
+
if (bytes < 1024)
|
|
117
|
+
return `${bytes} B`;
|
|
118
|
+
if (bytes < 1024 * 1024)
|
|
119
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
120
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=backup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup.js","sourceRoot":"","sources":["../src/backup.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -6,41 +6,29 @@
|
|
|
6
6
|
* Usage: threat-cloud [--port 8080] [--host 0.0.0.0] [--db ./data/threats.db]
|
|
7
7
|
*/
|
|
8
8
|
import { ThreatCloudServer } from './server.js';
|
|
9
|
-
const MIN_API_KEY_LENGTH = 32;
|
|
10
9
|
function parseArgs(args) {
|
|
11
10
|
const config = {};
|
|
12
11
|
for (let i = 0; i < args.length; i++) {
|
|
13
12
|
switch (args[i]) {
|
|
14
|
-
case '--port':
|
|
15
|
-
|
|
16
|
-
if (isNaN(port) || port < 1 || port > 65535) {
|
|
17
|
-
console.error(`Error: Invalid port number. Must be 1-65535.`);
|
|
18
|
-
process.exit(1);
|
|
19
|
-
}
|
|
20
|
-
config.port = port;
|
|
13
|
+
case '--port':
|
|
14
|
+
config.port = Number(args[++i]);
|
|
21
15
|
break;
|
|
22
|
-
}
|
|
23
16
|
case '--host':
|
|
24
17
|
config.host = args[++i];
|
|
25
18
|
break;
|
|
26
19
|
case '--db':
|
|
27
20
|
config.dbPath = args[++i];
|
|
28
21
|
break;
|
|
29
|
-
case '--
|
|
30
|
-
config.backupDir = args[++i];
|
|
31
|
-
break;
|
|
32
|
-
case '--api-key': {
|
|
33
|
-
const keys = (args[++i] ?? '').split(',').filter(Boolean);
|
|
34
|
-
const weak = keys.filter((k) => k.length < MIN_API_KEY_LENGTH);
|
|
35
|
-
if (weak.length > 0) {
|
|
36
|
-
console.error(`Error: API keys must be at least ${MIN_API_KEY_LENGTH} characters. ` +
|
|
37
|
-
`Generate with: openssl rand -hex 32`);
|
|
38
|
-
process.exit(1);
|
|
39
|
-
}
|
|
22
|
+
case '--api-key':
|
|
40
23
|
config.apiKeyRequired = true;
|
|
41
|
-
config.apiKeys =
|
|
24
|
+
config.apiKeys = (args[++i] ?? '').split(',');
|
|
25
|
+
break;
|
|
26
|
+
case '--anthropic-api-key':
|
|
27
|
+
config.anthropicApiKey = args[++i];
|
|
28
|
+
break;
|
|
29
|
+
case '--admin-api-key':
|
|
30
|
+
config.adminApiKey = args[++i];
|
|
42
31
|
break;
|
|
43
|
-
}
|
|
44
32
|
case '--help':
|
|
45
33
|
console.log(`
|
|
46
34
|
Threat Cloud Server - Collective Threat Intelligence Backend
|
|
@@ -48,15 +36,13 @@ Threat Cloud Server - Collective Threat Intelligence Backend
|
|
|
48
36
|
Usage: threat-cloud [options]
|
|
49
37
|
|
|
50
38
|
Options:
|
|
51
|
-
--port <number>
|
|
52
|
-
--host <string>
|
|
53
|
-
--db <path>
|
|
54
|
-
--api-key <keys>
|
|
55
|
-
--
|
|
56
|
-
--
|
|
57
|
-
|
|
58
|
-
Generate API key:
|
|
59
|
-
openssl rand -hex 32
|
|
39
|
+
--port <number> Listen port (default: 8080)
|
|
40
|
+
--host <string> Listen host (default: 127.0.0.1)
|
|
41
|
+
--db <path> SQLite database path (default: ./threat-cloud.db)
|
|
42
|
+
--api-key <keys> Comma-separated API keys (enables auth)
|
|
43
|
+
--anthropic-api-key <key> Anthropic API key for LLM review of ATR proposals
|
|
44
|
+
--admin-api-key <key> Admin key for write-protected endpoints (POST /api/rules)
|
|
45
|
+
--help Show this help
|
|
60
46
|
`);
|
|
61
47
|
process.exit(0);
|
|
62
48
|
}
|
|
@@ -65,28 +51,16 @@ Generate API key:
|
|
|
65
51
|
}
|
|
66
52
|
async function main() {
|
|
67
53
|
const args = parseArgs(process.argv.slice(2));
|
|
68
|
-
// Environment variables override defaults; CLI args override env
|
|
69
|
-
const envApiKeys = process.env['TC_API_KEYS']?.split(',').filter(Boolean) ?? [];
|
|
70
|
-
// Validate env API keys length in production
|
|
71
|
-
if (process.env['NODE_ENV'] === 'production' && envApiKeys.length === 0) {
|
|
72
|
-
console.error('Error: TC_API_KEYS is required in production mode.');
|
|
73
|
-
console.error('Generate with: openssl rand -hex 32');
|
|
74
|
-
process.exit(1);
|
|
75
|
-
}
|
|
76
|
-
const weakEnvKeys = envApiKeys.filter((k) => k.length < MIN_API_KEY_LENGTH);
|
|
77
|
-
if (weakEnvKeys.length > 0) {
|
|
78
|
-
console.warn(`WARNING: ${weakEnvKeys.length} API key(s) shorter than ${MIN_API_KEY_LENGTH} chars. ` +
|
|
79
|
-
`Generate stronger keys with: openssl rand -hex 32`);
|
|
80
|
-
}
|
|
81
54
|
const config = {
|
|
82
|
-
port: args.port ?? Number(process.env['
|
|
83
|
-
host: args.host ??
|
|
84
|
-
dbPath: args.dbPath ?? process.env['
|
|
85
|
-
apiKeyRequired: args.apiKeyRequired ??
|
|
86
|
-
apiKeys: args.apiKeys ??
|
|
55
|
+
port: args.port ?? Number(process.env['PORT'] ?? '8080'),
|
|
56
|
+
host: args.host ?? '0.0.0.0',
|
|
57
|
+
dbPath: args.dbPath ?? process.env['DB_PATH'] ?? './threat-cloud.db',
|
|
58
|
+
apiKeyRequired: args.apiKeyRequired ?? false,
|
|
59
|
+
apiKeys: args.apiKeys ?? [],
|
|
87
60
|
rateLimitPerMinute: 120,
|
|
61
|
+
anthropicApiKey: args.anthropicApiKey ?? process.env['ANTHROPIC_API_KEY'],
|
|
62
|
+
adminApiKey: args.adminApiKey ?? process.env['TC_ADMIN_API_KEY'],
|
|
88
63
|
};
|
|
89
|
-
const backupDir = args.backupDir ?? process.env['TC_BACKUP_DIR'] ?? './backups';
|
|
90
64
|
const server = new ThreatCloudServer(config);
|
|
91
65
|
const shutdown = async () => {
|
|
92
66
|
console.log('\nShutting down Threat Cloud server...');
|
|
@@ -96,20 +70,6 @@ async function main() {
|
|
|
96
70
|
process.on('SIGINT', () => void shutdown());
|
|
97
71
|
process.on('SIGTERM', () => void shutdown());
|
|
98
72
|
await server.start();
|
|
99
|
-
// Schedule daily backup (3am UTC by default)
|
|
100
|
-
const runBackup = () => {
|
|
101
|
-
const dest = server.getScheduler().runBackup(backupDir);
|
|
102
|
-
if (dest) {
|
|
103
|
-
console.log(`[Backup] Database backed up to ${dest}`);
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
console.error('[Backup] Database backup failed');
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
// Initial backup on startup
|
|
110
|
-
runBackup();
|
|
111
|
-
// Daily backup interval
|
|
112
|
-
setInterval(runBackup, 24 * 60 * 60 * 1000);
|
|
113
73
|
}
|
|
114
74
|
void main();
|
|
115
75
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGhD,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/database.d.ts
CHANGED
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
* SQLite database layer for Threat Cloud
|
|
3
3
|
* 威脅雲 SQLite 資料庫層
|
|
4
4
|
*
|
|
5
|
-
* Stores anonymized threat data
|
|
5
|
+
* Stores anonymized threat data and community rules using better-sqlite3.
|
|
6
6
|
*
|
|
7
7
|
* @module @panguard-ai/threat-cloud/database
|
|
8
8
|
*/
|
|
9
|
-
import
|
|
10
|
-
import type { AnonymizedThreatData, ThreatCloudRule, ThreatStats, EnrichedThreatEvent, TrapIntelligencePayload } from './types.js';
|
|
9
|
+
import type { AnonymizedThreatData, ThreatCloudRule, ThreatStats, ATRProposal, SkillThreatSubmission } from './types.js';
|
|
11
10
|
/**
|
|
12
11
|
* Threat Cloud database backed by SQLite
|
|
13
12
|
* 基於 SQLite 的威脅雲資料庫
|
|
@@ -15,48 +14,71 @@ import type { AnonymizedThreatData, ThreatCloudRule, ThreatStats, EnrichedThreat
|
|
|
15
14
|
export declare class ThreatCloudDB {
|
|
16
15
|
private readonly db;
|
|
17
16
|
constructor(dbPath: string);
|
|
18
|
-
/** Create
|
|
19
|
-
backup(destPath: string): void;
|
|
20
|
-
/** Expose underlying db for sub-modules (IoCStore, etc.) / 暴露底層 DB 給子模組 */
|
|
21
|
-
getDB(): Database.Database;
|
|
22
|
-
/** Create original tables if they don't exist / 建立原始資料表 */
|
|
17
|
+
/** Create tables if they don't exist / 建立資料表 */
|
|
23
18
|
private initialize;
|
|
24
|
-
/** Run idempotent schema migrations / 執行冪等 schema 遷移 */
|
|
25
|
-
private runMigrations;
|
|
26
19
|
/** Insert anonymized threat data / 插入匿名化威脅數據 */
|
|
27
20
|
insertThreat(data: AnonymizedThreatData): void;
|
|
28
|
-
/**
|
|
29
|
-
* Insert enriched threat event (deduplicates by event_hash).
|
|
30
|
-
* Returns the row id if inserted, null if duplicate.
|
|
31
|
-
* 插入豐富化威脅事件(以 event_hash 去重)
|
|
32
|
-
*/
|
|
33
|
-
insertEnrichedThreat(event: Omit<EnrichedThreatEvent, 'id'>): number | null;
|
|
34
|
-
/**
|
|
35
|
-
* Insert trap credential records.
|
|
36
|
-
* Usernames are hashed (SHA-256, truncated to 16 hex chars) before storage
|
|
37
|
-
* to avoid storing PII from attacker-attempted credentials.
|
|
38
|
-
* 插入 Trap 憑證記錄(使用者名稱先雜湊化以避免 PII 洩漏)
|
|
39
|
-
*/
|
|
40
|
-
insertTrapCredentials(enrichedThreatId: number, credentials: Array<{
|
|
41
|
-
username: string;
|
|
42
|
-
count: number;
|
|
43
|
-
}>): void;
|
|
44
|
-
/** Get enriched threats count by source type / 依來源類型取得豐富化威脅數量 */
|
|
45
|
-
getEnrichedThreatCountBySource(): Record<string, number>;
|
|
46
|
-
/** Count related threats for an IP / 計算某 IP 的相關威脅數量 */
|
|
47
|
-
countRelatedThreats(ip: string): number;
|
|
48
|
-
/** Convert AnonymizedThreatData to EnrichedThreatEvent / 轉換 Guard 資料 */
|
|
49
|
-
static guardToEnriched(data: AnonymizedThreatData): Omit<EnrichedThreatEvent, 'id'>;
|
|
50
|
-
/** Convert TrapIntelligencePayload to EnrichedThreatEvent / 轉換 Trap 資料 */
|
|
51
|
-
static trapToEnriched(data: TrapIntelligencePayload): Omit<EnrichedThreatEvent, 'id'>;
|
|
52
21
|
/** Insert or update a community rule / 插入或更新社群規則 */
|
|
53
22
|
upsertRule(rule: ThreatCloudRule): void;
|
|
54
23
|
/** Fetch rules published after a given timestamp / 取得指定時間後發佈的規則 */
|
|
55
24
|
getRulesSince(since: string): ThreatCloudRule[];
|
|
56
|
-
/** Fetch all rules /
|
|
57
|
-
getAllRules(): ThreatCloudRule[];
|
|
25
|
+
/** Fetch all rules with limit / 取得所有規則(含限制) */
|
|
26
|
+
getAllRules(limit?: number): ThreatCloudRule[];
|
|
27
|
+
/** Insert ATR rule proposal / 插入 ATR 規則提案 */
|
|
28
|
+
insertATRProposal(proposal: ATRProposal): void;
|
|
29
|
+
/** Get ATR proposals, optionally filtered by status / 取得 ATR 提案 */
|
|
30
|
+
getATRProposals(status?: string): unknown[];
|
|
31
|
+
/** Increment confirmations for a proposal; auto-confirm at >= 3 / 增加提案確認數 */
|
|
32
|
+
confirmATRProposal(patternHash: string): void;
|
|
33
|
+
/** Update LLM review verdict for a proposal / 更新 LLM 審查結果 */
|
|
34
|
+
updateATRProposalLLMReview(patternHash: string, verdict: string): void;
|
|
35
|
+
/** Insert ATR feedback / 插入 ATR 回饋 */
|
|
36
|
+
insertATRFeedback(ruleId: string, isTruePositive: boolean, clientId?: string): void;
|
|
37
|
+
/** Get feedback stats for a rule / 取得規則回饋統計 */
|
|
38
|
+
getATRFeedbackStats(ruleId: string): {
|
|
39
|
+
truePositives: number;
|
|
40
|
+
falsePositives: number;
|
|
41
|
+
};
|
|
42
|
+
/** Insert skill threat submission / 插入技能威脅提交 */
|
|
43
|
+
insertSkillThreat(submission: SkillThreatSubmission): void;
|
|
44
|
+
/** Get recent skill threats / 取得最近技能威脅 */
|
|
45
|
+
getSkillThreats(limit?: number): unknown[];
|
|
46
|
+
/** Get proposal statistics / 取得提案統計 */
|
|
47
|
+
getProposalStats(): {
|
|
48
|
+
pending: number;
|
|
49
|
+
confirmed: number;
|
|
50
|
+
rejected: number;
|
|
51
|
+
total: number;
|
|
52
|
+
};
|
|
58
53
|
/** Get threat statistics / 取得威脅統計 */
|
|
59
54
|
getStats(): ThreatStats;
|
|
55
|
+
/** Get confirmed/promoted ATR rules, optionally filtered by date / 取得已確認 ATR 規則 */
|
|
56
|
+
getConfirmedATRRules(since?: string): Array<{
|
|
57
|
+
ruleId: string;
|
|
58
|
+
ruleContent: string;
|
|
59
|
+
publishedAt: string;
|
|
60
|
+
source: string;
|
|
61
|
+
}>;
|
|
62
|
+
/** Get IP blocklist from IoC entries and aggregated threat data / 取得 IP 封鎖清單 */
|
|
63
|
+
getIPBlocklist(minReputation: number): string[];
|
|
64
|
+
/** Get domain blocklist from IoC entries / 取得域名封鎖清單 */
|
|
65
|
+
getDomainBlocklist(minReputation: number): string[];
|
|
66
|
+
/** Upsert an IoC entry / 插入或更新 IoC 條目 */
|
|
67
|
+
upsertIoC(type: string, value: string, reputation: number, source: string): void;
|
|
68
|
+
/** Promote confirmed proposals with approved LLM review to rules / 推廣已確認提案為規則 */
|
|
69
|
+
promoteConfirmedProposals(): number;
|
|
70
|
+
/** Reject an ATR proposal / 拒絕 ATR 提案 */
|
|
71
|
+
rejectATRProposal(patternHash: string): void;
|
|
72
|
+
/** Get rules by source type, optionally filtered by date / 依來源取得規則 */
|
|
73
|
+
getRulesBySource(source: string, since?: string): ThreatCloudRule[];
|
|
74
|
+
/** Report a safe skill (increment confirmations, auto-confirm at 3+) / 回報安全 skill */
|
|
75
|
+
reportSafeSkill(skillName: string, fingerprintHash?: string): void;
|
|
76
|
+
/** Get confirmed community whitelist / 取得社群白名單 */
|
|
77
|
+
getSkillWhitelist(): Array<{
|
|
78
|
+
name: string;
|
|
79
|
+
hash: string | null;
|
|
80
|
+
confirmations: number;
|
|
81
|
+
}>;
|
|
60
82
|
/** Close the database / 關閉資料庫 */
|
|
61
83
|
close(): void;
|
|
62
84
|
}
|
package/dist/database.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEzH;;;GAGG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAoB;gBAE3B,MAAM,EAAE,MAAM;IAO1B,gDAAgD;IAChD,OAAO,CAAC,UAAU;IA+FlB,gDAAgD;IAChD,YAAY,CAAC,IAAI,EAAE,oBAAoB,GAAG,IAAI;IAgB9C,oDAAoD;IACpD,UAAU,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI;IAavC,mEAAmE;IACnE,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,EAAE;IAU/C,+CAA+C;IAC/C,WAAW,CAAC,KAAK,SAAO,GAAG,eAAe,EAAE;IAU5C,6CAA6C;IAC7C,iBAAiB,CAAC,QAAQ,EAAE,WAAW,GAAG,IAAI;IAe9C,mEAAmE;IACnE,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE;IAO3C,6EAA6E;IAC7E,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAU7C,6DAA6D;IAC7D,0BAA0B,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAMtE,sCAAsC;IACtC,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAMnF,+CAA+C;IAC/C,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE;IAMtF,gDAAgD;IAChD,iBAAiB,CAAC,UAAU,EAAE,qBAAqB,GAAG,IAAI;IAe1D,0CAA0C;IAC1C,eAAe,CAAC,KAAK,GAAE,MAAW,GAAG,OAAO,EAAE;IAI9C,uCAAuC;IACvC,gBAAgB,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAQ3F,qCAAqC;IACrC,QAAQ,IAAI,WAAW;IAsCvB,mFAAmF;IACnF,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAiBzH,gFAAgF;IAChF,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,EAAE;IAuB/C,uDAAuD;IACvD,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,EAAE;IAUnD,yCAAyC;IACzC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAYhF,iFAAiF;IACjF,yBAAyB,IAAI,MAAM;IAkCnC,yCAAyC;IACzC,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAO5C,sEAAsE;IACtE,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,eAAe,EAAE;IAiBnE,qFAAqF;IACrF,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI;IAalE,kDAAkD;IAClD,iBAAiB,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IASxF,iCAAiC;IACjC,KAAK,IAAI,IAAI;CAGd"}
|