@remnux/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/LICENSE +674 -0
- package/README.md +720 -0
- package/dist/archive-extractor.d.ts +46 -0
- package/dist/archive-extractor.d.ts.map +1 -0
- package/dist/archive-extractor.js +268 -0
- package/dist/archive-extractor.js.map +1 -0
- package/dist/catalog/index.d.ts +40 -0
- package/dist/catalog/index.d.ts.map +1 -0
- package/dist/catalog/index.js +114 -0
- package/dist/catalog/index.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +154 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/archive-passwords.txt +3 -0
- package/dist/connectors/docker.d.ts +13 -0
- package/dist/connectors/docker.d.ts.map +1 -0
- package/dist/connectors/docker.js +201 -0
- package/dist/connectors/docker.js.map +1 -0
- package/dist/connectors/index.d.ts +27 -0
- package/dist/connectors/index.d.ts.map +1 -0
- package/dist/connectors/index.js +23 -0
- package/dist/connectors/index.js.map +1 -0
- package/dist/connectors/local.d.ts +10 -0
- package/dist/connectors/local.d.ts.map +1 -0
- package/dist/connectors/local.js +105 -0
- package/dist/connectors/local.js.map +1 -0
- package/dist/connectors/ssh.d.ts +21 -0
- package/dist/connectors/ssh.d.ts.map +1 -0
- package/dist/connectors/ssh.js +237 -0
- package/dist/connectors/ssh.js.map +1 -0
- package/dist/errors/error-mapper.d.ts +9 -0
- package/dist/errors/error-mapper.d.ts.map +1 -0
- package/dist/errors/error-mapper.js +24 -0
- package/dist/errors/error-mapper.js.map +1 -0
- package/dist/errors/remnux-error.d.ts +14 -0
- package/dist/errors/remnux-error.d.ts.map +1 -0
- package/dist/errors/remnux-error.js +19 -0
- package/dist/errors/remnux-error.js.map +1 -0
- package/dist/file-type-mappings.d.ts +30 -0
- package/dist/file-type-mappings.d.ts.map +1 -0
- package/dist/file-type-mappings.js +136 -0
- package/dist/file-type-mappings.js.map +1 -0
- package/dist/file-upload.d.ts +44 -0
- package/dist/file-upload.d.ts.map +1 -0
- package/dist/file-upload.js +170 -0
- package/dist/file-upload.js.map +1 -0
- package/dist/handlers/analyze-file.d.ts +10 -0
- package/dist/handlers/analyze-file.d.ts.map +1 -0
- package/dist/handlers/analyze-file.js +149 -0
- package/dist/handlers/analyze-file.js.map +1 -0
- package/dist/handlers/check-tools.d.ts +9 -0
- package/dist/handlers/check-tools.d.ts.map +1 -0
- package/dist/handlers/check-tools.js +47 -0
- package/dist/handlers/check-tools.js.map +1 -0
- package/dist/handlers/download-file.d.ts +10 -0
- package/dist/handlers/download-file.d.ts.map +1 -0
- package/dist/handlers/download-file.js +113 -0
- package/dist/handlers/download-file.js.map +1 -0
- package/dist/handlers/download-from-url.d.ts +30 -0
- package/dist/handlers/download-from-url.d.ts.map +1 -0
- package/dist/handlers/download-from-url.js +295 -0
- package/dist/handlers/download-from-url.js.map +1 -0
- package/dist/handlers/extract-archive.d.ts +10 -0
- package/dist/handlers/extract-archive.d.ts.map +1 -0
- package/dist/handlers/extract-archive.js +57 -0
- package/dist/handlers/extract-archive.js.map +1 -0
- package/dist/handlers/extract-iocs.d.ts +10 -0
- package/dist/handlers/extract-iocs.d.ts.map +1 -0
- package/dist/handlers/extract-iocs.js +21 -0
- package/dist/handlers/extract-iocs.js.map +1 -0
- package/dist/handlers/get-file-info.d.ts +10 -0
- package/dist/handlers/get-file-info.d.ts.map +1 -0
- package/dist/handlers/get-file-info.js +89 -0
- package/dist/handlers/get-file-info.js.map +1 -0
- package/dist/handlers/list-files.d.ts +10 -0
- package/dist/handlers/list-files.d.ts.map +1 -0
- package/dist/handlers/list-files.js +60 -0
- package/dist/handlers/list-files.js.map +1 -0
- package/dist/handlers/run-tool.d.ts +10 -0
- package/dist/handlers/run-tool.d.ts.map +1 -0
- package/dist/handlers/run-tool.js +99 -0
- package/dist/handlers/run-tool.js.map +1 -0
- package/dist/handlers/suggest-tools.d.ts +10 -0
- package/dist/handlers/suggest-tools.d.ts.map +1 -0
- package/dist/handlers/suggest-tools.js +202 -0
- package/dist/handlers/suggest-tools.js.map +1 -0
- package/dist/handlers/types.d.ts +15 -0
- package/dist/handlers/types.d.ts.map +1 -0
- package/dist/handlers/types.js +2 -0
- package/dist/handlers/types.js.map +1 -0
- package/dist/handlers/upload-file.d.ts +10 -0
- package/dist/handlers/upload-file.d.ts.map +1 -0
- package/dist/handlers/upload-file.js +33 -0
- package/dist/handlers/upload-file.js.map +1 -0
- package/dist/handlers/upload-from-host.d.ts +10 -0
- package/dist/handlers/upload-from-host.d.ts.map +1 -0
- package/dist/handlers/upload-from-host.js +33 -0
- package/dist/handlers/upload-from-host.js.map +1 -0
- package/dist/handlers/upload-sample.d.ts +10 -0
- package/dist/handlers/upload-sample.d.ts.map +1 -0
- package/dist/handlers/upload-sample.js +26 -0
- package/dist/handlers/upload-sample.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +254 -0
- package/dist/index.js.map +1 -0
- package/dist/ioc/extractor.d.ts +21 -0
- package/dist/ioc/extractor.d.ts.map +1 -0
- package/dist/ioc/extractor.js +91 -0
- package/dist/ioc/extractor.js.map +1 -0
- package/dist/ioc/known-values.d.ts +7 -0
- package/dist/ioc/known-values.d.ts.map +1 -0
- package/dist/ioc/known-values.js +43 -0
- package/dist/ioc/known-values.js.map +1 -0
- package/dist/ioc/noise.d.ts +6 -0
- package/dist/ioc/noise.d.ts.map +1 -0
- package/dist/ioc/noise.js +170 -0
- package/dist/ioc/noise.js.map +1 -0
- package/dist/ioc/patterns.d.ts +10 -0
- package/dist/ioc/patterns.d.ts.map +1 -0
- package/dist/ioc/patterns.js +65 -0
- package/dist/ioc/patterns.js.map +1 -0
- package/dist/ioc/scoring.d.ts +6 -0
- package/dist/ioc/scoring.d.ts.map +1 -0
- package/dist/ioc/scoring.js +69 -0
- package/dist/ioc/scoring.js.map +1 -0
- package/dist/parsers/capa.d.ts +9 -0
- package/dist/parsers/capa.d.ts.map +1 -0
- package/dist/parsers/capa.js +55 -0
- package/dist/parsers/capa.js.map +1 -0
- package/dist/parsers/diec.d.ts +9 -0
- package/dist/parsers/diec.d.ts.map +1 -0
- package/dist/parsers/diec.js +53 -0
- package/dist/parsers/diec.js.map +1 -0
- package/dist/parsers/floss.d.ts +14 -0
- package/dist/parsers/floss.d.ts.map +1 -0
- package/dist/parsers/floss.js +89 -0
- package/dist/parsers/floss.js.map +1 -0
- package/dist/parsers/index.d.ts +16 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +46 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/parsers/oleid.d.ts +8 -0
- package/dist/parsers/oleid.d.ts.map +1 -0
- package/dist/parsers/oleid.js +94 -0
- package/dist/parsers/oleid.js.map +1 -0
- package/dist/parsers/olevba.d.ts +8 -0
- package/dist/parsers/olevba.d.ts.map +1 -0
- package/dist/parsers/olevba.js +83 -0
- package/dist/parsers/olevba.js.map +1 -0
- package/dist/parsers/passthrough.d.ts +6 -0
- package/dist/parsers/passthrough.d.ts.map +1 -0
- package/dist/parsers/passthrough.js +13 -0
- package/dist/parsers/passthrough.js.map +1 -0
- package/dist/parsers/pdf-parser.d.ts +9 -0
- package/dist/parsers/pdf-parser.d.ts.map +1 -0
- package/dist/parsers/pdf-parser.js +76 -0
- package/dist/parsers/pdf-parser.js.map +1 -0
- package/dist/parsers/pdfid.d.ts +9 -0
- package/dist/parsers/pdfid.d.ts.map +1 -0
- package/dist/parsers/pdfid.js +56 -0
- package/dist/parsers/pdfid.js.map +1 -0
- package/dist/parsers/peframe.d.ts +8 -0
- package/dist/parsers/peframe.d.ts.map +1 -0
- package/dist/parsers/peframe.js +76 -0
- package/dist/parsers/peframe.js.map +1 -0
- package/dist/parsers/readelf.d.ts +8 -0
- package/dist/parsers/readelf.d.ts.map +1 -0
- package/dist/parsers/readelf.js +50 -0
- package/dist/parsers/readelf.js.map +1 -0
- package/dist/parsers/types.d.ts +30 -0
- package/dist/parsers/types.d.ts.map +1 -0
- package/dist/parsers/types.js +5 -0
- package/dist/parsers/types.js.map +1 -0
- package/dist/parsers/yara.d.ts +8 -0
- package/dist/parsers/yara.d.ts.map +1 -0
- package/dist/parsers/yara.js +88 -0
- package/dist/parsers/yara.js.map +1 -0
- package/dist/response.d.ts +44 -0
- package/dist/response.d.ts.map +1 -0
- package/dist/response.js +48 -0
- package/dist/response.js.map +1 -0
- package/dist/schemas/tools.d.ts +135 -0
- package/dist/schemas/tools.d.ts.map +1 -0
- package/dist/schemas/tools.js +53 -0
- package/dist/schemas/tools.js.map +1 -0
- package/dist/security/blocklist.d.ts +69 -0
- package/dist/security/blocklist.d.ts.map +1 -0
- package/dist/security/blocklist.js +148 -0
- package/dist/security/blocklist.js.map +1 -0
- package/dist/state/session.d.ts +35 -0
- package/dist/state/session.d.ts.map +1 -0
- package/dist/state/session.js +45 -0
- package/dist/state/session.js.map +1 -0
- package/dist/tools/definitions.d.ts +9 -0
- package/dist/tools/definitions.d.ts.map +1 -0
- package/dist/tools/definitions.js +708 -0
- package/dist/tools/definitions.js.map +1 -0
- package/dist/tools/invoker.d.ts +17 -0
- package/dist/tools/invoker.d.ts.map +1 -0
- package/dist/tools/invoker.js +44 -0
- package/dist/tools/invoker.js.map +1 -0
- package/dist/tools/registry.d.ts +62 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +53 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/workflows/engine.d.ts +27 -0
- package/dist/workflows/engine.d.ts.map +1 -0
- package/dist/workflows/engine.js +224 -0
- package/dist/workflows/engine.js.map +1 -0
- package/dist/workflows/loader.d.ts +33 -0
- package/dist/workflows/loader.d.ts.map +1 -0
- package/dist/workflows/loader.js +130 -0
- package/dist/workflows/loader.js.map +1 -0
- package/dist/workflows/types.d.ts +109 -0
- package/dist/workflows/types.d.ts.map +1 -0
- package/dist/workflows/types.js +5 -0
- package/dist/workflows/types.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File upload module for REMnux MCP server
|
|
3
|
+
*
|
|
4
|
+
* Allows AI assistants to upload files from the host filesystem to the
|
|
5
|
+
* samples directory via MCP. Reads files locally — no base64 in context.
|
|
6
|
+
*/
|
|
7
|
+
import { createHash } from "crypto";
|
|
8
|
+
import { createReadStream, lstatSync } from "fs";
|
|
9
|
+
import { pipeline } from "stream/promises";
|
|
10
|
+
import { basename, isAbsolute } from "path";
|
|
11
|
+
// 200MB limit for uploaded files (stat-based check, no in-memory buffering)
|
|
12
|
+
const MAX_FILE_SIZE = 200 * 1024 * 1024;
|
|
13
|
+
/**
|
|
14
|
+
* Validate filename for security
|
|
15
|
+
* - No path separators (/, \)
|
|
16
|
+
* - No path traversal (..)
|
|
17
|
+
* - No null bytes
|
|
18
|
+
* - Reasonable length
|
|
19
|
+
*/
|
|
20
|
+
export function validateFilename(filename) {
|
|
21
|
+
if (!filename || filename.length === 0) {
|
|
22
|
+
return { valid: false, error: "Filename cannot be empty" };
|
|
23
|
+
}
|
|
24
|
+
if (filename.length > 255) {
|
|
25
|
+
return { valid: false, error: "Filename too long (max 255 characters)" };
|
|
26
|
+
}
|
|
27
|
+
if (filename.includes("/") || filename.includes("\\")) {
|
|
28
|
+
return { valid: false, error: "Filename cannot contain path separators" };
|
|
29
|
+
}
|
|
30
|
+
if (filename.includes("..")) {
|
|
31
|
+
return { valid: false, error: "Filename cannot contain '..'" };
|
|
32
|
+
}
|
|
33
|
+
if (filename.includes("\0")) {
|
|
34
|
+
return { valid: false, error: "Filename cannot contain null bytes" };
|
|
35
|
+
}
|
|
36
|
+
// Check for shell metacharacters that could cause issues
|
|
37
|
+
if (/[;&|`$\n\r'"<>]/.test(filename)) {
|
|
38
|
+
return { valid: false, error: "Filename contains invalid characters" };
|
|
39
|
+
}
|
|
40
|
+
return { valid: true };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Validate a host filesystem path for security
|
|
44
|
+
*/
|
|
45
|
+
export function validateHostPath(hostPath) {
|
|
46
|
+
if (!hostPath || hostPath.length === 0) {
|
|
47
|
+
return { valid: false, error: "host_path cannot be empty" };
|
|
48
|
+
}
|
|
49
|
+
if (!isAbsolute(hostPath)) {
|
|
50
|
+
return { valid: false, error: "host_path must be an absolute path" };
|
|
51
|
+
}
|
|
52
|
+
if (hostPath.includes("\0")) {
|
|
53
|
+
return { valid: false, error: "host_path cannot contain null bytes" };
|
|
54
|
+
}
|
|
55
|
+
// Reject path traversal — always reject ".." components
|
|
56
|
+
if (hostPath.includes("..")) {
|
|
57
|
+
return { valid: false, error: "host_path cannot contain path traversal (..)" };
|
|
58
|
+
}
|
|
59
|
+
// Reject shell metacharacters
|
|
60
|
+
if (/[;&|`$\n\r'"<>]/.test(hostPath)) {
|
|
61
|
+
return { valid: false, error: "host_path contains invalid characters" };
|
|
62
|
+
}
|
|
63
|
+
return { valid: true };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Upload a file from the host filesystem to the samples directory
|
|
67
|
+
*
|
|
68
|
+
* @param connector - Connector to write files on REMnux
|
|
69
|
+
* @param samplesDir - Base samples directory on REMnux
|
|
70
|
+
* @param hostPath - Absolute path on the host filesystem
|
|
71
|
+
* @param filename - Override filename (defaults to basename of hostPath)
|
|
72
|
+
* @param overwrite - Whether to overwrite if file exists (default: false)
|
|
73
|
+
* @returns Upload result with file path, size, and SHA256 hash
|
|
74
|
+
*/
|
|
75
|
+
export async function uploadSampleFromHost(connector, samplesDir, hostPath, filename, overwrite = false) {
|
|
76
|
+
// Validate host path
|
|
77
|
+
const pathValidation = validateHostPath(hostPath);
|
|
78
|
+
if (!pathValidation.valid) {
|
|
79
|
+
return { success: false, error: pathValidation.error };
|
|
80
|
+
}
|
|
81
|
+
// Reject symlinks
|
|
82
|
+
let stat;
|
|
83
|
+
try {
|
|
84
|
+
stat = lstatSync(hostPath);
|
|
85
|
+
}
|
|
86
|
+
catch (_err) {
|
|
87
|
+
return { success: false, error: `File not found: ${hostPath}` };
|
|
88
|
+
}
|
|
89
|
+
if (stat.isSymbolicLink()) {
|
|
90
|
+
return { success: false, error: "Symlinks are not allowed" };
|
|
91
|
+
}
|
|
92
|
+
if (!stat.isFile()) {
|
|
93
|
+
return { success: false, error: "host_path must point to a regular file" };
|
|
94
|
+
}
|
|
95
|
+
// Check file size via stat (no buffering)
|
|
96
|
+
if (stat.size > MAX_FILE_SIZE) {
|
|
97
|
+
const sizeMB = (stat.size / 1024 / 1024).toFixed(0);
|
|
98
|
+
const limitMB = MAX_FILE_SIZE / 1024 / 1024;
|
|
99
|
+
return {
|
|
100
|
+
success: false,
|
|
101
|
+
error: `File size (${sizeMB}MB) exceeds the ${limitMB}MB upload limit. ` +
|
|
102
|
+
`For large files such as memory images, mount a host directory into the container instead:\n` +
|
|
103
|
+
` docker run -v /path/to/evidence:/home/remnux/files/samples/evidence remnux/remnux-distro\n` +
|
|
104
|
+
`Then reference files as: evidence/<filename>`,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
// Determine target filename
|
|
108
|
+
const targetFilename = filename ?? basename(hostPath);
|
|
109
|
+
// Validate target filename
|
|
110
|
+
const filenameValidation = validateFilename(targetFilename);
|
|
111
|
+
if (!filenameValidation.valid) {
|
|
112
|
+
return { success: false, error: filenameValidation.error };
|
|
113
|
+
}
|
|
114
|
+
// Calculate SHA256 hash via streaming
|
|
115
|
+
let sha256;
|
|
116
|
+
try {
|
|
117
|
+
const hash = createHash("sha256");
|
|
118
|
+
await pipeline(createReadStream(hostPath), hash);
|
|
119
|
+
sha256 = hash.digest("hex");
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
return {
|
|
123
|
+
success: false,
|
|
124
|
+
error: `Failed to read file: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// Build full file path
|
|
128
|
+
const filePath = `${samplesDir}/${targetFilename}`;
|
|
129
|
+
// Check if file already exists (unless overwrite is true)
|
|
130
|
+
if (!overwrite) {
|
|
131
|
+
try {
|
|
132
|
+
const checkResult = await connector.execute(["test", "-e", filePath], {
|
|
133
|
+
timeout: 5000,
|
|
134
|
+
});
|
|
135
|
+
if (checkResult.exitCode === 0) {
|
|
136
|
+
return {
|
|
137
|
+
success: false,
|
|
138
|
+
error: "File already exists. Use overwrite=true to replace.",
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// test command failed, file probably doesn't exist
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Ensure target directory exists (fresh containers may lack it)
|
|
147
|
+
try {
|
|
148
|
+
await connector.execute(["mkdir", "-p", samplesDir], { timeout: 5000 });
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Ignore — real errors surface in writeFileFromPath
|
|
152
|
+
}
|
|
153
|
+
// Write file using connector's streaming path-based method
|
|
154
|
+
try {
|
|
155
|
+
await connector.writeFileFromPath(filePath, hostPath);
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
return {
|
|
159
|
+
success: false,
|
|
160
|
+
error: `Failed to write file: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
success: true,
|
|
165
|
+
path: filePath,
|
|
166
|
+
size_bytes: stat.size,
|
|
167
|
+
sha256,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=file-upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-upload.js","sourceRoot":"","sources":["../src/file-upload.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { HandlerDeps } from "./types.js";
|
|
2
|
+
import type { AnalyzeFileArgs } from "../schemas/tools.js";
|
|
3
|
+
export declare function handleAnalyzeFile(deps: HandlerDeps, args: AnalyzeFileArgs): Promise<{
|
|
4
|
+
content: Array<{
|
|
5
|
+
type: "text";
|
|
6
|
+
text: string;
|
|
7
|
+
}>;
|
|
8
|
+
isError?: boolean;
|
|
9
|
+
}>;
|
|
10
|
+
//# sourceMappingURL=analyze-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-file.d.ts","sourceRoot":"","sources":["../../src/handlers/analyze-file.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAwC3D,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,eAAe;;;;;;GAwJtB"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { validateFilePath } from "../security/blocklist.js";
|
|
2
|
+
import { matchFileType, CATEGORY_TAG_MAP } from "../file-type-mappings.js";
|
|
3
|
+
import { toolRegistry } from "../tools/registry.js";
|
|
4
|
+
import { buildCommandFromDefinition } from "../tools/invoker.js";
|
|
5
|
+
import { parseToolOutput } from "../parsers/index.js";
|
|
6
|
+
import { formatResponse, formatError } from "../response.js";
|
|
7
|
+
import { REMnuxError } from "../errors/remnux-error.js";
|
|
8
|
+
import { toREMnuxError } from "../errors/error-mapper.js";
|
|
9
|
+
import { extractIOCs } from "../ioc/extractor.js";
|
|
10
|
+
const DEFAULT_OUTPUT_BUDGET = 80 * 1024; // 80KB default
|
|
11
|
+
const TOTAL_RESPONSE_BUDGET = 200 * 1024; // 200KB total across all tools
|
|
12
|
+
/** Per-tool output budgets — tools known to produce large output get tighter limits. */
|
|
13
|
+
const TOOL_OUTPUT_BUDGETS = {
|
|
14
|
+
floss: 20 * 1024,
|
|
15
|
+
ilspycmd: 15 * 1024,
|
|
16
|
+
rtfdump: 10 * 1024,
|
|
17
|
+
olevba: 30 * 1024,
|
|
18
|
+
oledump: 20 * 1024,
|
|
19
|
+
exiftool: 10 * 1024,
|
|
20
|
+
zipdump: 15 * 1024,
|
|
21
|
+
};
|
|
22
|
+
export async function handleAnalyzeFile(deps, args) {
|
|
23
|
+
const startTime = Date.now();
|
|
24
|
+
try {
|
|
25
|
+
const { connector, config } = deps;
|
|
26
|
+
const depth = (args.depth ?? "standard");
|
|
27
|
+
// Validate file path (skip unless --sandbox)
|
|
28
|
+
if (!config.noSandbox) {
|
|
29
|
+
const validation = validateFilePath(args.file, config.samplesDir);
|
|
30
|
+
if (!validation.safe) {
|
|
31
|
+
return formatError("analyze_file", new REMnuxError(validation.error || "Invalid file path", "INVALID_PATH", "validation", "Use a relative path within the samples directory"), startTime);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const filePath = `${config.samplesDir}/${args.file}`;
|
|
35
|
+
const perToolTimeout = (args.timeout_per_tool || 60) * 1000;
|
|
36
|
+
// Step 1: Detect file type
|
|
37
|
+
let fileOutput;
|
|
38
|
+
try {
|
|
39
|
+
const result = await connector.execute(["file", filePath], { timeout: 30000 });
|
|
40
|
+
fileOutput = result.stdout?.trim() || "";
|
|
41
|
+
if (!fileOutput) {
|
|
42
|
+
return formatError("analyze_file", new REMnuxError("Could not determine file type (empty `file` output)", "EMPTY_OUTPUT", "tool_failure", "Check that the file exists and is readable"), startTime);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
const msg = `Error running file command: ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
47
|
+
return formatError("analyze_file", new REMnuxError(msg, "EMPTY_OUTPUT", "tool_failure", "Check that the file exists and is readable"), startTime);
|
|
48
|
+
}
|
|
49
|
+
// Step 2: Match to category and get tools from registry by tag + depth
|
|
50
|
+
const category = matchFileType(fileOutput, args.file);
|
|
51
|
+
const tag = CATEGORY_TAG_MAP[category.name] ?? "fallback";
|
|
52
|
+
const tools = toolRegistry.byTagAndTier(tag, depth);
|
|
53
|
+
const toolsRun = [];
|
|
54
|
+
const toolsFailed = [];
|
|
55
|
+
const toolsSkipped = [];
|
|
56
|
+
let totalOutputSize = 0;
|
|
57
|
+
// Step 3: Run each tool
|
|
58
|
+
for (const tool of tools) {
|
|
59
|
+
const cmd = buildCommandFromDefinition(tool, filePath);
|
|
60
|
+
// Use the greater of user-specified timeout and tool's own timeout
|
|
61
|
+
const effectiveTimeout = Math.max(perToolTimeout, (tool.timeout ?? 60) * 1000);
|
|
62
|
+
try {
|
|
63
|
+
const result = await connector.executeShell(cmd, {
|
|
64
|
+
timeout: effectiveTimeout,
|
|
65
|
+
cwd: config.samplesDir,
|
|
66
|
+
});
|
|
67
|
+
let stderr = result.stderr || "";
|
|
68
|
+
// Filter Volatility 3 progress bar noise from stderr
|
|
69
|
+
stderr = stderr.replace(/^Progress:\s+[\d.]+\s+.*$/gm, "").trim();
|
|
70
|
+
// Detect missing tools by exit code or error messages
|
|
71
|
+
if (result.exitCode === 127 || stderr.includes("not found") || stderr.includes("No such file or directory")) {
|
|
72
|
+
toolsSkipped.push({ name: tool.name, command: cmd, reason: "Tool not installed" });
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
let output = result.stdout || stderr || "(no output)";
|
|
76
|
+
const fullLen = output.length;
|
|
77
|
+
// Per-tool budget, further reduced if approaching total response budget
|
|
78
|
+
const remainingBudget = Math.max(5 * 1024, TOTAL_RESPONSE_BUDGET - totalOutputSize);
|
|
79
|
+
const budget = Math.min(TOOL_OUTPUT_BUDGETS[tool.name] ?? DEFAULT_OUTPUT_BUDGET, remainingBudget);
|
|
80
|
+
const outputTruncated = output.length > budget;
|
|
81
|
+
let savedOutputFile;
|
|
82
|
+
if (outputTruncated) {
|
|
83
|
+
// Save full output to output dir for later retrieval
|
|
84
|
+
const safeFile = args.file.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
85
|
+
const outFilename = `${tool.name}-${safeFile}.txt`;
|
|
86
|
+
try {
|
|
87
|
+
const outPath = `${config.outputDir}/${outFilename}`;
|
|
88
|
+
await connector.writeFile(outPath, Buffer.from(output, "utf-8"));
|
|
89
|
+
savedOutputFile = outFilename;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Non-fatal: truncation hint won't include file reference
|
|
93
|
+
}
|
|
94
|
+
output = output.slice(0, budget) +
|
|
95
|
+
`\n\n[Truncated at ${Math.round(budget / 1024)}KB of ${Math.round(fullLen / 1024)}KB total` +
|
|
96
|
+
(savedOutputFile ? `. Full output: output/${savedOutputFile} — use download_file to retrieve]` : "]");
|
|
97
|
+
}
|
|
98
|
+
totalOutputSize += output.length;
|
|
99
|
+
const parsed = parseToolOutput(tool.name, output);
|
|
100
|
+
// capa exit code 14 = packed file detected
|
|
101
|
+
const extraMetadata = {};
|
|
102
|
+
if ((tool.name === "capa" || tool.name === "capa-json") && result.exitCode === 14) {
|
|
103
|
+
extraMetadata.analyst_note = "capa detected a packed file — capabilities analysis may be incomplete. Consider unpacking first.";
|
|
104
|
+
}
|
|
105
|
+
toolsRun.push({
|
|
106
|
+
name: tool.name,
|
|
107
|
+
command: cmd,
|
|
108
|
+
output,
|
|
109
|
+
exit_code: result.exitCode,
|
|
110
|
+
...(outputTruncated && { truncated: true, full_output_length: fullLen }),
|
|
111
|
+
...(parsed.parsed && {
|
|
112
|
+
findings: parsed.findings,
|
|
113
|
+
metadata: { ...parsed.metadata, ...extraMetadata },
|
|
114
|
+
}),
|
|
115
|
+
...(!parsed.parsed && Object.keys(extraMetadata).length > 0 && { metadata: extraMetadata }),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
const msg = error instanceof Error ? error.message : "Unknown error";
|
|
120
|
+
if (msg.toLowerCase().includes("timeout")) {
|
|
121
|
+
toolsFailed.push({ name: tool.name, command: cmd, error: "Timed out" });
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
toolsFailed.push({ name: tool.name, command: cmd, error: msg });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const combinedOutput = toolsRun.map(t => t.output).join("\n\n");
|
|
129
|
+
const iocResult = extractIOCs(combinedOutput);
|
|
130
|
+
return formatResponse("analyze_file", {
|
|
131
|
+
file: args.file,
|
|
132
|
+
detected_type: fileOutput,
|
|
133
|
+
matched_category: category.name,
|
|
134
|
+
depth,
|
|
135
|
+
...(tools.length === 0 && {
|
|
136
|
+
warning: `No tools registered for category "${category.name}" at depth "${depth}". Try depth "deep" or use run_tool directly.`,
|
|
137
|
+
}),
|
|
138
|
+
iocs: iocResult.iocs,
|
|
139
|
+
ioc_summary: iocResult.summary,
|
|
140
|
+
tools_run: toolsRun,
|
|
141
|
+
tools_failed: toolsFailed,
|
|
142
|
+
tools_skipped: toolsSkipped,
|
|
143
|
+
}, startTime);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
return formatError("analyze_file", toREMnuxError(error), startTime);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=analyze-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-file.js","sourceRoot":"","sources":["../../src/handlers/analyze-file.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE3E,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAelD,MAAM,qBAAqB,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,eAAe;AACxD,MAAM,qBAAqB,GAAG,GAAG,GAAG,IAAI,CAAC,CAAC,+BAA+B;AAEzE,wFAAwF;AACxF,MAAM,mBAAmB,GAA2B;IAClD,KAAK,EAAE,EAAE,GAAG,IAAI;IAChB,QAAQ,EAAE,EAAE,GAAG,IAAI;IACnB,OAAO,EAAE,EAAE,GAAG,IAAI;IAClB,MAAM,EAAE,EAAE,GAAG,IAAI;IACjB,OAAO,EAAE,EAAE,GAAG,IAAI;IAClB,QAAQ,EAAE,EAAE,GAAG,IAAI;IACnB,OAAO,EAAE,EAAE,GAAG,IAAI;CACnB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAiB,EACjB,IAAqB;IAErB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,IAAI,CAAC;QACL,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QACnC,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,UAAU,CAAc,CAAC;QAEtD,6CAA6C;QAC7C,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YACtB,MAAM,UAAU,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;YAClE,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;gBACrB,OAAO,WAAW,CAAC,cAAc,EAAE,IAAI,WAAW,CAChD,UAAU,CAAC,KAAK,IAAI,mBAAmB,EACvC,cAAc,EACd,YAAY,EACZ,kDAAkD,CACnD,EAAE,SAAS,CAAC,CAAC;YAChB,CAAC;QACH,CAAC;QAED,MAAM,QAAQ,GAAG,GAAG,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACrD,MAAM,cAAc,GAAG,CAAC,IAAI,CAAC,gBAAgB,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;QAE5D,2BAA2B;QAC3B,IAAI,UAAkB,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YAC/E,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;YACzC,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,OAAO,WAAW,CAAC,cAAc,EAAE,IAAI,WAAW,CAChD,qDAAqD,EACrD,cAAc,EACd,cAAc,EACd,4CAA4C,CAC7C,EAAE,SAAS,CAAC,CAAC;YAChB,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,GAAG,GAAG,+BAA+B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC;YACtG,OAAO,WAAW,CAAC,cAAc,EAAE,IAAI,WAAW,CAChD,GAAG,EACH,cAAc,EACd,cAAc,EACd,4CAA4C,CAC7C,EAAE,SAAS,CAAC,CAAC;QAChB,CAAC;QAED,uEAAuE;QACvE,MAAM,QAAQ,GAAG,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACtD,MAAM,GAAG,GAAG,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC;QAC1D,MAAM,KAAK,GAAG,YAAY,CAAC,YAAY,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAEpD,MAAM,QAAQ,GAAc,EAAE,CAAC;QAC/B,MAAM,WAAW,GAAiB,EAAE,CAAC;QACrC,MAAM,YAAY,GAAkB,EAAE,CAAC;QACvC,IAAI,eAAe,GAAG,CAAC,CAAC;QAExB,wBAAwB;QACxB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,GAAG,GAAG,0BAA0B,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YACvD,mEAAmE;YACnE,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;YAE/E,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,GAAG,EAAE;oBAC/C,OAAO,EAAE,gBAAgB;oBACzB,GAAG,EAAE,MAAM,CAAC,UAAU;iBACvB,CAAC,CAAC;gBAEH,IAAI,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;gBACjC,qDAAqD;gBACrD,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,6BAA6B,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBAClE,sDAAsD;gBACtD,IAAI,MAAM,CAAC,QAAQ,KAAK,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,2BAA2B,CAAC,EAAE,CAAC;oBAC5G,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC,CAAC;oBACnF,SAAS;gBACX,CAAC;gBAED,IAAI,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,IAAI,aAAa,CAAC;gBACtD,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC;gBAC9B,wEAAwE;gBACxE,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,eAAe,CAAC,CAAC;gBACpF,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,qBAAqB,EAAE,eAAe,CAAC,CAAC;gBAClG,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;gBAC/C,IAAI,eAAmC,CAAC;gBACxC,IAAI,eAAe,EAAE,CAAC;oBACpB,qDAAqD;oBACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;oBAC5D,MAAM,WAAW,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,QAAQ,MAAM,CAAC;oBACnD,IAAI,CAAC;wBACH,MAAM,OAAO,GAAG,GAAG,MAAM,CAAC,SAAS,IAAI,WAAW,EAAE,CAAC;wBACrD,MAAM,SAAS,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;wBACjE,eAAe,GAAG,WAAW,CAAC;oBAChC,CAAC;oBAAC,MAAM,CAAC;wBACP,0DAA0D;oBAC5D,CAAC;oBACD,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC;wBAC9B,qBAAqB,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,UAAU;wBAC3F,CAAC,eAAe,CAAC,CAAC,CAAC,yBAAyB,eAAe,mCAAmC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;gBAC1G,CAAC;gBAED,eAAe,IAAI,MAAM,CAAC,MAAM,CAAC;gBAEjC,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBAElD,2CAA2C;gBAC3C,MAAM,aAAa,GAA4B,EAAE,CAAC;gBAClD,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,CAAC,IAAI,MAAM,CAAC,QAAQ,KAAK,EAAE,EAAE,CAAC;oBAClF,aAAa,CAAC,YAAY,GAAG,kGAAkG,CAAC;gBAClI,CAAC;gBAED,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,OAAO,EAAE,GAAG;oBACZ,MAAM;oBACN,SAAS,EAAE,MAAM,CAAC,QAAQ;oBAC1B,GAAG,CAAC,eAAe,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,CAAC;oBACxE,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI;wBACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;wBACzB,QAAQ,EAAE,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,GAAG,aAAa,EAAE;qBACnD,CAAC;oBACF,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;iBAC5F,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;gBACrE,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC1C,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;gBAC1E,CAAC;qBAAM,CAAC;oBACN,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;gBAClE,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,cAAc,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAChE,MAAM,SAAS,GAAG,WAAW,CAAC,cAAc,CAAC,CAAC;QAE9C,OAAO,cAAc,CAAC,cAAc,EAAE;YACpC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,aAAa,EAAE,UAAU;YACzB,gBAAgB,EAAE,QAAQ,CAAC,IAAI;YAC/B,KAAK;YACL,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI;gBACxB,OAAO,EAAE,qCAAqC,QAAQ,CAAC,IAAI,eAAe,KAAK,+CAA+C;aAC/H,CAAC;YACF,IAAI,EAAE,SAAS,CAAC,IAAI;YACpB,WAAW,EAAE,SAAS,CAAC,OAAO;YAC9B,SAAS,EAAE,QAAQ;YACnB,YAAY,EAAE,WAAW;YACzB,aAAa,EAAE,YAAY;SAC5B,EAAE,SAAS,CAAC,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,WAAW,CAAC,cAAc,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,CAAC;IACtE,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-tools.d.ts","sourceRoot":"","sources":["../../src/handlers/check-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAK9C,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,WAAW;;;;;;GA+CvD"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { toolRegistry } from "../tools/registry.js";
|
|
2
|
+
import { formatResponse, formatError } from "../response.js";
|
|
3
|
+
import { toREMnuxError } from "../errors/error-mapper.js";
|
|
4
|
+
export async function handleCheckTools(deps) {
|
|
5
|
+
const startTime = Date.now();
|
|
6
|
+
const { connector } = deps;
|
|
7
|
+
// Collect unique command names from the tool registry
|
|
8
|
+
const toolNames = new Set();
|
|
9
|
+
for (const def of toolRegistry.all()) {
|
|
10
|
+
// Extract the command name (first word)
|
|
11
|
+
const cmdName = def.command.split(/\s/)[0];
|
|
12
|
+
toolNames.add(cmdName);
|
|
13
|
+
}
|
|
14
|
+
const tools = [];
|
|
15
|
+
// Verify container connectivity before checking individual tools
|
|
16
|
+
try {
|
|
17
|
+
await connector.execute(["true"], { timeout: 5000 });
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
return formatError("check_tools", toREMnuxError(error), startTime);
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const results = await Promise.all([...toolNames].map(async (name) => {
|
|
24
|
+
try {
|
|
25
|
+
const result = await connector.execute(["which", name], { timeout: 5000 });
|
|
26
|
+
if (result.exitCode === 0 && result.stdout?.trim()) {
|
|
27
|
+
return { tool: name, available: true, path: result.stdout.trim() };
|
|
28
|
+
}
|
|
29
|
+
return { tool: name, available: false };
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return { tool: name, available: false };
|
|
33
|
+
}
|
|
34
|
+
}));
|
|
35
|
+
tools.push(...results);
|
|
36
|
+
const available = tools.filter(t => t.available).length;
|
|
37
|
+
const missing = tools.filter(t => !t.available).length;
|
|
38
|
+
return formatResponse("check_tools", {
|
|
39
|
+
summary: { total: tools.length, available, missing },
|
|
40
|
+
tools: tools.sort((a, b) => a.tool.localeCompare(b.tool)),
|
|
41
|
+
}, startTime);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
return formatError("check_tools", toREMnuxError(error), startTime);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=check-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-tools.js","sourceRoot":"","sources":["../../src/handlers/check-tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { HandlerDeps } from "./types.js";
|
|
2
|
+
import type { DownloadFileArgs } from "../schemas/tools.js";
|
|
3
|
+
export declare function handleDownloadFile(deps: HandlerDeps, args: DownloadFileArgs): Promise<{
|
|
4
|
+
content: Array<{
|
|
5
|
+
type: "text";
|
|
6
|
+
text: string;
|
|
7
|
+
}>;
|
|
8
|
+
isError?: boolean;
|
|
9
|
+
}>;
|
|
10
|
+
//# sourceMappingURL=download-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download-file.d.ts","sourceRoot":"","sources":["../../src/handlers/download-file.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAiC5D,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,gBAAgB;;;;;;GA0IvB"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { existsSync, statSync } from "fs";
|
|
2
|
+
import { join, basename } from "path";
|
|
3
|
+
import { validateFilePath } from "../security/blocklist.js";
|
|
4
|
+
import { validateHostPath } from "../file-upload.js";
|
|
5
|
+
import { formatResponse, formatError } from "../response.js";
|
|
6
|
+
import { REMnuxError } from "../errors/remnux-error.js";
|
|
7
|
+
import { toREMnuxError } from "../errors/error-mapper.js";
|
|
8
|
+
import { DEFAULT_ARCHIVE_PASSWORD, DEFAULT_ARCHIVE_FORMAT } from "../state/session.js";
|
|
9
|
+
const MAX_DOWNLOAD_SIZE = 200 * 1024 * 1024; // 200MB
|
|
10
|
+
/**
|
|
11
|
+
* Build the command to create a password-protected archive inside REMnux.
|
|
12
|
+
*/
|
|
13
|
+
function getArchiveCommand(format, archivePath, sourcePath, password) {
|
|
14
|
+
switch (format) {
|
|
15
|
+
case "zip":
|
|
16
|
+
return ["zip", "-j", "-P", password, archivePath, sourcePath];
|
|
17
|
+
case "7z":
|
|
18
|
+
return ["7z", "a", `-p${password}`, "-mhe=on", archivePath, sourcePath];
|
|
19
|
+
case "rar":
|
|
20
|
+
return ["rar", "a", `-p${password}`, "-hp", archivePath, sourcePath];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function archiveExtension(format) {
|
|
24
|
+
return `.${format}`;
|
|
25
|
+
}
|
|
26
|
+
export async function handleDownloadFile(deps, args) {
|
|
27
|
+
const startTime = Date.now();
|
|
28
|
+
const { connector, config } = deps;
|
|
29
|
+
const shouldArchive = args.archive !== false;
|
|
30
|
+
// Validate file path (skip unless --sandbox)
|
|
31
|
+
if (!config.noSandbox) {
|
|
32
|
+
const validation = validateFilePath(args.file_path, config.outputDir);
|
|
33
|
+
if (!validation.safe) {
|
|
34
|
+
return formatError("download_file", new REMnuxError(validation.error || "Invalid file path", "INVALID_PATH", "validation", "Use a relative path within the output directory"), startTime);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Validate outputPath
|
|
38
|
+
const pathValidation = validateHostPath(args.output_path);
|
|
39
|
+
if (!pathValidation.valid) {
|
|
40
|
+
return formatError("download_file", new REMnuxError(pathValidation.error || "Invalid output path", "INVALID_PATH", "validation", "Provide an absolute path to a directory on the host filesystem"), startTime);
|
|
41
|
+
}
|
|
42
|
+
// Verify output directory exists and is a directory
|
|
43
|
+
if (!existsSync(args.output_path) || !statSync(args.output_path).isDirectory()) {
|
|
44
|
+
return formatError("download_file", new REMnuxError(`Output path does not exist or is not a directory: ${args.output_path}`, "INVALID_PATH", "validation", "Provide an absolute path to an existing directory"), startTime);
|
|
45
|
+
}
|
|
46
|
+
const fullPath = `${config.outputDir}/${args.file_path}`;
|
|
47
|
+
try {
|
|
48
|
+
// Get file size and hash (separate calls to avoid shell interpolation)
|
|
49
|
+
const statResult = await connector.execute(["stat", "-c", "%s", fullPath], { timeout: 30000 });
|
|
50
|
+
const hashResult = await connector.execute(["sha256sum", fullPath], { timeout: 30000 });
|
|
51
|
+
const sizeBytes = parseInt((statResult.stdout || "0").trim(), 10);
|
|
52
|
+
// Guard against oversized downloads
|
|
53
|
+
if (sizeBytes > MAX_DOWNLOAD_SIZE) {
|
|
54
|
+
return formatError("download_file", new REMnuxError(`File exceeds ${MAX_DOWNLOAD_SIZE / 1024 / 1024}MB download limit (got ${(sizeBytes / 1024 / 1024).toFixed(2)}MB)`, "FILE_TOO_LARGE", "validation", "Use run_tool with 'split' to break the file into smaller parts first"), startTime);
|
|
55
|
+
}
|
|
56
|
+
const sha256 = (hashResult.stdout || "").trim().split(/\s+/)[0] || "unknown";
|
|
57
|
+
const filename = basename(args.file_path);
|
|
58
|
+
if (shouldArchive) {
|
|
59
|
+
// Determine archive format and password from session state
|
|
60
|
+
const archiveMeta = deps.sessionState.getArchiveInfo(filename);
|
|
61
|
+
const archiveFormat = archiveMeta?.format ?? DEFAULT_ARCHIVE_FORMAT;
|
|
62
|
+
const archivePassword = archiveMeta?.password ?? DEFAULT_ARCHIVE_PASSWORD;
|
|
63
|
+
// Defense-in-depth: reject passwords with shell metacharacters
|
|
64
|
+
if (/[;&|`$\n\r'"\\]/.test(archivePassword)) {
|
|
65
|
+
return formatError("download_file", new REMnuxError("Archive password contains unsafe characters", "INVALID_PASSWORD", "validation", "Try downloading with archive: false"), startTime);
|
|
66
|
+
}
|
|
67
|
+
// Create temp archive path inside REMnux
|
|
68
|
+
const timestamp = Date.now();
|
|
69
|
+
const archiveName = `${filename}${archiveExtension(archiveFormat)}`;
|
|
70
|
+
const remoteTmpArchive = `/tmp/dl_${timestamp}_${archiveName}`;
|
|
71
|
+
// Create password-protected archive
|
|
72
|
+
const archiveCmd = getArchiveCommand(archiveFormat, remoteTmpArchive, fullPath, archivePassword);
|
|
73
|
+
const archiveResult = await connector.execute(archiveCmd, {
|
|
74
|
+
timeout: Math.max(60000, sizeBytes / 1024),
|
|
75
|
+
});
|
|
76
|
+
if (archiveResult.exitCode !== 0) {
|
|
77
|
+
return formatError("download_file", new REMnuxError(`Failed to create archive: ${archiveResult.stderr || archiveResult.stdout}`, "ARCHIVE_FAILED", "tool_failure", "Try downloading with archive: false"), startTime);
|
|
78
|
+
}
|
|
79
|
+
// Transfer archive to host
|
|
80
|
+
const hostPath = join(args.output_path, archiveName);
|
|
81
|
+
try {
|
|
82
|
+
await connector.readFileToPath(remoteTmpArchive, hostPath);
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
// Clean up temp archive inside REMnux
|
|
86
|
+
await connector.execute(["rm", "-f", remoteTmpArchive], { timeout: 10000 }).catch(() => { });
|
|
87
|
+
}
|
|
88
|
+
return formatResponse("download_file", {
|
|
89
|
+
file_path: args.file_path,
|
|
90
|
+
size_bytes: sizeBytes,
|
|
91
|
+
sha256,
|
|
92
|
+
host_path: hostPath,
|
|
93
|
+
archived: true,
|
|
94
|
+
archive_format: archiveFormat,
|
|
95
|
+
archive_password: archivePassword,
|
|
96
|
+
}, startTime);
|
|
97
|
+
}
|
|
98
|
+
// No archiving — transfer raw file
|
|
99
|
+
const hostPath = join(args.output_path, filename);
|
|
100
|
+
await connector.readFileToPath(fullPath, hostPath);
|
|
101
|
+
return formatResponse("download_file", {
|
|
102
|
+
file_path: args.file_path,
|
|
103
|
+
size_bytes: sizeBytes,
|
|
104
|
+
sha256,
|
|
105
|
+
host_path: hostPath,
|
|
106
|
+
archived: false,
|
|
107
|
+
}, startTime);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
return formatError("download_file", toREMnuxError(error), startTime);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=download-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download-file.js","sourceRoot":"","sources":["../../src/handlers/download-file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { HandlerDeps } from "./types.js";
|
|
2
|
+
import type { DownloadFromUrlArgs } from "../schemas/tools.js";
|
|
3
|
+
/**
|
|
4
|
+
* Validate a URL for download: must be http(s), no control chars, no single quotes.
|
|
5
|
+
*/
|
|
6
|
+
export declare function validateUrl(url: string): {
|
|
7
|
+
valid: boolean;
|
|
8
|
+
error?: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Validate a single HTTP header string.
|
|
12
|
+
* Must be "Name: value" with no injection characters.
|
|
13
|
+
*/
|
|
14
|
+
export declare function validateHeader(header: string): {
|
|
15
|
+
valid: boolean;
|
|
16
|
+
error?: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Derive a filename from a URL path.
|
|
20
|
+
* Falls back to "downloaded_sample" if no basename can be extracted.
|
|
21
|
+
*/
|
|
22
|
+
export declare function deriveFilename(url: string): string;
|
|
23
|
+
export declare function handleDownloadFromUrl(deps: HandlerDeps, args: DownloadFromUrlArgs): Promise<{
|
|
24
|
+
content: Array<{
|
|
25
|
+
type: "text";
|
|
26
|
+
text: string;
|
|
27
|
+
}>;
|
|
28
|
+
isError?: boolean;
|
|
29
|
+
}>;
|
|
30
|
+
//# sourceMappingURL=download-from-url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download-from-url.d.ts","sourceRoot":"","sources":["../../src/handlers/download-from-url.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AA4B/D;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAkB3E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAYjF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAkBlD;AAED,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,mBAAmB;;;;;;GA0H1B"}
|