windows-exe-decompiler-mcp-server 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/CODEX_INSTALLATION.md +69 -0
- package/COPILOT_INSTALLATION.md +77 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/bin/windows-exe-decompiler-mcp-server.js +3 -0
- package/dist/analysis-provenance.d.ts +184 -0
- package/dist/analysis-provenance.js +74 -0
- package/dist/analysis-task-runner.d.ts +31 -0
- package/dist/analysis-task-runner.js +160 -0
- package/dist/artifact-inventory.d.ts +23 -0
- package/dist/artifact-inventory.js +175 -0
- package/dist/cache-manager.d.ts +128 -0
- package/dist/cache-manager.js +454 -0
- package/dist/confidence-semantics.d.ts +66 -0
- package/dist/confidence-semantics.js +122 -0
- package/dist/config.d.ts +335 -0
- package/dist/config.js +193 -0
- package/dist/database.d.ts +227 -0
- package/dist/database.js +601 -0
- package/dist/decompiler-worker.d.ts +441 -0
- package/dist/decompiler-worker.js +1962 -0
- package/dist/dynamic-trace.d.ts +95 -0
- package/dist/dynamic-trace.js +629 -0
- package/dist/env-validator.d.ts +15 -0
- package/dist/env-validator.js +249 -0
- package/dist/error-handler.d.ts +28 -0
- package/dist/error-handler.example.d.ts +22 -0
- package/dist/error-handler.example.js +141 -0
- package/dist/error-handler.js +139 -0
- package/dist/ghidra-analysis-status.d.ts +49 -0
- package/dist/ghidra-analysis-status.js +178 -0
- package/dist/ghidra-config.d.ts +134 -0
- package/dist/ghidra-config.js +464 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +200 -0
- package/dist/job-queue.d.ts +169 -0
- package/dist/job-queue.js +407 -0
- package/dist/logger.d.ts +106 -0
- package/dist/logger.js +176 -0
- package/dist/policy-guard.d.ts +115 -0
- package/dist/policy-guard.js +243 -0
- package/dist/process-output.d.ts +15 -0
- package/dist/process-output.js +90 -0
- package/dist/prompts/function-explanation-review.d.ts +5 -0
- package/dist/prompts/function-explanation-review.js +64 -0
- package/dist/prompts/semantic-name-review.d.ts +5 -0
- package/dist/prompts/semantic-name-review.js +63 -0
- package/dist/runtime-correlation.d.ts +34 -0
- package/dist/runtime-correlation.js +279 -0
- package/dist/runtime-paths.d.ts +3 -0
- package/dist/runtime-paths.js +11 -0
- package/dist/selection-diff.d.ts +667 -0
- package/dist/selection-diff.js +53 -0
- package/dist/semantic-name-suggestion-artifacts.d.ts +116 -0
- package/dist/semantic-name-suggestion-artifacts.js +314 -0
- package/dist/server.d.ts +129 -0
- package/dist/server.js +578 -0
- package/dist/tools/artifact-read.d.ts +235 -0
- package/dist/tools/artifact-read.js +317 -0
- package/dist/tools/artifacts-diff.d.ts +728 -0
- package/dist/tools/artifacts-diff.js +304 -0
- package/dist/tools/artifacts-list.d.ts +515 -0
- package/dist/tools/artifacts-list.js +389 -0
- package/dist/tools/attack-map.d.ts +290 -0
- package/dist/tools/attack-map.js +519 -0
- package/dist/tools/cache-observability.d.ts +4 -0
- package/dist/tools/cache-observability.js +36 -0
- package/dist/tools/code-function-cfg.d.ts +50 -0
- package/dist/tools/code-function-cfg.js +102 -0
- package/dist/tools/code-function-decompile.d.ts +55 -0
- package/dist/tools/code-function-decompile.js +103 -0
- package/dist/tools/code-function-disassemble.d.ts +43 -0
- package/dist/tools/code-function-disassemble.js +185 -0
- package/dist/tools/code-function-explain-apply.d.ts +255 -0
- package/dist/tools/code-function-explain-apply.js +225 -0
- package/dist/tools/code-function-explain-prepare.d.ts +535 -0
- package/dist/tools/code-function-explain-prepare.js +276 -0
- package/dist/tools/code-function-explain-review.d.ts +397 -0
- package/dist/tools/code-function-explain-review.js +589 -0
- package/dist/tools/code-function-rename-apply.d.ts +248 -0
- package/dist/tools/code-function-rename-apply.js +220 -0
- package/dist/tools/code-function-rename-prepare.d.ts +506 -0
- package/dist/tools/code-function-rename-prepare.js +279 -0
- package/dist/tools/code-function-rename-review.d.ts +574 -0
- package/dist/tools/code-function-rename-review.js +761 -0
- package/dist/tools/code-functions-list.d.ts +37 -0
- package/dist/tools/code-functions-list.js +91 -0
- package/dist/tools/code-functions-rank.d.ts +34 -0
- package/dist/tools/code-functions-rank.js +90 -0
- package/dist/tools/code-functions-reconstruct.d.ts +2725 -0
- package/dist/tools/code-functions-reconstruct.js +2807 -0
- package/dist/tools/code-functions-search.d.ts +39 -0
- package/dist/tools/code-functions-search.js +90 -0
- package/dist/tools/code-reconstruct-export.d.ts +1212 -0
- package/dist/tools/code-reconstruct-export.js +4002 -0
- package/dist/tools/code-reconstruct-plan.d.ts +274 -0
- package/dist/tools/code-reconstruct-plan.js +342 -0
- package/dist/tools/dotnet-metadata-extract.d.ts +541 -0
- package/dist/tools/dotnet-metadata-extract.js +355 -0
- package/dist/tools/dotnet-reconstruct-export.d.ts +567 -0
- package/dist/tools/dotnet-reconstruct-export.js +1151 -0
- package/dist/tools/dotnet-types-list.d.ts +325 -0
- package/dist/tools/dotnet-types-list.js +201 -0
- package/dist/tools/dynamic-dependencies.d.ts +115 -0
- package/dist/tools/dynamic-dependencies.js +213 -0
- package/dist/tools/dynamic-memory-import.d.ts +10 -0
- package/dist/tools/dynamic-memory-import.js +567 -0
- package/dist/tools/dynamic-trace-import.d.ts +10 -0
- package/dist/tools/dynamic-trace-import.js +235 -0
- package/dist/tools/entrypoint-fallback-disasm.d.ts +30 -0
- package/dist/tools/entrypoint-fallback-disasm.js +89 -0
- package/dist/tools/ghidra-analyze.d.ts +88 -0
- package/dist/tools/ghidra-analyze.js +208 -0
- package/dist/tools/ghidra-health.d.ts +37 -0
- package/dist/tools/ghidra-health.js +212 -0
- package/dist/tools/ioc-export.d.ts +209 -0
- package/dist/tools/ioc-export.js +542 -0
- package/dist/tools/packer-detect.d.ts +165 -0
- package/dist/tools/packer-detect.js +284 -0
- package/dist/tools/pe-exports-extract.d.ts +175 -0
- package/dist/tools/pe-exports-extract.js +253 -0
- package/dist/tools/pe-fingerprint.d.ts +234 -0
- package/dist/tools/pe-fingerprint.js +269 -0
- package/dist/tools/pe-imports-extract.d.ts +105 -0
- package/dist/tools/pe-imports-extract.js +245 -0
- package/dist/tools/report-generate.d.ts +157 -0
- package/dist/tools/report-generate.js +457 -0
- package/dist/tools/report-summarize.d.ts +2131 -0
- package/dist/tools/report-summarize.js +596 -0
- package/dist/tools/runtime-detect.d.ts +135 -0
- package/dist/tools/runtime-detect.js +247 -0
- package/dist/tools/sample-ingest.d.ts +94 -0
- package/dist/tools/sample-ingest.js +327 -0
- package/dist/tools/sample-profile-get.d.ts +183 -0
- package/dist/tools/sample-profile-get.js +121 -0
- package/dist/tools/sandbox-execute.d.ts +441 -0
- package/dist/tools/sandbox-execute.js +392 -0
- package/dist/tools/strings-extract.d.ts +375 -0
- package/dist/tools/strings-extract.js +314 -0
- package/dist/tools/strings-floss-decode.d.ts +143 -0
- package/dist/tools/strings-floss-decode.js +259 -0
- package/dist/tools/system-health.d.ts +434 -0
- package/dist/tools/system-health.js +446 -0
- package/dist/tools/task-cancel.d.ts +21 -0
- package/dist/tools/task-cancel.js +70 -0
- package/dist/tools/task-status.d.ts +27 -0
- package/dist/tools/task-status.js +106 -0
- package/dist/tools/task-sweep.d.ts +22 -0
- package/dist/tools/task-sweep.js +77 -0
- package/dist/tools/tool-help.d.ts +340 -0
- package/dist/tools/tool-help.js +261 -0
- package/dist/tools/yara-scan.d.ts +554 -0
- package/dist/tools/yara-scan.js +313 -0
- package/dist/types.d.ts +266 -0
- package/dist/types.js +41 -0
- package/dist/worker-pool.d.ts +204 -0
- package/dist/worker-pool.js +650 -0
- package/dist/workflows/deep-static.d.ts +104 -0
- package/dist/workflows/deep-static.js +276 -0
- package/dist/workflows/function-explanation-review.d.ts +655 -0
- package/dist/workflows/function-explanation-review.js +440 -0
- package/dist/workflows/reconstruct.d.ts +2053 -0
- package/dist/workflows/reconstruct.js +666 -0
- package/dist/workflows/semantic-name-review.d.ts +2418 -0
- package/dist/workflows/semantic-name-review.js +521 -0
- package/dist/workflows/triage.d.ts +659 -0
- package/dist/workflows/triage.js +1374 -0
- package/dist/workspace-manager.d.ts +150 -0
- package/dist/workspace-manager.js +411 -0
- package/ghidra_scripts/DecompileFunction.java +487 -0
- package/ghidra_scripts/DecompileFunction.py +150 -0
- package/ghidra_scripts/ExtractCFG.java +256 -0
- package/ghidra_scripts/ExtractCFG.py +233 -0
- package/ghidra_scripts/ExtractFunctions.java +442 -0
- package/ghidra_scripts/ExtractFunctions.py +101 -0
- package/ghidra_scripts/README.md +125 -0
- package/ghidra_scripts/SearchFunctionReferences.java +380 -0
- package/helpers/DotNetMetadataProbe/DotNetMetadataProbe.csproj +9 -0
- package/helpers/DotNetMetadataProbe/Program.cs +566 -0
- package/install-to-codex.ps1 +178 -0
- package/install-to-copilot.ps1 +303 -0
- package/package.json +101 -0
- package/requirements.txt +9 -0
- package/workers/requirements-dynamic.txt +11 -0
- package/workers/requirements.txt +8 -0
- package/workers/speakeasy_compat.py +175 -0
- package/workers/static_worker.py +5183 -0
- package/workers/yara_rules/default.yar +33 -0
- package/workers/yara_rules/malware_families.yar +93 -0
- package/workers/yara_rules/packers.yar +80 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache Manager
|
|
3
|
+
* Implements cache key generation and three-tier caching architecture
|
|
4
|
+
* Requirements: 20.1, 20.2, 20.3, 20.4, 20.5
|
|
5
|
+
*/
|
|
6
|
+
import crypto from 'crypto';
|
|
7
|
+
import fs from 'fs/promises';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
/**
|
|
10
|
+
* LRU Cache implementation for memory caching
|
|
11
|
+
*/
|
|
12
|
+
class LRUCache {
|
|
13
|
+
cache;
|
|
14
|
+
maxSize;
|
|
15
|
+
ttlMs;
|
|
16
|
+
constructor(maxSize, ttlMs) {
|
|
17
|
+
this.cache = new Map();
|
|
18
|
+
this.maxSize = maxSize;
|
|
19
|
+
this.ttlMs = ttlMs;
|
|
20
|
+
}
|
|
21
|
+
getWithMeta(key) {
|
|
22
|
+
const entry = this.cache.get(key);
|
|
23
|
+
if (!entry)
|
|
24
|
+
return null;
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
// Check if expired
|
|
27
|
+
if (now - entry.insertedAt > this.ttlMs) {
|
|
28
|
+
this.cache.delete(key);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
// Check absolute source expiration (if available)
|
|
32
|
+
if (entry.expiresAtMs && now > entry.expiresAtMs) {
|
|
33
|
+
this.cache.delete(key);
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
// Move to end (most recently used)
|
|
37
|
+
this.cache.delete(key);
|
|
38
|
+
this.cache.set(key, entry);
|
|
39
|
+
return {
|
|
40
|
+
value: entry.value,
|
|
41
|
+
createdAt: entry.createdAt,
|
|
42
|
+
expiresAt: entry.expiresAt,
|
|
43
|
+
sampleSha256: entry.sampleSha256,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
get(key) {
|
|
47
|
+
const entry = this.getWithMeta(key);
|
|
48
|
+
if (!entry) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
return entry.value;
|
|
52
|
+
}
|
|
53
|
+
set(key, value, options) {
|
|
54
|
+
// Remove if exists (to update position)
|
|
55
|
+
this.cache.delete(key);
|
|
56
|
+
// Evict oldest if at capacity
|
|
57
|
+
if (this.cache.size >= this.maxSize) {
|
|
58
|
+
const firstKey = this.cache.keys().next().value;
|
|
59
|
+
if (firstKey) {
|
|
60
|
+
this.cache.delete(firstKey);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const createdAt = options?.createdAt || new Date().toISOString();
|
|
64
|
+
const expiresAt = options?.expiresAt;
|
|
65
|
+
const expiresAtMs = expiresAt ? new Date(expiresAt).getTime() : undefined;
|
|
66
|
+
this.cache.set(key, {
|
|
67
|
+
value,
|
|
68
|
+
insertedAt: Date.now(),
|
|
69
|
+
createdAt,
|
|
70
|
+
expiresAt,
|
|
71
|
+
expiresAtMs: Number.isNaN(expiresAtMs) ? undefined : expiresAtMs,
|
|
72
|
+
sampleSha256: options?.sampleSha256,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
clear() {
|
|
76
|
+
this.cache.clear();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* File System Cache implementation
|
|
81
|
+
*/
|
|
82
|
+
class FileSystemCache {
|
|
83
|
+
cacheDir;
|
|
84
|
+
ttlMs;
|
|
85
|
+
constructor(cacheDir, ttlMs) {
|
|
86
|
+
this.cacheDir = cacheDir;
|
|
87
|
+
this.ttlMs = ttlMs;
|
|
88
|
+
}
|
|
89
|
+
getCachePath(key) {
|
|
90
|
+
// Use first 2 chars for bucketing to avoid too many files in one directory
|
|
91
|
+
const bucket = key.substring(6, 8); // Skip "cache:" prefix
|
|
92
|
+
return path.join(this.cacheDir, bucket, `${key}.json`);
|
|
93
|
+
}
|
|
94
|
+
async getWithMeta(key) {
|
|
95
|
+
try {
|
|
96
|
+
const cachePath = this.getCachePath(key);
|
|
97
|
+
const content = await fs.readFile(cachePath, 'utf-8');
|
|
98
|
+
const cached = JSON.parse(content);
|
|
99
|
+
// Check if expired
|
|
100
|
+
if (cached.expiresAt && new Date(cached.expiresAt) < new Date()) {
|
|
101
|
+
await fs.unlink(cachePath).catch(() => { }); // Ignore errors
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
data: cached.data,
|
|
106
|
+
createdAt: cached.createdAt,
|
|
107
|
+
expiresAt: cached.expiresAt,
|
|
108
|
+
sampleSha256: cached.sampleSha256,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
// File doesn't exist or can't be read
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async get(key) {
|
|
117
|
+
const cached = await this.getWithMeta(key);
|
|
118
|
+
if (!cached) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
return cached.data;
|
|
122
|
+
}
|
|
123
|
+
async set(key, data, ttlMs, sampleSha256) {
|
|
124
|
+
try {
|
|
125
|
+
const cachePath = this.getCachePath(key);
|
|
126
|
+
const cacheDir = path.dirname(cachePath);
|
|
127
|
+
// Ensure directory exists
|
|
128
|
+
await fs.mkdir(cacheDir, { recursive: true });
|
|
129
|
+
const cached = {
|
|
130
|
+
key,
|
|
131
|
+
data,
|
|
132
|
+
createdAt: new Date().toISOString(),
|
|
133
|
+
expiresAt: new Date(Date.now() + (ttlMs || this.ttlMs)).toISOString(),
|
|
134
|
+
sampleSha256,
|
|
135
|
+
};
|
|
136
|
+
await fs.writeFile(cachePath, JSON.stringify(cached), 'utf-8');
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
// Ignore write errors (cache is optional)
|
|
140
|
+
console.warn(`Failed to write cache to filesystem: ${error}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Cache Manager with three-tier architecture
|
|
146
|
+
*
|
|
147
|
+
* Requirements: 20.3, 20.4, 20.5, 26.1 (cache prewarming)
|
|
148
|
+
*
|
|
149
|
+
* Architecture:
|
|
150
|
+
* - L1: Memory cache (LRU, 5 minutes TTL)
|
|
151
|
+
* - L2: File system cache (30 days TTL)
|
|
152
|
+
* - L3: Database cache
|
|
153
|
+
*/
|
|
154
|
+
export class CacheManager {
|
|
155
|
+
memoryCache;
|
|
156
|
+
fsCache;
|
|
157
|
+
db;
|
|
158
|
+
prewarmInProgress = false;
|
|
159
|
+
constructor(cacheDir, db) {
|
|
160
|
+
// L1: Memory cache - 1000 items, 5 minutes TTL
|
|
161
|
+
this.memoryCache = new LRUCache(1000, 5 * 60 * 1000);
|
|
162
|
+
// L2: File system cache - 30 days TTL
|
|
163
|
+
this.fsCache = new FileSystemCache(cacheDir, 30 * 24 * 60 * 60 * 1000);
|
|
164
|
+
// L3: Database cache (optional)
|
|
165
|
+
this.db = db || null;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Get cached result from three-tier cache
|
|
169
|
+
*
|
|
170
|
+
* Requirements: 20.3
|
|
171
|
+
*
|
|
172
|
+
* Algorithm:
|
|
173
|
+
* 1. Check L1 (memory cache)
|
|
174
|
+
* 2. If miss, check L2 (file system cache) and populate L1
|
|
175
|
+
* 3. If miss, check L3 (database cache) and populate L1 and L2
|
|
176
|
+
* 4. Return null if not found in any layer
|
|
177
|
+
*
|
|
178
|
+
* @param key - Cache key
|
|
179
|
+
* @returns Cached data or null if not found
|
|
180
|
+
*/
|
|
181
|
+
async getCachedResult(key) {
|
|
182
|
+
const cached = await this.getCachedResultWithMetadata(key);
|
|
183
|
+
return cached?.data ?? null;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Get cached result with hit metadata for observability.
|
|
187
|
+
*/
|
|
188
|
+
async getCachedResultWithMetadata(key) {
|
|
189
|
+
// L1: Check memory cache
|
|
190
|
+
const memoryResult = this.memoryCache.getWithMeta(key);
|
|
191
|
+
if (memoryResult !== null) {
|
|
192
|
+
return {
|
|
193
|
+
data: memoryResult.value,
|
|
194
|
+
metadata: {
|
|
195
|
+
key,
|
|
196
|
+
tier: 'memory',
|
|
197
|
+
createdAt: memoryResult.createdAt,
|
|
198
|
+
expiresAt: memoryResult.expiresAt,
|
|
199
|
+
fetchedAt: new Date().toISOString(),
|
|
200
|
+
sampleSha256: memoryResult.sampleSha256,
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
// L2: Check file system cache
|
|
205
|
+
const fsResult = await this.fsCache.getWithMeta(key);
|
|
206
|
+
if (fsResult !== null) {
|
|
207
|
+
// Populate L1
|
|
208
|
+
this.memoryCache.set(key, fsResult.data, {
|
|
209
|
+
createdAt: fsResult.createdAt,
|
|
210
|
+
expiresAt: fsResult.expiresAt,
|
|
211
|
+
sampleSha256: fsResult.sampleSha256,
|
|
212
|
+
});
|
|
213
|
+
return {
|
|
214
|
+
data: fsResult.data,
|
|
215
|
+
metadata: {
|
|
216
|
+
key,
|
|
217
|
+
tier: 'filesystem',
|
|
218
|
+
createdAt: fsResult.createdAt,
|
|
219
|
+
expiresAt: fsResult.expiresAt,
|
|
220
|
+
fetchedAt: new Date().toISOString(),
|
|
221
|
+
sampleSha256: fsResult.sampleSha256,
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
// L3: Check database cache
|
|
226
|
+
if (this.db) {
|
|
227
|
+
const cached = await this.db.getCachedResult(key);
|
|
228
|
+
if (cached) {
|
|
229
|
+
// Check if expired
|
|
230
|
+
if (cached.expiresAt && new Date(cached.expiresAt) < new Date()) {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
let remainingTtlMs;
|
|
234
|
+
if (cached.expiresAt) {
|
|
235
|
+
const remaining = new Date(cached.expiresAt).getTime() - Date.now();
|
|
236
|
+
if (remaining > 0) {
|
|
237
|
+
remainingTtlMs = remaining;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// Populate L1 and L2
|
|
241
|
+
this.memoryCache.set(key, cached.data, {
|
|
242
|
+
createdAt: cached.createdAt,
|
|
243
|
+
expiresAt: cached.expiresAt,
|
|
244
|
+
sampleSha256: cached.sampleSha256,
|
|
245
|
+
});
|
|
246
|
+
await this.fsCache.set(key, cached.data, remainingTtlMs, cached.sampleSha256);
|
|
247
|
+
return {
|
|
248
|
+
data: cached.data,
|
|
249
|
+
metadata: {
|
|
250
|
+
key,
|
|
251
|
+
tier: 'database',
|
|
252
|
+
createdAt: cached.createdAt,
|
|
253
|
+
expiresAt: cached.expiresAt,
|
|
254
|
+
fetchedAt: new Date().toISOString(),
|
|
255
|
+
sampleSha256: cached.sampleSha256,
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Set cached result in all three tiers
|
|
264
|
+
*
|
|
265
|
+
* Requirements: 20.4
|
|
266
|
+
*
|
|
267
|
+
* Algorithm:
|
|
268
|
+
* 1. Store in L1 (memory cache)
|
|
269
|
+
* 2. Store in L2 (file system cache)
|
|
270
|
+
* 3. Store in L3 (database cache) if available
|
|
271
|
+
*
|
|
272
|
+
* @param key - Cache key
|
|
273
|
+
* @param data - Data to cache
|
|
274
|
+
* @param ttl - Time to live in milliseconds (optional)
|
|
275
|
+
*/
|
|
276
|
+
async setCachedResult(key, data, ttl, sampleSha256) {
|
|
277
|
+
// L1: Store in memory cache
|
|
278
|
+
this.memoryCache.set(key, data, { sampleSha256 });
|
|
279
|
+
// L2: Store in file system cache
|
|
280
|
+
await this.fsCache.set(key, data, ttl, sampleSha256);
|
|
281
|
+
// L3: Store in database cache
|
|
282
|
+
if (this.db) {
|
|
283
|
+
const expiresAt = ttl ? new Date(Date.now() + ttl).toISOString() : undefined;
|
|
284
|
+
await this.db.setCachedResult(key, data, expiresAt, sampleSha256);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Clear all caches
|
|
289
|
+
*/
|
|
290
|
+
clearAll() {
|
|
291
|
+
this.memoryCache.clear();
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Prewarm cache by loading frequently accessed data into memory
|
|
295
|
+
*
|
|
296
|
+
* Requirements: 26.1 (cache prewarming)
|
|
297
|
+
*
|
|
298
|
+
* Strategy:
|
|
299
|
+
* 1. Load recent cache entries from database
|
|
300
|
+
* 2. Populate L1 (memory) and L2 (filesystem) caches
|
|
301
|
+
* 3. Prioritize entries with high access frequency
|
|
302
|
+
*
|
|
303
|
+
* @param maxEntries - Maximum number of entries to prewarm (default: 100)
|
|
304
|
+
*/
|
|
305
|
+
async prewarmCache(maxEntries = 100) {
|
|
306
|
+
if (this.prewarmInProgress) {
|
|
307
|
+
return 0; // Already prewarming
|
|
308
|
+
}
|
|
309
|
+
this.prewarmInProgress = true;
|
|
310
|
+
let prewarmedCount = 0;
|
|
311
|
+
try {
|
|
312
|
+
if (!this.db) {
|
|
313
|
+
return 0; // No database to prewarm from
|
|
314
|
+
}
|
|
315
|
+
// Get recent cache entries from database
|
|
316
|
+
const recentEntries = await this.db.getRecentCacheEntries(maxEntries);
|
|
317
|
+
// Load into memory and filesystem caches
|
|
318
|
+
for (const entry of recentEntries) {
|
|
319
|
+
try {
|
|
320
|
+
// Skip expired entries
|
|
321
|
+
if (entry.expires_at && new Date(entry.expires_at) < new Date()) {
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
// Parse data
|
|
325
|
+
const data = JSON.parse(entry.data);
|
|
326
|
+
// Populate L1 (memory cache)
|
|
327
|
+
this.memoryCache.set(entry.key, data);
|
|
328
|
+
// Populate L2 (filesystem cache) - async, don't wait
|
|
329
|
+
this.fsCache.set(entry.key, data).catch(() => {
|
|
330
|
+
// Ignore filesystem errors during prewarming
|
|
331
|
+
});
|
|
332
|
+
prewarmedCount++;
|
|
333
|
+
}
|
|
334
|
+
catch (error) {
|
|
335
|
+
// Skip invalid entries
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return prewarmedCount;
|
|
340
|
+
}
|
|
341
|
+
finally {
|
|
342
|
+
this.prewarmInProgress = false;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Prewarm cache for a specific sample
|
|
347
|
+
* Loads all cached results for a sample into memory
|
|
348
|
+
*
|
|
349
|
+
* Requirements: 26.1 (cache prewarming)
|
|
350
|
+
*
|
|
351
|
+
* @param sampleSha256 - SHA256 hash of the sample
|
|
352
|
+
* @returns Number of entries prewarmed
|
|
353
|
+
*/
|
|
354
|
+
async prewarmSampleCache(sampleSha256) {
|
|
355
|
+
if (!this.db) {
|
|
356
|
+
return 0;
|
|
357
|
+
}
|
|
358
|
+
let prewarmedCount = 0;
|
|
359
|
+
// Get all cache entries for this sample
|
|
360
|
+
const sampleEntries = await this.db.getCacheEntriesBySample(sampleSha256);
|
|
361
|
+
for (const entry of sampleEntries) {
|
|
362
|
+
try {
|
|
363
|
+
// Skip expired entries
|
|
364
|
+
if (entry.expires_at && new Date(entry.expires_at) < new Date()) {
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
// Parse data
|
|
368
|
+
const data = JSON.parse(entry.data);
|
|
369
|
+
// Populate L1 (memory cache)
|
|
370
|
+
this.memoryCache.set(entry.key, data);
|
|
371
|
+
prewarmedCount++;
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
// Skip invalid entries
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return prewarmedCount;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Generate deterministic cache key from parameters
|
|
383
|
+
*
|
|
384
|
+
* Requirements: 20.1, 20.2
|
|
385
|
+
*
|
|
386
|
+
* Algorithm:
|
|
387
|
+
* 1. Normalize arguments (sort keys, remove defaults)
|
|
388
|
+
* 2. Create canonical representation
|
|
389
|
+
* 3. Generate SHA256 hash
|
|
390
|
+
*
|
|
391
|
+
* @param params - Cache key parameters
|
|
392
|
+
* @returns Cache key string in format "cache:<sha256>"
|
|
393
|
+
*/
|
|
394
|
+
export function generateCacheKey(params) {
|
|
395
|
+
// Create normalized object with sorted keys
|
|
396
|
+
const normalized = {
|
|
397
|
+
sampleSha256: params.sampleSha256,
|
|
398
|
+
toolName: params.toolName,
|
|
399
|
+
toolVersion: params.toolVersion,
|
|
400
|
+
args: normalizeArgs(params.args),
|
|
401
|
+
...(params.rulesetVersion && { rulesetVersion: params.rulesetVersion })
|
|
402
|
+
};
|
|
403
|
+
// Sort keys at top level to ensure deterministic order
|
|
404
|
+
const sortedKeys = Object.keys(normalized).sort();
|
|
405
|
+
const sortedNormalized = sortedKeys.reduce((acc, key) => {
|
|
406
|
+
acc[key] = normalized[key];
|
|
407
|
+
return acc;
|
|
408
|
+
}, {});
|
|
409
|
+
// Generate canonical JSON string
|
|
410
|
+
const keyString = JSON.stringify(sortedNormalized);
|
|
411
|
+
// Generate SHA256 hash
|
|
412
|
+
const hash = crypto.createHash('sha256').update(keyString).digest('hex');
|
|
413
|
+
return `cache:${hash}`;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Normalize arguments for cache key generation
|
|
417
|
+
*
|
|
418
|
+
* Requirements: 20.2
|
|
419
|
+
*
|
|
420
|
+
* Normalization rules:
|
|
421
|
+
* 1. Sort object keys recursively
|
|
422
|
+
* 2. Remove null and undefined values
|
|
423
|
+
* 3. Recursively normalize nested objects
|
|
424
|
+
* 4. Preserve arrays as-is (order matters)
|
|
425
|
+
*
|
|
426
|
+
* @param args - Arguments object to normalize
|
|
427
|
+
* @returns Normalized arguments object
|
|
428
|
+
*/
|
|
429
|
+
export function normalizeArgs(args) {
|
|
430
|
+
// Handle null/undefined
|
|
431
|
+
if (args === null || args === undefined) {
|
|
432
|
+
return {};
|
|
433
|
+
}
|
|
434
|
+
// Sort keys and filter out null/undefined values
|
|
435
|
+
const sortedKeys = Object.keys(args).sort();
|
|
436
|
+
const normalized = sortedKeys.reduce((acc, key) => {
|
|
437
|
+
const value = args[key];
|
|
438
|
+
// Skip null and undefined values
|
|
439
|
+
if (value === null || value === undefined) {
|
|
440
|
+
return acc;
|
|
441
|
+
}
|
|
442
|
+
// Recursively normalize nested objects
|
|
443
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
444
|
+
acc[key] = normalizeArgs(value);
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
// Keep primitives and arrays as-is
|
|
448
|
+
acc[key] = value;
|
|
449
|
+
}
|
|
450
|
+
return acc;
|
|
451
|
+
}, {});
|
|
452
|
+
return normalized;
|
|
453
|
+
}
|
|
454
|
+
//# sourceMappingURL=cache-manager.js.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const ConfidenceSemanticsSchema: z.ZodObject<{
|
|
3
|
+
score_kind: z.ZodEnum<["heuristic_reconstruction", "runtime_correlation", "naming_resolution", "report_assessment"]>;
|
|
4
|
+
score: z.ZodNullable<z.ZodNumber>;
|
|
5
|
+
band: z.ZodEnum<["none", "low", "medium", "high"]>;
|
|
6
|
+
calibrated: z.ZodBoolean;
|
|
7
|
+
meaning: z.ZodString;
|
|
8
|
+
compare_within: z.ZodString;
|
|
9
|
+
caution: z.ZodString;
|
|
10
|
+
acceptance_rule: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
11
|
+
drivers: z.ZodArray<z.ZodString, "many">;
|
|
12
|
+
}, "strip", z.ZodTypeAny, {
|
|
13
|
+
score: number | null;
|
|
14
|
+
score_kind: "heuristic_reconstruction" | "runtime_correlation" | "naming_resolution" | "report_assessment";
|
|
15
|
+
band: "high" | "none" | "low" | "medium";
|
|
16
|
+
calibrated: boolean;
|
|
17
|
+
meaning: string;
|
|
18
|
+
compare_within: string;
|
|
19
|
+
caution: string;
|
|
20
|
+
drivers: string[];
|
|
21
|
+
acceptance_rule?: string | null | undefined;
|
|
22
|
+
}, {
|
|
23
|
+
score: number | null;
|
|
24
|
+
score_kind: "heuristic_reconstruction" | "runtime_correlation" | "naming_resolution" | "report_assessment";
|
|
25
|
+
band: "high" | "none" | "low" | "medium";
|
|
26
|
+
calibrated: boolean;
|
|
27
|
+
meaning: string;
|
|
28
|
+
compare_within: string;
|
|
29
|
+
caution: string;
|
|
30
|
+
drivers: string[];
|
|
31
|
+
acceptance_rule?: string | null | undefined;
|
|
32
|
+
}>;
|
|
33
|
+
export type ConfidenceSemantics = z.infer<typeof ConfidenceSemanticsSchema>;
|
|
34
|
+
export declare function confidenceBand(score: number | null | undefined): z.infer<typeof ConfidenceSemanticsSchema>['band'];
|
|
35
|
+
export declare function buildReconstructionConfidenceSemantics(input: {
|
|
36
|
+
score: number;
|
|
37
|
+
breakdown: {
|
|
38
|
+
decompile: number;
|
|
39
|
+
cfg: number;
|
|
40
|
+
assembly: number;
|
|
41
|
+
context: number;
|
|
42
|
+
};
|
|
43
|
+
runtimeConfidence?: number | null;
|
|
44
|
+
}): ConfidenceSemantics;
|
|
45
|
+
export declare function buildRuntimeConfidenceSemantics(input: {
|
|
46
|
+
score: number | null | undefined;
|
|
47
|
+
matchedApis?: string[];
|
|
48
|
+
matchedStages?: string[];
|
|
49
|
+
matchedMemoryRegions?: string[];
|
|
50
|
+
executed?: boolean;
|
|
51
|
+
evidenceSources?: string[];
|
|
52
|
+
}): ConfidenceSemantics | null;
|
|
53
|
+
export declare function buildNamingConfidenceSemantics(input: {
|
|
54
|
+
resolutionSource: 'rule' | 'llm' | 'hybrid' | 'unresolved';
|
|
55
|
+
renameConfidence?: number | null;
|
|
56
|
+
llmConfidence?: number | null;
|
|
57
|
+
ruleBasedName?: string | null;
|
|
58
|
+
validatedName?: string | null;
|
|
59
|
+
}): ConfidenceSemantics;
|
|
60
|
+
export declare function buildReportConfidenceSemantics(input: {
|
|
61
|
+
score: number;
|
|
62
|
+
evidenceScope: 'all' | 'latest' | 'session';
|
|
63
|
+
runtimeLayers?: string[];
|
|
64
|
+
executedTracePresent?: boolean;
|
|
65
|
+
}): ConfidenceSemantics;
|
|
66
|
+
//# sourceMappingURL=confidence-semantics.d.ts.map
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const ConfidenceSemanticsSchema = z.object({
|
|
3
|
+
score_kind: z.enum([
|
|
4
|
+
'heuristic_reconstruction',
|
|
5
|
+
'runtime_correlation',
|
|
6
|
+
'naming_resolution',
|
|
7
|
+
'report_assessment',
|
|
8
|
+
]),
|
|
9
|
+
score: z.number().min(0).max(1).nullable(),
|
|
10
|
+
band: z.enum(['none', 'low', 'medium', 'high']),
|
|
11
|
+
calibrated: z.boolean(),
|
|
12
|
+
meaning: z.string(),
|
|
13
|
+
compare_within: z.string(),
|
|
14
|
+
caution: z.string(),
|
|
15
|
+
acceptance_rule: z.string().nullable().optional(),
|
|
16
|
+
drivers: z.array(z.string()),
|
|
17
|
+
});
|
|
18
|
+
function clamp(value, min, max) {
|
|
19
|
+
return Math.max(min, Math.min(max, value));
|
|
20
|
+
}
|
|
21
|
+
export function confidenceBand(score) {
|
|
22
|
+
if (typeof score !== 'number' || Number.isNaN(score)) {
|
|
23
|
+
return 'none';
|
|
24
|
+
}
|
|
25
|
+
if (score >= 0.75) {
|
|
26
|
+
return 'high';
|
|
27
|
+
}
|
|
28
|
+
if (score >= 0.45) {
|
|
29
|
+
return 'medium';
|
|
30
|
+
}
|
|
31
|
+
return 'low';
|
|
32
|
+
}
|
|
33
|
+
export function buildReconstructionConfidenceSemantics(input) {
|
|
34
|
+
const drivers = [
|
|
35
|
+
input.breakdown.decompile > 0 ? `decompile=${input.breakdown.decompile.toFixed(2)}` : '',
|
|
36
|
+
input.breakdown.cfg > 0 ? `cfg=${input.breakdown.cfg.toFixed(2)}` : '',
|
|
37
|
+
input.breakdown.assembly > 0 ? `assembly=${input.breakdown.assembly.toFixed(2)}` : '',
|
|
38
|
+
input.breakdown.context > 0 ? `context=${input.breakdown.context.toFixed(2)}` : '',
|
|
39
|
+
typeof input.runtimeConfidence === 'number' && input.runtimeConfidence > 0
|
|
40
|
+
? `runtime_correlation=${input.runtimeConfidence.toFixed(2)}`
|
|
41
|
+
: '',
|
|
42
|
+
].filter((item) => item.length > 0);
|
|
43
|
+
return {
|
|
44
|
+
score_kind: 'heuristic_reconstruction',
|
|
45
|
+
score: clamp(input.score, 0, 1),
|
|
46
|
+
band: confidenceBand(input.score),
|
|
47
|
+
calibrated: false,
|
|
48
|
+
meaning: 'Heuristic evidence score for reconstruction quality. Higher values mean stronger decompile/CFG/assembly/context support, not probability of semantic correctness.',
|
|
49
|
+
compare_within: 'Compare across functions produced by the same tool version and evidence scope, not across unrelated tools or datasets.',
|
|
50
|
+
caution: 'Treat as ranking-oriented guidance. Low-level helper functions can still be correct at lower scores, and large complex functions can still contain wrong details at high scores.',
|
|
51
|
+
drivers,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function buildRuntimeConfidenceSemantics(input) {
|
|
55
|
+
if (typeof input.score !== 'number' || Number.isNaN(input.score)) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const drivers = [
|
|
59
|
+
(input.matchedApis || []).length > 0 ? `matched_apis=${(input.matchedApis || []).length}` : '',
|
|
60
|
+
(input.matchedStages || []).length > 0 ? `matched_stages=${(input.matchedStages || []).length}` : '',
|
|
61
|
+
(input.matchedMemoryRegions || []).length > 0
|
|
62
|
+
? `matched_regions=${(input.matchedMemoryRegions || []).length}`
|
|
63
|
+
: '',
|
|
64
|
+
input.executed ? 'executed_trace=yes' : 'executed_trace=no',
|
|
65
|
+
(input.evidenceSources || []).length > 0
|
|
66
|
+
? `sources=${(input.evidenceSources || []).slice(0, 3).join(',')}`
|
|
67
|
+
: '',
|
|
68
|
+
].filter((item) => item.length > 0);
|
|
69
|
+
return {
|
|
70
|
+
score_kind: 'runtime_correlation',
|
|
71
|
+
score: clamp(input.score, 0, 1),
|
|
72
|
+
band: confidenceBand(input.score),
|
|
73
|
+
calibrated: false,
|
|
74
|
+
meaning: 'Heuristic overlap score between this function and runtime evidence. Higher values mean more API/stage/region corroboration, not proof that the entire function executed.',
|
|
75
|
+
compare_within: 'Compare across functions under the same evidence_scope and selected runtime artifacts.',
|
|
76
|
+
caution: 'String-heavy memory snapshots and shared helper routines can inflate overlap without proving exact control-flow execution.',
|
|
77
|
+
drivers,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export function buildNamingConfidenceSemantics(input) {
|
|
81
|
+
const baseScore = input.resolutionSource === 'llm'
|
|
82
|
+
? input.llmConfidence ?? null
|
|
83
|
+
: input.renameConfidence ?? input.llmConfidence ?? null;
|
|
84
|
+
const drivers = [
|
|
85
|
+
input.ruleBasedName ? `rule_name=${input.ruleBasedName}` : '',
|
|
86
|
+
input.validatedName ? `validated_name=${input.validatedName}` : '',
|
|
87
|
+
typeof input.renameConfidence === 'number' ? `rule_score=${input.renameConfidence.toFixed(2)}` : '',
|
|
88
|
+
typeof input.llmConfidence === 'number' ? `llm_score=${input.llmConfidence.toFixed(2)}` : '',
|
|
89
|
+
`resolution_source=${input.resolutionSource}`,
|
|
90
|
+
].filter((item) => item.length > 0);
|
|
91
|
+
return {
|
|
92
|
+
score_kind: 'naming_resolution',
|
|
93
|
+
score: typeof baseScore === 'number' ? clamp(baseScore, 0, 1) : null,
|
|
94
|
+
band: confidenceBand(baseScore),
|
|
95
|
+
calibrated: false,
|
|
96
|
+
meaning: 'Semantic naming confidence for the chosen label. It ranks naming support strength, not certainty that the recovered name matches the original source identifier.',
|
|
97
|
+
compare_within: 'Compare across names generated by the same naming pipeline version. Rule-based and LLM-suggested scores are still heuristic.',
|
|
98
|
+
caution: 'A validated name can still be approximate. Original developer naming cannot be reconstructed from this score alone.',
|
|
99
|
+
acceptance_rule: 'Rule-based names currently take priority. Pure LLM suggestions are promoted to validated_name only when llm_confidence >= 0.62.',
|
|
100
|
+
drivers,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export function buildReportConfidenceSemantics(input) {
|
|
104
|
+
const drivers = [
|
|
105
|
+
`evidence_scope=${input.evidenceScope}`,
|
|
106
|
+
(input.runtimeLayers || []).length > 0
|
|
107
|
+
? `runtime_layers=${(input.runtimeLayers || []).join('>')}`
|
|
108
|
+
: 'runtime_layers=static_only',
|
|
109
|
+
input.executedTracePresent ? 'executed_trace=yes' : 'executed_trace=no',
|
|
110
|
+
];
|
|
111
|
+
return {
|
|
112
|
+
score_kind: 'report_assessment',
|
|
113
|
+
score: clamp(input.score, 0, 1),
|
|
114
|
+
band: confidenceBand(input.score),
|
|
115
|
+
calibrated: false,
|
|
116
|
+
meaning: 'Assessment confidence for the generated report or triage summary. It indicates evidence strength and corroboration depth, not a calibrated threat probability.',
|
|
117
|
+
compare_within: 'Compare within the same report mode, tool version, and evidence scope.',
|
|
118
|
+
caution: 'Threat or intent judgments remain evidence-sensitive and can shift when scope changes from all to latest/session or when stronger runtime evidence is added.',
|
|
119
|
+
drivers,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=confidence-semantics.js.map
|