@remnux/mcp-server 0.1.54 → 0.1.55

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 (65) hide show
  1. package/README.md +7 -1
  2. package/dist/analysis/behavior-prerequisites.d.ts +65 -0
  3. package/dist/analysis/behavior-prerequisites.d.ts.map +1 -0
  4. package/dist/analysis/behavior-prerequisites.js +186 -0
  5. package/dist/analysis/behavior-prerequisites.js.map +1 -0
  6. package/dist/analysis/file-diff.d.ts +59 -0
  7. package/dist/analysis/file-diff.d.ts.map +1 -0
  8. package/dist/analysis/file-diff.js +55 -0
  9. package/dist/analysis/file-diff.js.map +1 -0
  10. package/dist/analysis/index.d.ts +1 -1
  11. package/dist/analysis/index.d.ts.map +1 -1
  12. package/dist/analysis/index.js +1 -1
  13. package/dist/analysis/index.js.map +1 -1
  14. package/dist/analysis/string-usage.d.ts +77 -0
  15. package/dist/analysis/string-usage.d.ts.map +1 -0
  16. package/dist/analysis/string-usage.js +130 -0
  17. package/dist/analysis/string-usage.js.map +1 -0
  18. package/dist/analysis/summarizer.d.ts +22 -0
  19. package/dist/analysis/summarizer.d.ts.map +1 -1
  20. package/dist/analysis/summarizer.js +41 -0
  21. package/dist/analysis/summarizer.js.map +1 -1
  22. package/dist/handlers/analyze-file.d.ts.map +1 -1
  23. package/dist/handlers/analyze-file.js +14 -2
  24. package/dist/handlers/analyze-file.js.map +1 -1
  25. package/dist/handlers/check-behavior-prerequisites.d.ts +16 -0
  26. package/dist/handlers/check-behavior-prerequisites.d.ts.map +1 -0
  27. package/dist/handlers/check-behavior-prerequisites.js +132 -0
  28. package/dist/handlers/check-behavior-prerequisites.js.map +1 -0
  29. package/dist/handlers/compare-files.d.ts +10 -0
  30. package/dist/handlers/compare-files.d.ts.map +1 -0
  31. package/dist/handlers/compare-files.js +147 -0
  32. package/dist/handlers/compare-files.js.map +1 -0
  33. package/dist/handlers/report.d.ts.map +1 -1
  34. package/dist/handlers/report.js +11 -0
  35. package/dist/handlers/report.js.map +1 -1
  36. package/dist/handlers/verify-string-usage.d.ts +10 -0
  37. package/dist/handlers/verify-string-usage.d.ts.map +1 -0
  38. package/dist/handlers/verify-string-usage.js +166 -0
  39. package/dist/handlers/verify-string-usage.js.map +1 -0
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +47 -8
  42. package/dist/index.js.map +1 -1
  43. package/dist/parsers/capa.d.ts +8 -1
  44. package/dist/parsers/capa.d.ts.map +1 -1
  45. package/dist/parsers/capa.js +138 -0
  46. package/dist/parsers/capa.js.map +1 -1
  47. package/dist/parsers/imports.d.ts +36 -0
  48. package/dist/parsers/imports.d.ts.map +1 -0
  49. package/dist/parsers/imports.js +67 -0
  50. package/dist/parsers/imports.js.map +1 -0
  51. package/dist/parsers/r2.d.ts +73 -0
  52. package/dist/parsers/r2.d.ts.map +1 -0
  53. package/dist/parsers/r2.js +161 -0
  54. package/dist/parsers/r2.js.map +1 -0
  55. package/dist/parsers/types.d.ts +19 -0
  56. package/dist/parsers/types.d.ts.map +1 -1
  57. package/dist/report/triage-discipline.d.ts +28 -0
  58. package/dist/report/triage-discipline.d.ts.map +1 -0
  59. package/dist/report/triage-discipline.js +49 -0
  60. package/dist/report/triage-discipline.js.map +1 -0
  61. package/dist/schemas/tools.d.ts +45 -3
  62. package/dist/schemas/tools.d.ts.map +1 -1
  63. package/dist/schemas/tools.js +22 -2
  64. package/dist/schemas/tools.js.map +1 -1
  65. package/package.json +1 -1
package/README.md CHANGED
@@ -15,6 +15,7 @@ Beyond raw command execution, the server encodes malware analysis domain experti
15
15
  - Recommends the right tools for each file type (`suggest_tools`) and retrieves usage flags for any installed tool (`get_tool_help`)
16
16
  - Runs appropriate tool chains automatically (`analyze_file`) with structured output and IOC extraction
17
17
  - Uses neutral language to counteract confirmation bias in AI-generated verdicts
18
+ - Separates static artifacts from executed behavior — tags capa matches by evidence type, gates behavioral claims on the actual import surface (`check_behavior_prerequisites`), and checks whether an embedded string is referenced by code or vestigial (`verify_string_usage`)
18
19
 
19
20
  For additional tool documentation, you can optionally enable the [REMnux docs MCP server](https://docs.remnux.org/~gitbook/mcp).
20
21
 
@@ -260,11 +261,14 @@ claude mcp add remnux --transport http http://REMNUX_IP:3000/mcp \
260
261
  | `download_file` | Download a file from the output directory to the host (password-protected archive by default; password: `infected`) |
261
262
  | `analyze_file` | Auto-select and run REMnux tools based on detected file type |
262
263
  | `extract_iocs` | Extract IOCs (IPs, domains, URLs, hashes, registry keys, etc.) from text with confidence scoring |
264
+ | `check_behavior_prerequisites` | For a Windows PE, report per-behavior `static_capability` (clipboard, HTTP/WinHTTP C2, injection, persistence, etc.) from the import table; packed/.NET binaries return `analysis_incomplete`, not a false negative |
265
+ | `verify_string_usage` | Check whether an embedded string is referenced by code (`referenced_from_code`) or vestigial (`no_code_xrefs_detected`) using radare2 — never claims a string is "unused"; degraded analysis returns `unknown` |
266
+ | `compare_files` | Structured diff of two related samples (loader vs payload): size/entropy, architecture, compiler, packer, imports, capabilities, and sections added/removed |
263
267
  | `suggest_tools` | Detect file type and return recommended tools with analysis hints (no execution) |
264
268
  | `get_tool_help` | Get usage help (`--help` output) for any installed REMnux tool |
265
269
  | `check_tools` | Check which REMnux analysis tools are installed and available |
266
270
  | `get_report_template` | Return a bundled malware analysis report template (CC BY 4.0, by Lenny Zeltser) for drafting a report offline |
267
- | `get_report_guidance` | Return bundled report writing guidelines (sections, confidence, capabilities, IOC tiering, anti-patterns); `topic` narrows the digest |
271
+ | `get_report_guidance` | Return bundled report writing guidelines (sections, confidence, capabilities, IOC tiering, anti-patterns); `topic` narrows the digest, or `topic='triage_checklist'` returns the pre-claim artifact-vs-behavior triage discipline checklist |
268
272
 
269
273
  ### Key Behaviors
270
274
 
@@ -274,6 +278,8 @@ claude mcp add remnux --transport http http://REMNUX_IP:3000/mcp \
274
278
 
275
279
  **Tool advisories:** `analyze_file` includes per-tool `advisory` messages that frame findings in neutral language, prompting the AI to consider benign explanations before concluding malicious intent. When cross-tool conditions indicate follow-up is needed, an `action_required` array appears with prioritized remediation steps.
276
280
 
281
+ **Artifact vs behavior:** capa findings are tagged with `evidence_types` (`artifact`/`behavior`/`structural`/`linking`), derived from the feature nodes that *actually matched* — so a rule that fired only on strings is not mistaken for one backed by code. `analyze_file` rolls this up into a `capability_evidence` field that separates `behavior_capable` (matched on API calls or instructions — the code is present, though static analysis alone doesn't confirm it runs) from `artifact_only` (matched only on data/strings/imports/structure — present, but not evidence the behavior executes). This keeps the distinction between "the data is in the file" and "the binary does this" structural rather than left to prose. See `get_report_guidance` `topic='triage_checklist'` for the corresponding pre-claim discipline.
282
+
277
283
  **Auto-summarization:** When total tool output exceeds ~32KB, `analyze_file` automatically switches to summary mode to prevent LLM context overflow — key findings per tool, full IOC extraction, and paths to saved full outputs for drill-down via `download_file`.
278
284
 
279
285
  **Preprocessing:** Before analysis, `analyze_file` checks for conditions that prevent effective analysis (encrypted Office docs, bloated PEs, PyInstaller bundles) and applies automatic fixes. Results appear in the `preprocessing` field.
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Static behavior-prerequisite analysis.
3
+ *
4
+ * Answers "can this binary even call the APIs that behavior X requires?" from
5
+ * the static import table. This is a deliberate GATE against the most common
6
+ * triage error — claiming a behavior on artifact evidence alone — by forcing an
7
+ * explicit "are the prerequisite APIs accessible?" check.
8
+ *
9
+ * Design constraints (from the project's design review):
10
+ * - It reports a STATIC-capability assessment, never a behavioral verdict. The
11
+ * field is `static_capability` (not `verdict`) and every value is suffixed
12
+ * with the basis (`_statically`) so it cannot be misread as "the sample does
13
+ * X". Static capability is necessary, not sufficient, for the behavior.
14
+ * - It never emits a clean "incapable" on a packed/obscured binary — the
15
+ * import table is unreliable there, so it returns `analysis_incomplete`
16
+ * ("unpack first") rather than a false negative.
17
+ * - When the required APIs are absent but GetProcAddress + a library loader are
18
+ * present, it returns `possibly_via_dynamic_resolution` — it cannot prove
19
+ * those resolvers are NOT used to reach the missing APIs without xref /
20
+ * dynamic analysis, so it does not claim `incapable_statically`.
21
+ * - The behavior→API table is generic malware-analysis knowledge, not tuned to
22
+ * any specific sample.
23
+ */
24
+ export type StaticCapability = "capable_statically" | "incapable_statically" | "possibly_via_dynamic_resolution" | "analysis_incomplete" | "not_applicable";
25
+ export interface BehaviorDef {
26
+ /** Human-readable description of the behavior. */
27
+ description: string;
28
+ /** Required API base names (no ANSI/Unicode suffix). */
29
+ apis: string[];
30
+ /** How many of `apis` must be present to call the binary statically capable. */
31
+ minMatch: number;
32
+ }
33
+ /**
34
+ * Behavior → prerequisite APIs. Base names only (apiPresent handles A/W). The
35
+ * minMatch threshold tolerates that a behavior rarely needs its entire API set
36
+ * and that import names vary, while still requiring a meaningful signature.
37
+ */
38
+ export declare const BEHAVIOR_PREREQUISITES: Record<string, BehaviorDef>;
39
+ export interface PrerequisiteResult {
40
+ behavior: string;
41
+ description: string;
42
+ required_apis: string[];
43
+ apis_present: string[];
44
+ apis_missing: string[];
45
+ dynamic_resolution_present: boolean;
46
+ static_capability: StaticCapability;
47
+ rationale: string;
48
+ recommended_followup: string;
49
+ }
50
+ /** Context derived from the binary (packer detection, whether imports parsed). */
51
+ export interface PrerequisiteContext {
52
+ /** A packer/protector was detected, or the import table is unreadable/empty. */
53
+ obscured: boolean;
54
+ /** readpe produced a usable import table. */
55
+ importsParsed: boolean;
56
+ /** Managed/.NET assembly — native imports don't reflect managed-code capability. */
57
+ managed?: boolean;
58
+ }
59
+ /** Detect the GetProcAddress + library-loader pair that enables runtime resolution. */
60
+ export declare function hasDynamicResolution(functionSet: Set<string>): boolean;
61
+ /**
62
+ * Classify one behavior against an import set + context. Pure and deterministic.
63
+ */
64
+ export declare function classifyPrerequisite(behaviorKey: string, def: BehaviorDef, functionSet: Set<string>, ctx: PrerequisiteContext): PrerequisiteResult;
65
+ //# sourceMappingURL=behavior-prerequisites.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"behavior-prerequisites.d.ts","sourceRoot":"","sources":["../../src/analysis/behavior-prerequisites.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,MAAM,MAAM,gBAAgB,GACxB,oBAAoB,GACpB,sBAAsB,GACtB,iCAAiC,GACjC,qBAAqB,GACrB,gBAAgB,CAAC;AAErB,MAAM,WAAW,WAAW;IAC1B,kDAAkD;IAClD,WAAW,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAkE9D,CAAC;AAMF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,0BAA0B,EAAE,OAAO,CAAC;IACpC,iBAAiB,EAAE,gBAAgB,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,kFAAkF;AAClF,MAAM,WAAW,mBAAmB;IAClC,gFAAgF;IAChF,QAAQ,EAAE,OAAO,CAAC;IAClB,6CAA6C;IAC7C,aAAa,EAAE,OAAO,CAAC;IACvB,oFAAoF;IACpF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAiBD,uFAAuF;AACvF,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAKtE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,WAAW,EAChB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EACxB,GAAG,EAAE,mBAAmB,GACvB,kBAAkB,CA8DpB"}
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Static behavior-prerequisite analysis.
3
+ *
4
+ * Answers "can this binary even call the APIs that behavior X requires?" from
5
+ * the static import table. This is a deliberate GATE against the most common
6
+ * triage error — claiming a behavior on artifact evidence alone — by forcing an
7
+ * explicit "are the prerequisite APIs accessible?" check.
8
+ *
9
+ * Design constraints (from the project's design review):
10
+ * - It reports a STATIC-capability assessment, never a behavioral verdict. The
11
+ * field is `static_capability` (not `verdict`) and every value is suffixed
12
+ * with the basis (`_statically`) so it cannot be misread as "the sample does
13
+ * X". Static capability is necessary, not sufficient, for the behavior.
14
+ * - It never emits a clean "incapable" on a packed/obscured binary — the
15
+ * import table is unreliable there, so it returns `analysis_incomplete`
16
+ * ("unpack first") rather than a false negative.
17
+ * - When the required APIs are absent but GetProcAddress + a library loader are
18
+ * present, it returns `possibly_via_dynamic_resolution` — it cannot prove
19
+ * those resolvers are NOT used to reach the missing APIs without xref /
20
+ * dynamic analysis, so it does not claim `incapable_statically`.
21
+ * - The behavior→API table is generic malware-analysis knowledge, not tuned to
22
+ * any specific sample.
23
+ */
24
+ import { apiPresent } from "../parsers/imports.js";
25
+ /**
26
+ * Behavior → prerequisite APIs. Base names only (apiPresent handles A/W). The
27
+ * minMatch threshold tolerates that a behavior rarely needs its entire API set
28
+ * and that import names vary, while still requiring a meaningful signature.
29
+ */
30
+ export const BEHAVIOR_PREREQUISITES = {
31
+ clipboard_hijacking: {
32
+ description: "Read and/or replace clipboard contents (e.g. a crypto clipper).",
33
+ apis: ["OpenClipboard", "GetClipboardData", "SetClipboardData", "EmptyClipboard"],
34
+ minMatch: 2,
35
+ },
36
+ http_c2_wininet: {
37
+ description: "HTTP(S) command-and-control or download via the WinINet API.",
38
+ apis: ["InternetOpen", "InternetConnect", "InternetOpenUrl", "HttpOpenRequest", "HttpSendRequest", "HttpQueryInfo", "InternetReadFile"],
39
+ minMatch: 2,
40
+ },
41
+ winhttp_c2: {
42
+ description: "HTTP(S) command-and-control or download via the WinHTTP API.",
43
+ apis: ["WinHttpOpen", "WinHttpConnect", "WinHttpOpenRequest", "WinHttpSendRequest", "WinHttpReceiveResponse", "WinHttpReadData"],
44
+ minMatch: 2,
45
+ },
46
+ socket_c2: {
47
+ description: "Raw-socket network communication (Winsock).",
48
+ apis: ["WSAStartup", "socket", "connect", "send", "recv", "WSASend", "WSARecv"],
49
+ minMatch: 3,
50
+ },
51
+ process_injection_remote: {
52
+ description: "Inject code into another process (remote injection).",
53
+ apis: ["OpenProcess", "VirtualAllocEx", "WriteProcessMemory", "CreateRemoteThread", "NtCreateThreadEx", "QueueUserAPC", "NtMapViewOfSection", "NtWriteVirtualMemory"],
54
+ minMatch: 3,
55
+ },
56
+ process_injection_self: {
57
+ description: "Allocate, write, and execute code in the binary's own process.",
58
+ apis: ["VirtualAlloc", "VirtualProtect", "WriteProcessMemory", "NtProtectVirtualMemory"],
59
+ minMatch: 2,
60
+ },
61
+ registry_persistence_run: {
62
+ description: "Persist via a registry Run key (or similar).",
63
+ apis: ["RegOpenKeyEx", "RegCreateKeyEx", "RegSetValueEx"],
64
+ minMatch: 2,
65
+ },
66
+ lnk_persistence: {
67
+ description: "Create a shortcut (.lnk) for persistence via IShellLink (COM).",
68
+ apis: ["CoCreateInstance", "CoInitialize", "CoInitializeEx"],
69
+ minMatch: 1,
70
+ },
71
+ browser_credential_theft: {
72
+ description: "Decrypt DPAPI-protected secrets (browser logins, wallets).",
73
+ apis: ["CryptUnprotectData"],
74
+ minMatch: 1,
75
+ },
76
+ screen_capture: {
77
+ description: "Capture the screen via GDI.",
78
+ apis: ["GetDC", "GetDesktopWindow", "CreateCompatibleDC", "CreateCompatibleBitmap", "BitBlt", "GetDIBits"],
79
+ minMatch: 3,
80
+ },
81
+ keylog_polling: {
82
+ description: "Log keystrokes by polling key state.",
83
+ apis: ["GetAsyncKeyState", "GetKeyState", "GetKeyboardState"],
84
+ minMatch: 1,
85
+ },
86
+ keylog_hook: {
87
+ description: "Log keystrokes via a Windows hook.",
88
+ apis: ["SetWindowsHookEx"],
89
+ minMatch: 1,
90
+ },
91
+ network_share_enum: {
92
+ description: "Enumerate network shares / servers.",
93
+ apis: ["NetShareEnum", "NetServerEnum", "WNetEnumResource", "WNetOpenEnum"],
94
+ minMatch: 1,
95
+ },
96
+ };
97
+ /** Primitives that allow resolving APIs at runtime instead of importing them. */
98
+ const DYNAMIC_RESOLVERS = ["GetProcAddress"];
99
+ const LIBRARY_LOADERS = ["LoadLibrary", "LoadLibraryEx", "LdrLoadDll", "LdrGetProcedureAddress"];
100
+ function followupFor(verdict) {
101
+ switch (verdict) {
102
+ case "capable_statically":
103
+ return "Static capability only. Confirm the behavior at runtime (speakeasy emulation or sandbox detonation) and verify the imported APIs are called from reachable code.";
104
+ case "possibly_via_dynamic_resolution":
105
+ return "Cannot rule out runtime API resolution. Xref the GetProcAddress calls or detonate dynamically to see which APIs are actually resolved.";
106
+ case "incapable_statically":
107
+ return "No static support found. If you still suspect this behavior, the sample may resolve APIs in a way static imports miss — detonate dynamically to be sure (static analysis cannot prove a negative under obfuscation).";
108
+ case "analysis_incomplete":
109
+ return "Unpack the sample (e.g. 'upx -d', or dump it from a sandbox) and re-run this check on the unpacked binary; the visible import table is not the real one.";
110
+ case "not_applicable":
111
+ return "This check applies to Windows PE files. Use the analysis tools appropriate for this file type.";
112
+ }
113
+ }
114
+ /** Detect the GetProcAddress + library-loader pair that enables runtime resolution. */
115
+ export function hasDynamicResolution(functionSet) {
116
+ return (DYNAMIC_RESOLVERS.some((a) => apiPresent(a, functionSet)) &&
117
+ LIBRARY_LOADERS.some((a) => apiPresent(a, functionSet)));
118
+ }
119
+ /**
120
+ * Classify one behavior against an import set + context. Pure and deterministic.
121
+ */
122
+ export function classifyPrerequisite(behaviorKey, def, functionSet, ctx) {
123
+ const present = def.apis.filter((a) => apiPresent(a, functionSet));
124
+ const missing = def.apis.filter((a) => !apiPresent(a, functionSet));
125
+ const dynamicResolutionPresent = hasDynamicResolution(functionSet);
126
+ let static_capability;
127
+ let rationale;
128
+ let recommended_followup;
129
+ if (present.length >= def.minMatch) {
130
+ static_capability = "capable_statically";
131
+ rationale =
132
+ `${present.length} of ${def.apis.length} prerequisite APIs are statically imported ` +
133
+ `(${present.join(", ")}). The binary CAN make these calls; this is not evidence the behavior ` +
134
+ `executes at runtime.`;
135
+ recommended_followup = followupFor(static_capability);
136
+ }
137
+ else if (ctx.managed) {
138
+ // A .NET/managed binary calls the framework, not native APIs — the native
139
+ // import table can't establish capability, so don't emit a false negative.
140
+ static_capability = "analysis_incomplete";
141
+ rationale =
142
+ "This is a managed/.NET assembly; native API imports do not reflect managed-code capability (a .NET " +
143
+ "program calls the framework, e.g. System.Windows.Forms.Clipboard, rather than importing native APIs). " +
144
+ "A clean negative cannot be given from the native import table.";
145
+ recommended_followup =
146
+ "Analyze as .NET: decompile with ilspycmd or dnSpy and look for the relevant framework calls " +
147
+ "(e.g. System.Windows.Forms.Clipboard, System.Net.Http, Microsoft.Win32.Registry).";
148
+ }
149
+ else if (ctx.obscured || !ctx.importsParsed) {
150
+ static_capability = "analysis_incomplete";
151
+ rationale = ctx.importsParsed
152
+ ? "The binary appears packed/obscured, so the import table does not reflect its real API surface. A clean negative cannot be given until it is unpacked."
153
+ : "The PE import table could not be read, so static capability cannot be assessed.";
154
+ recommended_followup =
155
+ "Unpack the sample (e.g. 'upx -d', or dump it from a sandbox) and re-run this check on the unpacked " +
156
+ "binary; the visible import table is not the real one.";
157
+ }
158
+ else if (dynamicResolutionPresent) {
159
+ static_capability = "possibly_via_dynamic_resolution";
160
+ rationale =
161
+ "The prerequisite APIs are not statically imported, but GetProcAddress and a library loader are present, " +
162
+ "so the APIs could be resolved at runtime. Static imports alone cannot rule this out — proving which APIs " +
163
+ "are resolved requires cross-reference or dynamic analysis.";
164
+ recommended_followup = followupFor(static_capability);
165
+ }
166
+ else {
167
+ static_capability = "incapable_statically";
168
+ rationale =
169
+ "The prerequisite APIs are not imported and no runtime-resolution primitives (GetProcAddress + a library " +
170
+ "loader) are present, so the behavior is not statically supported. Note: static analysis cannot prove a " +
171
+ "negative under obfuscation.";
172
+ recommended_followup = followupFor(static_capability);
173
+ }
174
+ return {
175
+ behavior: behaviorKey,
176
+ description: def.description,
177
+ required_apis: def.apis,
178
+ apis_present: present,
179
+ apis_missing: missing,
180
+ dynamic_resolution_present: dynamicResolutionPresent,
181
+ static_capability,
182
+ rationale,
183
+ recommended_followup,
184
+ };
185
+ }
186
+ //# sourceMappingURL=behavior-prerequisites.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"behavior-prerequisites.js","sourceRoot":"","sources":["../../src/analysis/behavior-prerequisites.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Pure structured diff between two related samples (e.g. loader vs unpacked
3
+ * payload). Operates on already-extracted metadata so it is fully unit-testable
4
+ * with synthetic inputs; the handler does the tool runs and feeds this.
5
+ */
6
+ export interface FileMeta {
7
+ file: string;
8
+ file_type: string;
9
+ sha256: string;
10
+ size_bytes: number;
11
+ arch?: string;
12
+ entropy?: number;
13
+ compiler?: string;
14
+ packer?: string;
15
+ /** Imported function names. */
16
+ imports: string[];
17
+ import_count: number;
18
+ /** capa capability names (empty when capa was not run). */
19
+ capabilities: string[];
20
+ /** Whether capa was actually run on this file (depth gate). */
21
+ capa_run: boolean;
22
+ /** Section names. */
23
+ sections: string[];
24
+ }
25
+ export interface SetDiff {
26
+ added: string[];
27
+ removed: string[];
28
+ common_count: number;
29
+ }
30
+ export interface FileDiff {
31
+ file_a: {
32
+ file: string;
33
+ sha256: string;
34
+ };
35
+ file_b: {
36
+ file: string;
37
+ sha256: string;
38
+ };
39
+ size_delta: number;
40
+ entropy_delta?: number;
41
+ architecture?: {
42
+ a?: string;
43
+ b?: string;
44
+ };
45
+ compiler?: {
46
+ a?: string;
47
+ b?: string;
48
+ };
49
+ packer?: {
50
+ a?: string;
51
+ b?: string;
52
+ };
53
+ imports: SetDiff;
54
+ capabilities?: SetDiff;
55
+ sections: SetDiff;
56
+ notes: string[];
57
+ }
58
+ export declare function diffFileMeta(a: FileMeta, b: FileMeta): FileDiff;
59
+ //# sourceMappingURL=file-diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-diff.d.ts","sourceRoot":"","sources":["../../src/analysis/file-diff.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,+DAA+D;IAC/D,QAAQ,EAAE,OAAO,CAAC;IAClB,qBAAqB;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE;QAAE,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,QAAQ,CAAC,EAAE;QAAE,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,MAAM,CAAC,EAAE;QAAE,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAoBD,wBAAgB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAiC/D"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Pure structured diff between two related samples (e.g. loader vs unpacked
3
+ * payload). Operates on already-extracted metadata so it is fully unit-testable
4
+ * with synthetic inputs; the handler does the tool runs and feeds this.
5
+ */
6
+ const CAP = 100;
7
+ /** Diff two string sets (case-insensitive de-dupe, original casing preserved). */
8
+ function setDiff(a, b) {
9
+ const aSet = new Set(a.map((x) => x.toLowerCase()));
10
+ const bSet = new Set(b.map((x) => x.toLowerCase()));
11
+ const added = [...new Set(b)].filter((x) => !aSet.has(x.toLowerCase()));
12
+ const removed = [...new Set(a)].filter((x) => !bSet.has(x.toLowerCase()));
13
+ let common = 0;
14
+ for (const x of aSet)
15
+ if (bSet.has(x))
16
+ common++;
17
+ const cap = (xs) => (xs.length > CAP ? [...xs.slice(0, CAP), `… and ${xs.length - CAP} more`] : xs);
18
+ return { added: cap(added), removed: cap(removed), common_count: common };
19
+ }
20
+ function changed(a, b) {
21
+ return a !== b ? { a, b } : undefined;
22
+ }
23
+ export function diffFileMeta(a, b) {
24
+ const notes = [];
25
+ const arch = changed(a.arch, b.arch);
26
+ if (arch)
27
+ notes.push(`Architecture changed ${a.arch ?? "?"} → ${b.arch ?? "?"} — a different build/stage.`);
28
+ const compiler = changed(a.compiler, b.compiler);
29
+ if (compiler)
30
+ notes.push(`Compiler/toolchain changed ${a.compiler ?? "?"} → ${b.compiler ?? "?"} (a toolchain mix is a strong multi-author/stage signal).`);
31
+ const packer = changed(a.packer, b.packer);
32
+ const imports = setDiff(a.imports, b.imports);
33
+ const sections = setDiff(a.sections, b.sections);
34
+ const bothCapa = a.capa_run && b.capa_run;
35
+ const capabilities = bothCapa ? setDiff(a.capabilities, b.capabilities) : undefined;
36
+ if (!bothCapa)
37
+ notes.push("Capability (capa) diff omitted — run with depth='standard' on both files for it.");
38
+ const entropy_delta = a.entropy !== undefined && b.entropy !== undefined ? Math.round((b.entropy - a.entropy) * 1000) / 1000 : undefined;
39
+ if (packer)
40
+ notes.push(`Packer differs (${a.packer ?? "none"} → ${b.packer ?? "none"}); compare on the unpacked stages for a meaningful import/capability diff.`);
41
+ return {
42
+ file_a: { file: a.file, sha256: a.sha256 },
43
+ file_b: { file: b.file, sha256: b.sha256 },
44
+ size_delta: b.size_bytes - a.size_bytes,
45
+ ...(entropy_delta !== undefined ? { entropy_delta } : {}),
46
+ ...(arch ? { architecture: arch } : {}),
47
+ ...(compiler ? { compiler } : {}),
48
+ ...(packer ? { packer } : {}),
49
+ imports,
50
+ ...(capabilities ? { capabilities } : {}),
51
+ sections,
52
+ notes,
53
+ };
54
+ }
55
+ //# sourceMappingURL=file-diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-diff.js","sourceRoot":"","sources":["../../src/analysis/file-diff.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA0CH,MAAM,GAAG,GAAG,GAAG,CAAC;AAEhB,kFAAkF;AAClF,SAAS,OAAO,CAAC,CAAW,EAAE,CAAW;IACvC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IACxE,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAC1E,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,CAAC,IAAI,IAAI;QAAE,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,MAAM,EAAE,CAAC;IAChD,MAAM,GAAG,GAAG,CAAC,EAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,SAAS,EAAE,CAAC,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC9G,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;AAC5E,CAAC;AAED,SAAS,OAAO,CAAI,CAAgB,EAAE,CAAgB;IACpD,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,CAAW,EAAE,CAAW;IACnD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,IAAI,IAAI,GAAG,MAAM,CAAC,CAAC,IAAI,IAAI,GAAG,6BAA6B,CAAC,CAAC;IAC5G,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;IACjD,IAAI,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC,QAAQ,IAAI,GAAG,MAAM,CAAC,CAAC,QAAQ,IAAI,GAAG,2DAA2D,CAAC,CAAC;IAC5J,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IAE3C,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;IAEjD,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ,CAAC;IAC1C,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACpF,IAAI,CAAC,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,kFAAkF,CAAC,CAAC;IAE9G,MAAM,aAAa,GACjB,CAAC,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACrH,IAAI,MAAM;QAAE,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,MAAM,IAAI,MAAM,MAAM,CAAC,CAAC,MAAM,IAAI,MAAM,4EAA4E,CAAC,CAAC;IAElK,OAAO;QACL,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;QAC1C,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;QAC1C,UAAU,EAAE,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU;QACvC,GAAG,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzD,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,OAAO;QACP,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzC,QAAQ;QACR,KAAK;KACN,CAAC;AACJ,CAAC"}
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * Analysis utilities for REMnux MCP server.
3
3
  */
4
- export { shouldSummarize, extractKeyLines, generateSummary, type ToolSummary, type AnalysisSummary, type ActionRequired, } from "./summarizer.js";
4
+ export { shouldSummarize, extractKeyLines, generateSummary, summarizeCapabilityEvidence, type ToolSummary, type AnalysisSummary, type ActionRequired, type CapabilityEvidence, } from "./summarizer.js";
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analysis/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,eAAe,EACf,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analysis/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,eAAe,EACf,eAAe,EACf,2BAA2B,EAC3B,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,kBAAkB,GACxB,MAAM,iBAAiB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * Analysis utilities for REMnux MCP server.
3
3
  */
4
- export { shouldSummarize, extractKeyLines, generateSummary, } from "./summarizer.js";
4
+ export { shouldSummarize, extractKeyLines, generateSummary, summarizeCapabilityEvidence, } from "./summarizer.js";
5
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/analysis/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,eAAe,EACf,eAAe,GAIhB,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/analysis/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,eAAe,EACf,eAAe,EACf,2BAA2B,GAK5B,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Pure classifier for verify_string_usage: given the strings/sections/xrefs
3
+ * radare2 reported for a queried value, decide whether the string is referenced
4
+ * by code — WITHOUT ever claiming it is "unused".
5
+ *
6
+ * The load-bearing rule (the "negative-only-when-complete" invariant): a
7
+ * `no_code_xrefs_detected` result is emitted ONLY when analysis was provably
8
+ * complete and well-formed. If the radare2 version/schema is unknown, the
9
+ * binary is packed/under-analyzed, output was truncated, or a non-empty xref
10
+ * blob failed to parse, the result is `unknown` — NEVER a negative. This mirrors
11
+ * the check_behavior_prerequisites packed-guard: it refuses to manufacture a
12
+ * confident "no xref" on exactly the obscured samples where static analysis is
13
+ * unreliable.
14
+ */
15
+ import type { R2String, R2Section, R2Xref } from "../parsers/r2.js";
16
+ export type XrefStatus = "referenced_from_code" | "no_code_xrefs_detected" | "data_only" | "unknown";
17
+ export interface MatchGroup {
18
+ value: string;
19
+ /** value === query (vs a substring hit). */
20
+ exact: boolean;
21
+ vaddrs: Array<{
22
+ vaddr: number;
23
+ section: string;
24
+ }>;
25
+ }
26
+ export interface MatchResult {
27
+ value: string;
28
+ exact: boolean;
29
+ found_at: Array<{
30
+ vaddr: string;
31
+ section: string;
32
+ }>;
33
+ xref_status: XrefStatus;
34
+ xref_count: number;
35
+ referenced_from_initializer_only?: boolean;
36
+ data_pointer_found?: boolean;
37
+ xref_sources?: Array<{
38
+ from: string;
39
+ fcn?: string;
40
+ opcode?: string;
41
+ }>;
42
+ note: string;
43
+ recommended_followup: string;
44
+ }
45
+ export interface ClassifyContext {
46
+ isCodeFile: boolean;
47
+ fileType: string;
48
+ /** `r2 -v` was parsed, so xref fields are read against a known schema. */
49
+ schemaKnown: boolean;
50
+ /** Analysis ran to completion, not truncated, no parse anomaly, entry resolved. */
51
+ analysisComplete: boolean;
52
+ /** The binary looks packed/obscured (import table / r2 coverage unreliable). */
53
+ obscured: boolean;
54
+ /** Addresses of functions registered as static initializers (.CRT$XCU/.init_array). */
55
+ initializerFunctionAddrs: Set<number>;
56
+ }
57
+ /** Group string hits by distinct value (exact matches first), capped. */
58
+ export declare function selectMatches(strings: R2String[], query: string, maxMatches?: number): {
59
+ groups: MatchGroup[];
60
+ truncated: boolean;
61
+ };
62
+ /**
63
+ * Whether a cross-reference originates from code. When section info is known,
64
+ * the source address must fall in an executable section (this filters `aar`
65
+ * heuristic refs whose source is in a data section). A containing function is
66
+ * surfaced as corroborating evidence; it's used as the signal only when section
67
+ * info is unavailable.
68
+ */
69
+ export declare function isFromCode(xref: R2Xref, sections: R2Section[]): boolean;
70
+ /**
71
+ * Classify one matched string value against the xrefs r2 reported for its
72
+ * address(es). `xrefs` is the parsed union across the group's vaddrs;
73
+ * `rawXrefParseAnomaly` is true when r2 emitted a non-empty xref blob that did
74
+ * not parse (version field drift — must degrade to `unknown`, never a negative).
75
+ */
76
+ export declare function classifyMatch(group: MatchGroup, xrefs: R2Xref[], sections: R2Section[], ctx: ClassifyContext, rawXrefParseAnomaly?: boolean): MatchResult;
77
+ //# sourceMappingURL=string-usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"string-usage.d.ts","sourceRoot":"","sources":["../../src/analysis/string-usage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEpE,MAAM,MAAM,UAAU,GAClB,sBAAsB,GACtB,wBAAwB,GACxB,WAAW,GACX,SAAS,CAAC;AAEd,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,WAAW,EAAE,UAAU,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,gCAAgC,CAAC,EAAE,OAAO,CAAC;IAC3C,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,WAAW,EAAE,OAAO,CAAC;IACrB,mFAAmF;IACnF,gBAAgB,EAAE,OAAO,CAAC;IAC1B,gFAAgF;IAChF,QAAQ,EAAE,OAAO,CAAC;IAClB,uFAAuF;IACvF,wBAAwB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACvC;AAID,yEAAyE;AACzE,wBAAgB,aAAa,CAC3B,OAAO,EAAE,QAAQ,EAAE,EACnB,KAAK,EAAE,MAAM,EACb,UAAU,SAAK,GACd;IAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAa9C;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,OAAO,CAKvE;AA4CD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,MAAM,EAAE,EACf,QAAQ,EAAE,SAAS,EAAE,EACrB,GAAG,EAAE,eAAe,EACpB,mBAAmB,UAAQ,GAC1B,WAAW,CAyDb"}