wire protocol is generated now instead of being hard coded with versioning for commands.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "overlord-server",
|
||||
"version": "2.6.1",
|
||||
"version": "3.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -20,6 +20,8 @@
|
||||
"watch:css": "tailwindcss -i ./src/styles.css -o ./public/assets/tailwind.css --watch=always",
|
||||
"minify": "bun run scripts/minify-public.ts",
|
||||
"bench:client-db": "bun run scripts/client-db-stress.ts",
|
||||
"protocol:generate": "bun ../scripts/generate-wire-protocol.ts",
|
||||
"protocol:check": "bun ../scripts/generate-wire-protocol.ts --check",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import {
|
||||
getCommandCompatibility,
|
||||
getNegotiatedCommandVersions,
|
||||
requireCommandVersion,
|
||||
sanitizeCommandVersionRanges,
|
||||
versionCommandForClient,
|
||||
} from "./command-compatibility";
|
||||
|
||||
describe("command version negotiation", () => {
|
||||
test("sanitizes advertised ranges and ignores unknown commands", () => {
|
||||
expect(sanitizeCommandVersionRanges({
|
||||
process_list: { min: 1, max: 3 },
|
||||
desktop_start: { min: 3, max: 2 },
|
||||
not_a_command: { min: 1, max: 1 },
|
||||
})).toEqual({
|
||||
process_list: { min: 1, max: 3 },
|
||||
});
|
||||
});
|
||||
|
||||
test("treats pre-3.0 agents as v1 for backward compatibility", () => {
|
||||
expect(getCommandCompatibility({}, "process_list")).toEqual({
|
||||
supported: true,
|
||||
version: 1,
|
||||
server: { min: 1, max: 1 },
|
||||
agent: { min: 1, max: 1 },
|
||||
legacyAssumption: true,
|
||||
});
|
||||
});
|
||||
|
||||
test("selects the highest shared version", () => {
|
||||
const client = {
|
||||
commandVersions: {
|
||||
process_list: { min: 1, max: 3 },
|
||||
},
|
||||
};
|
||||
expect(requireCommandVersion(client, "process_list")).toBe(1);
|
||||
expect(getNegotiatedCommandVersions(client)).toEqual({ process_list: 1 });
|
||||
expect(versionCommandForClient(client, {
|
||||
type: "command",
|
||||
commandType: "process_list",
|
||||
id: "command-123",
|
||||
})).toMatchObject({
|
||||
commandType: "process_list",
|
||||
commandVersion: 1,
|
||||
});
|
||||
});
|
||||
|
||||
test("strictly reports missing commands and non-overlapping versions", () => {
|
||||
expect(getCommandCompatibility(
|
||||
{ commandVersions: {} },
|
||||
"process_list",
|
||||
)).toMatchObject({
|
||||
supported: false,
|
||||
reason: "agent_missing_command",
|
||||
});
|
||||
|
||||
const incompatible = {
|
||||
commandVersions: {
|
||||
process_list: { min: 2, max: 3 },
|
||||
},
|
||||
};
|
||||
expect(getCommandCompatibility(incompatible, "process_list")).toMatchObject({
|
||||
supported: false,
|
||||
reason: "no_common_version",
|
||||
});
|
||||
expect(() => requireCommandVersion(incompatible, "process_list")).toThrow(
|
||||
"no shared version",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,120 @@
|
||||
import {
|
||||
COMMAND_TYPES,
|
||||
COMMAND_VERSION_SUPPORT,
|
||||
isCommandType,
|
||||
type CommandType,
|
||||
type CommandVersionRange,
|
||||
} from "./generated/wire-contract";
|
||||
import type { ClientInfo } from "./types";
|
||||
import type { Command } from "./protocol";
|
||||
|
||||
export type CommandCompatibility = {
|
||||
supported: boolean;
|
||||
version?: number;
|
||||
server: CommandVersionRange;
|
||||
agent?: CommandVersionRange;
|
||||
reason?: "agent_missing_command" | "no_common_version";
|
||||
legacyAssumption?: boolean;
|
||||
};
|
||||
|
||||
function cleanPositiveInt(value: unknown): number | undefined {
|
||||
if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
|
||||
const result = Math.floor(value);
|
||||
return result > 0 && result <= 65_535 ? result : undefined;
|
||||
}
|
||||
|
||||
export function sanitizeCommandVersionRanges(
|
||||
value: unknown,
|
||||
): Partial<Record<CommandType, CommandVersionRange>> | undefined {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
|
||||
const result: Partial<Record<CommandType, CommandVersionRange>> = {};
|
||||
let count = 0;
|
||||
for (const [name, rawRange] of Object.entries(value as Record<string, unknown>)) {
|
||||
if (count >= COMMAND_TYPES.length || !isCommandType(name)) continue;
|
||||
if (!rawRange || typeof rawRange !== "object" || Array.isArray(rawRange)) continue;
|
||||
const min = cleanPositiveInt((rawRange as any).min);
|
||||
const max = cleanPositiveInt((rawRange as any).max);
|
||||
if (min === undefined || max === undefined || min > max) continue;
|
||||
result[name] = { min, max };
|
||||
count += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getCommandCompatibility(
|
||||
client: Pick<ClientInfo, "protocolVersion" | "commandVersions">,
|
||||
command: CommandType,
|
||||
): CommandCompatibility {
|
||||
const server = COMMAND_VERSION_SUPPORT[command];
|
||||
const advertised = client.commandVersions;
|
||||
|
||||
// Pre-3.0 agents did not advertise a catalog. They remain v1 best-effort.
|
||||
if (!advertised) {
|
||||
const supported = server.min <= 1 && server.max >= 1;
|
||||
return supported
|
||||
? { supported: true, version: 1, server, agent: { min: 1, max: 1 }, legacyAssumption: true }
|
||||
: { supported: false, server, agent: { min: 1, max: 1 }, reason: "no_common_version", legacyAssumption: true };
|
||||
}
|
||||
|
||||
const agent = advertised[command];
|
||||
if (!agent) {
|
||||
return { supported: false, server, reason: "agent_missing_command" };
|
||||
}
|
||||
|
||||
const min = Math.max(server.min, agent.min);
|
||||
const max = Math.min(server.max, agent.max);
|
||||
if (min > max) {
|
||||
return { supported: false, server, agent, reason: "no_common_version" };
|
||||
}
|
||||
return { supported: true, version: max, server, agent };
|
||||
}
|
||||
|
||||
export function getCommandCompatibilityCatalog(
|
||||
client: Pick<ClientInfo, "protocolVersion" | "commandVersions">,
|
||||
): Record<CommandType, CommandCompatibility> {
|
||||
return Object.fromEntries(
|
||||
COMMAND_TYPES.map((command) => [command, getCommandCompatibility(client, command)]),
|
||||
) as Record<CommandType, CommandCompatibility>;
|
||||
}
|
||||
|
||||
export function getNegotiatedCommandVersions(
|
||||
client: Pick<ClientInfo, "protocolVersion" | "commandVersions">,
|
||||
): Partial<Record<CommandType, number>> {
|
||||
const result: Partial<Record<CommandType, number>> = {};
|
||||
for (const command of COMMAND_TYPES) {
|
||||
const compatibility = getCommandCompatibility(client, command);
|
||||
if (compatibility.supported && compatibility.version !== undefined) {
|
||||
result[command] = compatibility.version;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function requireCommandVersion(
|
||||
client: Pick<ClientInfo, "protocolVersion" | "commandVersions">,
|
||||
command: CommandType,
|
||||
): number {
|
||||
const compatibility = getCommandCompatibility(client, command);
|
||||
if (!compatibility.supported || compatibility.version === undefined) {
|
||||
const reason = compatibility.reason === "agent_missing_command"
|
||||
? "agent does not advertise this command"
|
||||
: `no shared version (server ${compatibility.server.min}-${compatibility.server.max}, agent ${compatibility.agent?.min ?? "none"}-${compatibility.agent?.max ?? "none"})`;
|
||||
throw new Error(`Command ${command} is unsupported: ${reason}`);
|
||||
}
|
||||
return compatibility.version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this at target-aware send boundaries. It fails before transmission when
|
||||
* the connected agent does not support the command and pins the negotiated
|
||||
* version into the envelope.
|
||||
*/
|
||||
export function versionCommandForClient(
|
||||
client: Pick<ClientInfo, "protocolVersion" | "commandVersions">,
|
||||
command: Command,
|
||||
): Command {
|
||||
return {
|
||||
...command,
|
||||
commandVersion: requireCommandVersion(client, command.commandType),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,879 @@
|
||||
// Code generated by scripts/generate-wire-protocol.ts; DO NOT EDIT.
|
||||
|
||||
export const WIRE_SCHEMA_VERSION = 1 as const;
|
||||
export const WIRE_PROTOCOL_VERSION = 1 as const;
|
||||
|
||||
export const WIRE_MESSAGE_TYPES = [
|
||||
"backstage_browser_check_result",
|
||||
"backstage_browser_launch_status",
|
||||
"backstage_clone_progress",
|
||||
"backstage_dxgi_status",
|
||||
"backstage_installed_apps_result",
|
||||
"backstage_lookup_result",
|
||||
"backstage_window_list_result",
|
||||
"client_logs_result",
|
||||
"clipboard_content",
|
||||
"command",
|
||||
"command_abort",
|
||||
"command_progress",
|
||||
"command_result",
|
||||
"console_output",
|
||||
"desktop_audio_uplink",
|
||||
"desktop_cursor",
|
||||
"desktop_encoder_capabilities",
|
||||
"desktop_stream_stats",
|
||||
"disconnect_info",
|
||||
"enrollment_challenge",
|
||||
"enrollment_status",
|
||||
"file_dirsize_result",
|
||||
"file_download",
|
||||
"file_hash_result",
|
||||
"file_icon_result",
|
||||
"file_list_result",
|
||||
"file_peek_result",
|
||||
"file_read_result",
|
||||
"file_search_result",
|
||||
"file_thumb_result",
|
||||
"file_upload_result",
|
||||
"frame",
|
||||
"frame_ack",
|
||||
"hello",
|
||||
"hello_ack",
|
||||
"keylog_clear_result",
|
||||
"keylog_delete_result",
|
||||
"keylog_file_content",
|
||||
"keylog_file_list",
|
||||
"keylog_permission_result",
|
||||
"notification",
|
||||
"notification_config",
|
||||
"ping",
|
||||
"plugin_event",
|
||||
"pong",
|
||||
"process_icon_result",
|
||||
"process_list_result",
|
||||
"proxy_close",
|
||||
"proxy_data",
|
||||
"screenshot_result",
|
||||
"script_result",
|
||||
"status",
|
||||
"voice_uplink",
|
||||
"webcam_devices",
|
||||
"webrtc_p2p_answer",
|
||||
"webrtc_p2p_ice"
|
||||
] as const;
|
||||
|
||||
export type MessageKind = (typeof WIRE_MESSAGE_TYPES)[number];
|
||||
|
||||
export const SERVER_TO_AGENT_MESSAGE_TYPES = [
|
||||
"command",
|
||||
"command_abort",
|
||||
"enrollment_challenge",
|
||||
"enrollment_status",
|
||||
"frame_ack",
|
||||
"hello_ack",
|
||||
"notification_config",
|
||||
"ping",
|
||||
"plugin_event",
|
||||
"pong"
|
||||
] as const;
|
||||
|
||||
export type ServerToAgentMessageType = (typeof SERVER_TO_AGENT_MESSAGE_TYPES)[number];
|
||||
|
||||
export const AGENT_TO_SERVER_MESSAGE_TYPES = [
|
||||
"backstage_browser_check_result",
|
||||
"backstage_browser_launch_status",
|
||||
"backstage_clone_progress",
|
||||
"backstage_dxgi_status",
|
||||
"backstage_installed_apps_result",
|
||||
"backstage_lookup_result",
|
||||
"backstage_window_list_result",
|
||||
"client_logs_result",
|
||||
"clipboard_content",
|
||||
"command_progress",
|
||||
"command_result",
|
||||
"console_output",
|
||||
"desktop_audio_uplink",
|
||||
"desktop_cursor",
|
||||
"desktop_encoder_capabilities",
|
||||
"desktop_stream_stats",
|
||||
"disconnect_info",
|
||||
"file_dirsize_result",
|
||||
"file_download",
|
||||
"file_hash_result",
|
||||
"file_icon_result",
|
||||
"file_list_result",
|
||||
"file_peek_result",
|
||||
"file_read_result",
|
||||
"file_search_result",
|
||||
"file_thumb_result",
|
||||
"file_upload_result",
|
||||
"frame",
|
||||
"hello",
|
||||
"keylog_clear_result",
|
||||
"keylog_delete_result",
|
||||
"keylog_file_content",
|
||||
"keylog_file_list",
|
||||
"keylog_permission_result",
|
||||
"notification",
|
||||
"ping",
|
||||
"plugin_event",
|
||||
"pong",
|
||||
"process_icon_result",
|
||||
"process_list_result",
|
||||
"proxy_close",
|
||||
"proxy_data",
|
||||
"screenshot_result",
|
||||
"script_result",
|
||||
"status",
|
||||
"voice_uplink",
|
||||
"webcam_devices",
|
||||
"webrtc_p2p_answer",
|
||||
"webrtc_p2p_ice"
|
||||
] as const;
|
||||
|
||||
export type AgentToServerMessageType = (typeof AGENT_TO_SERVER_MESSAGE_TYPES)[number];
|
||||
|
||||
export const COMMAND_TYPES = [
|
||||
"agent_update",
|
||||
"backstage_browser_check",
|
||||
"backstage_enable_cursor",
|
||||
"backstage_enable_dxgi",
|
||||
"backstage_enable_keyboard",
|
||||
"backstage_enable_mouse",
|
||||
"backstage_enable_uia",
|
||||
"backstage_installed_apps",
|
||||
"backstage_key_down",
|
||||
"backstage_key_up",
|
||||
"backstage_kill_all",
|
||||
"backstage_lookup",
|
||||
"backstage_mouse_down",
|
||||
"backstage_mouse_move",
|
||||
"backstage_mouse_up",
|
||||
"backstage_mouse_wheel",
|
||||
"backstage_request_keyframe",
|
||||
"backstage_select_display",
|
||||
"backstage_set_fps",
|
||||
"backstage_set_quality",
|
||||
"backstage_set_resolution",
|
||||
"backstage_start",
|
||||
"backstage_start_browser_injected",
|
||||
"backstage_start_chrome_injected",
|
||||
"backstage_start_process",
|
||||
"backstage_start_process_injected",
|
||||
"backstage_stop",
|
||||
"backstage_window_list",
|
||||
"client_logs_request",
|
||||
"clipboard_set",
|
||||
"clipboard_sync_start",
|
||||
"clipboard_sync_stop",
|
||||
"console_input",
|
||||
"console_resize",
|
||||
"console_start",
|
||||
"console_stop",
|
||||
"darwin_request_permissions",
|
||||
"desktop_audio_start",
|
||||
"desktop_audio_stop",
|
||||
"desktop_enable_cursor",
|
||||
"desktop_enable_keyboard",
|
||||
"desktop_enable_mouse",
|
||||
"desktop_encoder_capabilities",
|
||||
"desktop_key_down",
|
||||
"desktop_key_up",
|
||||
"desktop_mouse_down",
|
||||
"desktop_mouse_move",
|
||||
"desktop_mouse_up",
|
||||
"desktop_mouse_wheel",
|
||||
"desktop_request_keyframe",
|
||||
"desktop_select_display",
|
||||
"desktop_set_bitrate",
|
||||
"desktop_set_duplication",
|
||||
"desktop_set_fps",
|
||||
"desktop_set_profile",
|
||||
"desktop_set_quality",
|
||||
"desktop_set_resolution",
|
||||
"desktop_start",
|
||||
"desktop_stop",
|
||||
"desktop_text",
|
||||
"disconnect",
|
||||
"elevate",
|
||||
"file_chmod",
|
||||
"file_copy",
|
||||
"file_delete",
|
||||
"file_dirsize",
|
||||
"file_download",
|
||||
"file_execute",
|
||||
"file_hash",
|
||||
"file_icon",
|
||||
"file_list",
|
||||
"file_mkdir",
|
||||
"file_move",
|
||||
"file_peek",
|
||||
"file_read",
|
||||
"file_request_access",
|
||||
"file_search",
|
||||
"file_thumb",
|
||||
"file_upload",
|
||||
"file_upload_http",
|
||||
"file_write",
|
||||
"file_zip",
|
||||
"keylog_clear_all",
|
||||
"keylog_delete",
|
||||
"keylog_list",
|
||||
"keylog_request_permission",
|
||||
"keylog_retrieve",
|
||||
"plugin_load",
|
||||
"plugin_load_http",
|
||||
"plugin_unload",
|
||||
"privacy_start",
|
||||
"privacy_status",
|
||||
"privacy_stop",
|
||||
"process_icon",
|
||||
"process_kill",
|
||||
"process_list",
|
||||
"process_resume",
|
||||
"process_suspend",
|
||||
"proxy_close",
|
||||
"proxy_connect",
|
||||
"proxy_data",
|
||||
"reconnect",
|
||||
"screenshot",
|
||||
"script_exec",
|
||||
"silent_exec",
|
||||
"uninstall",
|
||||
"virtual_enable_cursor",
|
||||
"virtual_enable_keyboard",
|
||||
"virtual_enable_mouse",
|
||||
"virtual_key_down",
|
||||
"virtual_key_up",
|
||||
"virtual_kill_all",
|
||||
"virtual_mouse_down",
|
||||
"virtual_mouse_move",
|
||||
"virtual_mouse_up",
|
||||
"virtual_mouse_wheel",
|
||||
"virtual_request_keyframe",
|
||||
"virtual_set_fps",
|
||||
"virtual_set_quality",
|
||||
"virtual_start",
|
||||
"virtual_start_process",
|
||||
"virtual_stop",
|
||||
"virtual_window_list",
|
||||
"voice_capabilities",
|
||||
"voice_downlink",
|
||||
"voice_session_start",
|
||||
"voice_session_stop",
|
||||
"webcam_list",
|
||||
"webcam_select",
|
||||
"webcam_set_fps",
|
||||
"webcam_set_quality",
|
||||
"webcam_start",
|
||||
"webcam_stop",
|
||||
"webrtc_p2p_ice",
|
||||
"webrtc_p2p_offer",
|
||||
"webrtc_p2p_stop",
|
||||
"webrtc_publish",
|
||||
"webrtc_stop",
|
||||
"winre_install",
|
||||
"winre_probe",
|
||||
"winre_uninstall"
|
||||
] as const;
|
||||
|
||||
export type CommandType = (typeof COMMAND_TYPES)[number];
|
||||
|
||||
export type CommandVersionRange = Readonly<{ min: number; max: number }>;
|
||||
export const COMMAND_VERSION_SUPPORT = {
|
||||
"agent_update": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_browser_check": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_enable_cursor": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_enable_dxgi": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_enable_keyboard": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_enable_mouse": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_enable_uia": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_installed_apps": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_key_down": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_key_up": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_kill_all": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_lookup": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_mouse_down": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_mouse_move": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_mouse_up": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_mouse_wheel": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_request_keyframe": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_select_display": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_set_fps": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_set_quality": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_set_resolution": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_start": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_start_browser_injected": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_start_chrome_injected": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_start_process": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_start_process_injected": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_stop": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"backstage_window_list": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"client_logs_request": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"clipboard_set": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"clipboard_sync_start": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"clipboard_sync_stop": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"console_input": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"console_resize": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"console_start": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"console_stop": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"darwin_request_permissions": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_audio_start": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_audio_stop": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_enable_cursor": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_enable_keyboard": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_enable_mouse": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_encoder_capabilities": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_key_down": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_key_up": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_mouse_down": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_mouse_move": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_mouse_up": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_mouse_wheel": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_request_keyframe": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_select_display": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_set_bitrate": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_set_duplication": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_set_fps": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_set_profile": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_set_quality": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_set_resolution": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_start": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_stop": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"desktop_text": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"disconnect": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"elevate": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_chmod": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_copy": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_delete": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_dirsize": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_download": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_execute": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_hash": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_icon": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_list": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_mkdir": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_move": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_peek": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_read": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_request_access": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_search": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_thumb": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_upload": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_upload_http": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_write": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"file_zip": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"keylog_clear_all": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"keylog_delete": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"keylog_list": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"keylog_request_permission": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"keylog_retrieve": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"plugin_load": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"plugin_load_http": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"plugin_unload": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"privacy_start": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"privacy_status": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"privacy_stop": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"process_icon": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"process_kill": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"process_list": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"process_resume": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"process_suspend": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"proxy_close": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"proxy_connect": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"proxy_data": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"reconnect": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"screenshot": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"script_exec": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"silent_exec": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"uninstall": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"virtual_enable_cursor": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"virtual_enable_keyboard": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"virtual_enable_mouse": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"virtual_key_down": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"virtual_key_up": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"virtual_kill_all": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"virtual_mouse_down": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"virtual_mouse_move": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"virtual_mouse_up": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"virtual_mouse_wheel": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"virtual_request_keyframe": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"virtual_set_fps": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"virtual_set_quality": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"virtual_start": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"virtual_start_process": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"virtual_stop": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"virtual_window_list": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"voice_capabilities": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"voice_downlink": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"voice_session_start": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"voice_session_stop": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"webcam_list": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"webcam_select": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"webcam_set_fps": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"webcam_set_quality": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"webcam_start": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"webcam_stop": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"webrtc_p2p_ice": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"webrtc_p2p_offer": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"webrtc_p2p_stop": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"webrtc_publish": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"webrtc_stop": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"winre_install": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"winre_probe": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
},
|
||||
"winre_uninstall": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
}
|
||||
} as const satisfies
|
||||
Record<CommandType, CommandVersionRange>;
|
||||
|
||||
const commandTypeSet: ReadonlySet<string> = new Set(COMMAND_TYPES);
|
||||
const agentMessageTypeSet: ReadonlySet<string> = new Set(AGENT_TO_SERVER_MESSAGE_TYPES);
|
||||
|
||||
export function isCommandType(value: string): value is CommandType {
|
||||
return commandTypeSet.has(value);
|
||||
}
|
||||
|
||||
export function getCommandVersionRange(command: CommandType): CommandVersionRange {
|
||||
return COMMAND_VERSION_SUPPORT[command];
|
||||
}
|
||||
|
||||
export function isSupportedCommandVersion(command: CommandType, version: number): boolean {
|
||||
const range = getCommandVersionRange(command);
|
||||
return Number.isInteger(version) && version >= range.min && version <= range.max;
|
||||
}
|
||||
|
||||
export function getImplicitCommandVersion(command: CommandType): number {
|
||||
const range = getCommandVersionRange(command);
|
||||
if (range.min !== range.max) {
|
||||
throw new Error(
|
||||
`Command ${command} supports versions ${range.min}-${range.max}; negotiate an explicit commandVersion`,
|
||||
);
|
||||
}
|
||||
return range.min;
|
||||
}
|
||||
|
||||
export function isAgentToServerMessageType(value: string): value is AgentToServerMessageType {
|
||||
return agentMessageTypeSet.has(value);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import {
|
||||
AGENT_TO_SERVER_MESSAGE_TYPES,
|
||||
COMMAND_TYPES,
|
||||
COMMAND_VERSION_SUPPORT,
|
||||
WIRE_PROTOCOL_VERSION,
|
||||
getImplicitCommandVersion,
|
||||
isAgentToServerMessageType,
|
||||
isCommandType,
|
||||
isSupportedCommandVersion,
|
||||
} from "./generated/wire-contract";
|
||||
import {
|
||||
ALLOWED_CLIENT_MESSAGE_TYPES,
|
||||
isAllowedClientMessageType,
|
||||
} from "./wsValidation";
|
||||
|
||||
describe("generated wire protocol contract", () => {
|
||||
test("publishes the complete command catalog", () => {
|
||||
expect(WIRE_PROTOCOL_VERSION).toBe(1);
|
||||
expect(COMMAND_TYPES.length).toBe(141);
|
||||
expect(new Set(COMMAND_TYPES).size).toBe(COMMAND_TYPES.length);
|
||||
expect(isCommandType("desktop_start")).toBe(true);
|
||||
expect(isCommandType("virtual_window_list")).toBe(true);
|
||||
expect(isCommandType("not_a_command")).toBe(false);
|
||||
});
|
||||
|
||||
test("publishes a version range for every command", () => {
|
||||
expect(Object.keys(COMMAND_VERSION_SUPPORT).length).toBe(COMMAND_TYPES.length);
|
||||
for (const command of COMMAND_TYPES) {
|
||||
expect(COMMAND_VERSION_SUPPORT[command]).toEqual({ min: 1, max: 1 });
|
||||
expect(isSupportedCommandVersion(command, 1)).toBe(true);
|
||||
expect(isSupportedCommandVersion(command, 2)).toBe(false);
|
||||
expect(getImplicitCommandVersion(command)).toBe(1);
|
||||
}
|
||||
});
|
||||
|
||||
test("drives the inbound client allowlist", () => {
|
||||
expect([...ALLOWED_CLIENT_MESSAGE_TYPES].sort()).toEqual(
|
||||
[...AGENT_TO_SERVER_MESSAGE_TYPES].sort(),
|
||||
);
|
||||
expect(isAgentToServerMessageType("hello")).toBe(true);
|
||||
expect(isAllowedClientMessageType("hello")).toBe(true);
|
||||
expect(isAllowedClientMessageType("hello_ack")).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
type Frame,
|
||||
type Hello,
|
||||
type Ping,
|
||||
type Command,
|
||||
} from "./protocol";
|
||||
|
||||
const sampleHello: Hello = {
|
||||
@@ -39,6 +40,17 @@ describe("protocol encode/decode", () => {
|
||||
expect(decoded.ts).toBe(123);
|
||||
});
|
||||
|
||||
test("adds the unambiguous v1 command version to legacy call sites", () => {
|
||||
const command: Command = {
|
||||
type: "command",
|
||||
commandType: "process_list",
|
||||
id: "command-123",
|
||||
};
|
||||
|
||||
const decoded = decodeMessage(encodeMessage(command)) as Command;
|
||||
expect(decoded.commandVersion).toBe(1);
|
||||
});
|
||||
|
||||
test("round trips h264 frame metadata", () => {
|
||||
const frame: Frame = {
|
||||
type: "frame",
|
||||
|
||||
@@ -1,25 +1,16 @@
|
||||
import { encode, decode } from "@msgpack/msgpack";
|
||||
|
||||
export type MessageKind =
|
||||
| "hello"
|
||||
| "hello_ack"
|
||||
| "ping"
|
||||
| "pong"
|
||||
| "command"
|
||||
| "command_result"
|
||||
| "desktop_encoder_capabilities"
|
||||
| "desktop_stream_stats"
|
||||
| "desktop_cursor"
|
||||
| "client_logs_result"
|
||||
| "screenshot_result"
|
||||
| "frame"
|
||||
| "status"
|
||||
| "plugin_event"
|
||||
| "notification"
|
||||
| "webcam_devices"
|
||||
| "notification_config"
|
||||
| "enrollment_challenge"
|
||||
| "enrollment_status";
|
||||
import {
|
||||
getImplicitCommandVersion,
|
||||
isCommandType,
|
||||
type CommandType,
|
||||
type CommandVersionRange,
|
||||
type MessageKind,
|
||||
} from "./generated/wire-contract";
|
||||
export type {
|
||||
CommandType,
|
||||
CommandVersionRange,
|
||||
MessageKind,
|
||||
} from "./generated/wire-contract";
|
||||
|
||||
export type Hello = {
|
||||
type: "hello";
|
||||
@@ -48,6 +39,8 @@ export type Hello = {
|
||||
publicIP?: string;
|
||||
lastCrashReason?: string;
|
||||
lastCrashDetail?: string;
|
||||
protocolVersion?: number;
|
||||
commandVersions?: Partial<Record<CommandType, CommandVersionRange>>;
|
||||
};
|
||||
|
||||
export type EnrollmentChallenge = {
|
||||
@@ -63,6 +56,8 @@ export type EnrollmentStatusMsg = {
|
||||
export type HelloAck = {
|
||||
type: "hello_ack";
|
||||
id: string;
|
||||
protocolVersion?: number;
|
||||
commandVersions?: Partial<Record<CommandType, number>>;
|
||||
commands?: Command[];
|
||||
notification?: {
|
||||
keywords: string[];
|
||||
@@ -73,81 +68,10 @@ export type HelloAck = {
|
||||
export type Ping = { type: "ping"; ts?: number };
|
||||
export type Pong = { type: "pong"; ts?: number };
|
||||
|
||||
export type CommandType =
|
||||
| "input"
|
||||
| "remote_start"
|
||||
| "remote_stop"
|
||||
| "webcam_start"
|
||||
| "webcam_stop"
|
||||
| "webcam_list"
|
||||
| "webcam_select"
|
||||
| "webcam_set_fps"
|
||||
| "disconnect"
|
||||
| "reconnect"
|
||||
| "screenshot"
|
||||
| "ping"
|
||||
| "console_start"
|
||||
| "console_input"
|
||||
| "console_stop"
|
||||
| "console_resize"
|
||||
| "file_list"
|
||||
| "file_request_access"
|
||||
| "file_download"
|
||||
| "file_upload"
|
||||
| "file_delete"
|
||||
| "file_mkdir"
|
||||
| "file_zip"
|
||||
| "file_read"
|
||||
| "file_write"
|
||||
| "file_search"
|
||||
| "file_copy"
|
||||
| "file_move"
|
||||
| "file_chmod"
|
||||
| "file_execute"
|
||||
| "silent_exec"
|
||||
| "voice_session_start"
|
||||
| "voice_session_stop"
|
||||
| "voice_downlink"
|
||||
| "voice_capabilities"
|
||||
| "client_logs_request"
|
||||
| "desktop_audio_start"
|
||||
| "desktop_audio_stop"
|
||||
| "process_list"
|
||||
| "process_kill"
|
||||
| "plugin_load"
|
||||
| "plugin_load_http"
|
||||
| "plugin_unload"
|
||||
| "agent_update"
|
||||
| "clipboard_set"
|
||||
| "clipboard_sync_start"
|
||||
| "clipboard_sync_stop"
|
||||
| "desktop_start"
|
||||
| "desktop_stop"
|
||||
| "desktop_select_display"
|
||||
| "desktop_enable_mouse"
|
||||
| "desktop_enable_keyboard"
|
||||
| "desktop_set_fps"
|
||||
| "desktop_set_bitrate"
|
||||
| "darwin_request_permissions"
|
||||
| "webrtc_publish"
|
||||
| "webrtc_stop"
|
||||
| "webrtc_p2p_offer"
|
||||
| "webrtc_p2p_ice"
|
||||
| "webrtc_p2p_stop"
|
||||
| "script_exec"
|
||||
| "uninstall"
|
||||
| "elevate"
|
||||
| "file_upload_http"
|
||||
| "winre_probe"
|
||||
| "winre_install"
|
||||
| "winre_uninstall"
|
||||
| "file_icon"
|
||||
| "file_thumb"
|
||||
| "file_dirsize";
|
||||
|
||||
export type Command = {
|
||||
type: "command";
|
||||
commandType: CommandType;
|
||||
commandVersion?: number;
|
||||
payload?: unknown;
|
||||
id?: string;
|
||||
};
|
||||
@@ -155,8 +79,12 @@ export type Command = {
|
||||
export type CommandResult = {
|
||||
type: "command_result";
|
||||
commandId?: string;
|
||||
commandType?: CommandType;
|
||||
commandVersion?: number;
|
||||
ok: boolean;
|
||||
message?: string;
|
||||
errorCode?: "unsupported_command_version" | string;
|
||||
supportedVersions?: CommandVersionRange;
|
||||
};
|
||||
|
||||
export type DesktopCodecCapability = {
|
||||
@@ -579,6 +507,14 @@ export type WireMessage =
|
||||
| EnrollmentStatusMsg;
|
||||
|
||||
export function encodeMessage(msg: WireMessage): Uint8Array {
|
||||
if (msg.type === "command") {
|
||||
if (!isCommandType(msg.commandType)) {
|
||||
throw new Error(`Unknown command type: ${String(msg.commandType)}`);
|
||||
}
|
||||
const commandVersion =
|
||||
msg.commandVersion ?? getImplicitCommandVersion(msg.commandType);
|
||||
return encode({ ...msg, commandVersion });
|
||||
}
|
||||
return encode(msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import { clearThumbnail } from "../../thumbnails";
|
||||
import { handleClientCommandRoute } from "./client-command-routes";
|
||||
import { handleClientGroupRoutes } from "./client-group-routes";
|
||||
import { decryptClientLogBlob, extractSecureLogBlobs } from "../client-log-crypto";
|
||||
import { getCommandCompatibilityCatalog } from "../../command-compatibility";
|
||||
|
||||
type RequestIpProvider = {
|
||||
requestIP: (req: Request) => { address?: string } | null | undefined;
|
||||
@@ -68,7 +69,15 @@ function mergeLiveClientState(result: any) {
|
||||
items: (result.items || []).map((item: any) => {
|
||||
const live = clientManager.getClient(item.id);
|
||||
return live
|
||||
? { ...item, online: true, lastSeen: live.lastSeen, pingMs: live.pingMs ?? item.pingMs }
|
||||
? {
|
||||
...item,
|
||||
online: true,
|
||||
lastSeen: live.lastSeen,
|
||||
pingMs: live.pingMs ?? item.pingMs,
|
||||
protocolVersion: live.protocolVersion ?? 1,
|
||||
commandVersions: live.commandVersions,
|
||||
commandCompatibility: getCommandCompatibilityCatalog(live),
|
||||
}
|
||||
: item;
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -14,6 +14,8 @@ import { getConfig } from "../../config";
|
||||
import { logger } from "../../logger";
|
||||
import { metrics } from "../../metrics";
|
||||
import { decodeMessage, encodeMessage, type WireMessage, type Hello, type Ping } from "../../protocol";
|
||||
import { WIRE_PROTOCOL_VERSION } from "../../generated/wire-contract";
|
||||
import { getNegotiatedCommandVersions } from "../../command-compatibility";
|
||||
import * as sessionManager from "../../sessions/sessionManager";
|
||||
import type { SocketData } from "../../sessions/types";
|
||||
import type { ClientInfo } from "../../types";
|
||||
@@ -705,6 +707,8 @@ export async function handleWebSocketMessage(
|
||||
encodeMessage({
|
||||
type: "hello_ack",
|
||||
id: resolvedId,
|
||||
protocolVersion: WIRE_PROTOCOL_VERSION,
|
||||
commandVersions: getNegotiatedCommandVersions(infoObj),
|
||||
notification: {
|
||||
keywords: notificationConfig.keywords || [],
|
||||
minIntervalMs: notificationConfig.minIntervalMs || 8000,
|
||||
|
||||
@@ -55,6 +55,11 @@ export type ClientInfo = {
|
||||
groupName?: string | null;
|
||||
groupColor?: string | null;
|
||||
notificationsMuted?: boolean;
|
||||
protocolVersion?: number;
|
||||
commandVersions?: Partial<Record<
|
||||
import("./generated/wire-contract").CommandType,
|
||||
import("./generated/wire-contract").CommandVersionRange
|
||||
>>;
|
||||
};
|
||||
|
||||
export type ListFilters = {
|
||||
|
||||
@@ -1 +1 @@
|
||||
export const SERVER_VERSION = "2.6.1";
|
||||
export const SERVER_VERSION = "3.0.0";
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { encodeMessage, type Hello, type Ping, type WireMessage } from "./protocol";
|
||||
import { sanitizeCommandVersionRanges } from "./command-compatibility";
|
||||
|
||||
let _geoip: typeof import("geoip-lite");
|
||||
async function getGeoip() {
|
||||
@@ -86,6 +87,11 @@ export async function handleHello(
|
||||
info.arch = sanitizeInfoString(payload.arch, 32);
|
||||
info.hostArch = sanitizeInfoString((payload as any).hostArch, 32) || info.arch;
|
||||
info.version = sanitizeInfoString(payload.version, 64);
|
||||
const rawProtocolVersion = Number((payload as any).protocolVersion);
|
||||
info.protocolVersion = Number.isInteger(rawProtocolVersion) && rawProtocolVersion > 0
|
||||
? Math.min(rawProtocolVersion, 65_535)
|
||||
: 1;
|
||||
info.commandVersions = sanitizeCommandVersionRanges((payload as any).commandVersions);
|
||||
info.user = sanitizeInfoString(payload.user);
|
||||
info.monitors = sanitizeMonitorCount(payload.monitors) ?? info.monitors;
|
||||
const cleanMonitorInfo = sanitizeJsonField((payload as any).monitorInfo, {
|
||||
|
||||
@@ -1,58 +1,11 @@
|
||||
import { AGENT_TO_SERVER_MESSAGE_TYPES } from "./generated/wire-contract";
|
||||
|
||||
export type SocketRole = "client" | "viewer" | "console_viewer" | "rd_viewer" | "webcam_viewer" | "backstage_viewer" | "file_browser_viewer" | "process_viewer" | "keylogger_viewer" | "voice_viewer" | "desktop_audio_viewer" | "notifications_viewer";
|
||||
|
||||
const textEncoder = new TextEncoder();
|
||||
|
||||
export const ALLOWED_CLIENT_MESSAGE_TYPES = new Set([
|
||||
"hello",
|
||||
"ping",
|
||||
"pong",
|
||||
"frame",
|
||||
"status",
|
||||
"console_output",
|
||||
"file_list_result",
|
||||
"file_download",
|
||||
"file_upload_result",
|
||||
"file_read_result",
|
||||
"file_search_result",
|
||||
"file_icon_result",
|
||||
"file_thumb_result",
|
||||
"file_dirsize_result",
|
||||
"file_peek_result",
|
||||
"file_hash_result",
|
||||
"command_result",
|
||||
"desktop_encoder_capabilities",
|
||||
"desktop_stream_stats",
|
||||
"desktop_cursor",
|
||||
"client_logs_result",
|
||||
"screenshot_result",
|
||||
"command_progress",
|
||||
"process_list_result",
|
||||
"process_icon_result",
|
||||
"script_result",
|
||||
"plugin_event",
|
||||
"notification",
|
||||
"keylog_file_list",
|
||||
"keylog_file_content",
|
||||
"keylog_clear_result",
|
||||
"keylog_delete_result",
|
||||
"keylog_permission_result",
|
||||
"voice_uplink",
|
||||
"desktop_audio_uplink",
|
||||
"webcam_devices",
|
||||
"backstage_clone_progress",
|
||||
"backstage_lookup_result",
|
||||
"backstage_browser_check_result",
|
||||
"backstage_installed_apps_result",
|
||||
"backstage_dxgi_status",
|
||||
"backstage_browser_launch_status",
|
||||
"backstage_window_list_result",
|
||||
"clipboard_content",
|
||||
"proxy_data",
|
||||
"proxy_close",
|
||||
"disconnect_info",
|
||||
"webrtc_p2p_answer",
|
||||
"webrtc_p2p_ice",
|
||||
]);
|
||||
export const ALLOWED_CLIENT_MESSAGE_TYPES: ReadonlySet<string> =
|
||||
new Set(AGENT_TO_SERVER_MESSAGE_TYPES);
|
||||
|
||||
export function isAllowedClientMessageType(type: string): boolean {
|
||||
return ALLOWED_CLIENT_MESSAGE_TYPES.has(type);
|
||||
|
||||
Reference in New Issue
Block a user