more security improvements

This commit is contained in:
KingKDot
2026-04-13 17:33:51 -05:00
parent e80736ec37
commit bfc3e3fa4d
2 changed files with 33 additions and 2 deletions
@@ -177,6 +177,21 @@ async function deliverToUserWebhook(
return;
}
const hostname = parsed.hostname.toLowerCase();
const BLOCKED_HOSTS = ["localhost", "metadata.google.internal", "169.254.169.254"];
if (
BLOCKED_HOSTS.includes(hostname) ||
hostname.endsWith(".internal") ||
hostname.startsWith("127.") ||
hostname === "[::1]" ||
/^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.)/.test(hostname) ||
hostname.startsWith("169.254.") ||
hostname.startsWith("0.")
) {
logger.warn(`[notify] blocked webhook to private/internal address: ${hostname}`);
return;
}
try {
const isDiscord = /discord(app)?\.com$/i.test(parsed.hostname);
if (isDiscord) {
@@ -3,6 +3,7 @@ import path from "path";
import { v4 as uuidv4 } from "uuid";
import { authenticateRequest } from "../../auth";
import { requirePermission } from "../../rbac";
import { canUserAccessClient } from "../../users";
import * as clientManager from "../../clientManager";
import { metrics } from "../../metrics";
import { encodeMessage } from "../../protocol";
@@ -88,6 +89,9 @@ export async function handlePluginRoutes(
}
const clientId = clientPluginsMatch[1];
if (!canUserAccessClient(user.userId, user.role, clientId)) {
return new Response("Forbidden: You do not have access to this client", { status: 403 });
}
const loaded = deps.pluginLoadedByClient.get(clientId) || new Set<string>();
const manifests = await deps.listPluginManifests();
const plugins = manifests.map((manifest) => ({
@@ -337,8 +341,8 @@ export async function handlePluginRoutes(
if (req.method === "POST" && pluginExecMatch) {
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 });
if (user.role !== "admin") {
return new Response("Forbidden: Admin access required", { status: 403 });
}
let pluginId = "";
try { pluginId = deps.sanitizePluginId(pluginExecMatch[1]); } catch { return new Response("Invalid plugin id", { status: 400 }); }
@@ -458,6 +462,9 @@ export async function handlePluginRoutes(
return new Response("Forbidden", { status: 403 });
}
const targetId = pluginLoadMatch[1];
if (!canUserAccessClient(user.userId, user.role, targetId)) {
return new Response("Forbidden: You do not have access to this client", { status: 403 });
}
const pluginId = pluginLoadMatch[2];
const target = clientManager.getClient(targetId);
if (!target) return new Response("Not found", { status: 404 });
@@ -489,6 +496,9 @@ export async function handlePluginRoutes(
return new Response("Forbidden", { status: 403 });
}
const targetId = pluginEventsPollMatch[1];
if (!canUserAccessClient(user.userId, user.role, targetId)) {
return new Response("Forbidden: You do not have access to this client", { status: 403 });
}
const pluginId = pluginEventsPollMatch[2];
const events = deps.drainPluginUIEvents(targetId, pluginId);
return Response.json({ events });
@@ -506,6 +516,9 @@ export async function handlePluginRoutes(
}
const targetId = pluginEventMatch[1];
if (!canUserAccessClient(user.userId, user.role, targetId)) {
return new Response("Forbidden: You do not have access to this client", { status: 403 });
}
const pluginId = pluginEventMatch[2];
const target = clientManager.getClient(targetId);
if (!target) return new Response("Not found", { status: 404 });
@@ -565,6 +578,9 @@ export async function handlePluginRoutes(
}
const targetId = pluginUnloadMatch[1];
if (!canUserAccessClient(user.userId, user.role, targetId)) {
return new Response("Forbidden: You do not have access to this client", { status: 403 });
}
const pluginId = pluginUnloadMatch[2];
const target = clientManager.getClient(targetId);
if (!target) return new Response("Not found", { status: 404 });