This commit is contained in:
KingKDot
2026-08-03 23:29:16 -05:00
parent 5693409c93
commit f2aae81792
17 changed files with 930 additions and 3 deletions
@@ -214,6 +214,7 @@ export const COMMAND_TYPES = [
"file_search",
"file_thumb",
"file_upload",
"file_upload_desktop",
"file_upload_http",
"file_write",
"file_zip",
@@ -602,6 +603,10 @@ export const COMMAND_VERSION_SUPPORT = {
"min": 1,
"max": 1
},
"file_upload_desktop": {
"min": 1,
"max": 1
},
"file_upload_http": {
"min": 1,
"max": 1
@@ -17,9 +17,10 @@ import {
describe("generated wire protocol contract", () => {
test("publishes the complete command catalog", () => {
expect(WIRE_PROTOCOL_VERSION).toBe(1);
expect(COMMAND_TYPES.length).toBe(142);
expect(COMMAND_TYPES.length).toBe(143);
expect(new Set(COMMAND_TYPES).size).toBe(COMMAND_TYPES.length);
expect(isCommandType("desktop_start")).toBe(true);
expect(isCommandType("file_upload_desktop")).toBe(true);
expect(isCommandType("virtual_window_list")).toBe(true);
expect(isCommandType("not_a_command")).toBe(false);
});
@@ -35,4 +35,19 @@ describe("file browser command payload validation", () => {
path: "C:\\ok.bin", url: "https://attacker.invalid/payload", total: 10,
})).toBeNull();
});
test("validates remote desktop uploads as safe basenames", () => {
const valid = {
fileName: "report.pdf",
url: "/api/file/upload/pull/123e4567-e89b-42d3-a456-426614174000",
total: 10,
};
expect(validateFileBrowserCommandPayload("file_upload_desktop", valid)).not.toBeNull();
expect(validateFileBrowserCommandPayload("file_upload_desktop", { ...valid, display: 1, x: 640, y: 360 })).not.toBeNull();
expect(validateFileBrowserCommandPayload("file_upload_desktop", { ...valid, x: 640 })).toBeNull();
expect(validateFileBrowserCommandPayload("file_upload_desktop", { ...valid, x: 640.5, y: 360 })).toBeNull();
expect(validateFileBrowserCommandPayload("file_upload_desktop", { ...valid, fileName: "../report.pdf" })).toBeNull();
expect(validateFileBrowserCommandPayload("file_upload_desktop", { ...valid, fileName: "folder/report.pdf" })).toBeNull();
expect(validateFileBrowserCommandPayload("file_upload_desktop", { ...valid, url: "https://attacker.invalid/file" })).toBeNull();
});
});
@@ -80,6 +80,17 @@ export function validateFileBrowserCommandPayload(
|| !/^\/api\/file\/upload\/pull\/[0-9a-f-]{36}$/i.test(payload.url)) return null;
if (!Number.isSafeInteger(Number(payload.total)) || Number(payload.total) < 0) return null;
return payload;
case "file_upload_desktop":
if (!safeString(payload.fileName, 255) || /[\\/]/.test(payload.fileName as string)
|| payload.fileName === "." || payload.fileName === ".."
|| typeof payload.url !== "string"
|| !/^\/api\/file\/upload\/pull\/[0-9a-f-]{36}$/i.test(payload.url)) return null;
if (!Number.isSafeInteger(Number(payload.total)) || Number(payload.total) < 0) return null;
if ((payload.x === undefined) !== (payload.y === undefined)) return null;
if (payload.x !== undefined && (!Number.isSafeInteger(Number(payload.x)) || Math.abs(Number(payload.x)) > 1_000_000)) return null;
if (payload.y !== undefined && (!Number.isSafeInteger(Number(payload.y)) || Math.abs(Number(payload.y)) > 1_000_000)) return null;
if (payload.display !== undefined && (!Number.isSafeInteger(Number(payload.display)) || Number(payload.display) < 0 || Number(payload.display) > 128)) return null;
return payload;
default:
return null;
}
@@ -4,6 +4,7 @@ import { AuditAction, logAudit } from "../auditLog";
import * as clientManager from "../clientManager";
import { logger } from "../logger";
import { metrics } from "../metrics";
import { versionCommandForClient } from "../command-compatibility";
import { encodeMessage } from "../protocol";
import { getSessionByTokenHash } from "../db";
import * as sessionManager from "../sessions/sessionManager";
@@ -251,6 +252,35 @@ export function handleFileBrowserViewerMessage(ws: ServerWebSocket<SocketData>,
success: true,
});
break;
case "file_upload_desktop":
try {
target.ws.send(encodeMessage(versionCommandForClient(target, {
type: "command",
commandType: "file_upload_desktop",
id: routedId,
payload: actualPayload,
} as any)));
} catch (error) {
finishFileBrowserCommand(routedId);
safeSendViewer(ws, {
type: "command_result",
commandId: routedId,
ok: false,
message: error instanceof Error ? error.message : "Desktop upload is unsupported by this client",
});
break;
}
metrics.recordCommand("file_upload_desktop");
logAudit({
timestamp: Date.now(),
username: (ws.data as any).username || "unknown",
ip: ws.data.ip || "unknown",
action: AuditAction.FILE_UPLOAD,
targetClientId: clientId,
details: JSON.stringify({ fileName: actualPayload.fileName || "", destination: "remote_pointer", display: actualPayload.display, x: actualPayload.x, y: actualPayload.y, mode: "http_pull" }),
success: true,
});
break;
default:
break;
}