Harden admin API routes and shell usage

- Require admin auth on interface-local-ip, network-interfaces,
  next-listen-port and public-ip (interface-local-ip and public-ip
  allowed unauthenticated command injection as root)
- Validate interface names and call ip/curl via execFileSync
- Write host/client WireGuard configs with fs instead of shell
  heredocs, validating IPs, AllowedIPs, endpoint and listen port
- Replace rm -rf/cat/mkdir/chmod/test shell calls with fs APIs
- Refuse private media requested through the public media route

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-15 21:09:27 +01:00
co-authored by Claude Opus 5.5
parent 178f98104c
commit cd707465f4
19 changed files with 326 additions and 106 deletions
@@ -1,5 +1,5 @@
import _ from "lodash";
import { execSync } from "node:child_process";
import { rmSync } from "node:fs";
import path from "node:path";
import type { AdminCrudAPIParams, TableType } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
@@ -14,7 +14,7 @@ import type {
BUN_SQLITE_WGUI_HOSTS,
} from "@/db/types/db";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { ServerQueryParam } from "@moduletrace/bun-sqlite/dist/types";
import type { ServerQueryParam } from "@moduletrace/bun-sqlite/types";
import grabClientDirnames from "@/src/functions/backend/setup/grab-client-dir-names";
const { WGUI_LIB_HOSTS_CONFIGS_DIR, WGUI_LIB_HOST_CLIENTS_DIR_NAME } =
@@ -72,7 +72,7 @@ export default async function (
});
try {
execSync(`rm -rf ${CLIENT_DIR}`, { encoding: "utf-8" });
rmSync(CLIENT_DIR, { recursive: true, force: true });
} catch (error) {}
affected_host_ids.add(host_id);
+8 -1
View File
@@ -1,5 +1,5 @@
import getInterfaceLocalIP from "@/src/functions/backend/setup/get-interface-local-ip";
import type { ApiReqParams } from "@/src/types";
import adminApiGuard from "@/src/functions/backend/auth/admin-api-guard";
import type {
APIResponseObject,
BunextAPIRouteHandler,
@@ -11,6 +11,13 @@ export const handler: BunextAPIRouteHandler<APIResponseObject> = async (
if (params.req.method !== "GET") {
return { success: false };
}
const unauthorized_res = await adminApiGuard({ req: params.req });
if (unauthorized_res) {
return unauthorized_res;
}
const url = new URL(params.req.url || "");
const iface = url.searchParams.get("interface") || undefined;
try {
+8 -1
View File
@@ -1,5 +1,5 @@
import grabHostNetworkInterfaces from "@/src/functions/backend/setup/grab-host-network-interfaces";
import type { ApiReqParams } from "@/src/types";
import adminApiGuard from "@/src/functions/backend/auth/admin-api-guard";
import type {
APIResponseObject,
BunextAPIRouteHandler,
@@ -11,6 +11,13 @@ export const handler: BunextAPIRouteHandler<APIResponseObject> = async (
if (params.req.method !== "GET") {
return { success: false };
}
const unauthorized_res = await adminApiGuard({ req: params.req });
if (unauthorized_res) {
return unauthorized_res;
}
try {
const interfaces = await grabHostNetworkInterfaces();
return { success: true, stringRes: JSON.stringify(interfaces) };
+8 -1
View File
@@ -1,5 +1,5 @@
import getNextAvailableListenPort from "@/src/functions/backend/setup/get-next-available-listen-port";
import type { ApiReqParams } from "@/src/types";
import adminApiGuard from "@/src/functions/backend/auth/admin-api-guard";
import type {
APIResponseObject,
BunextAPIRouteHandler,
@@ -11,6 +11,13 @@ export const handler: BunextAPIRouteHandler<APIResponseObject> = async (
if (params.req.method !== "GET") {
return { success: false };
}
const unauthorized_res = await adminApiGuard({ req: params.req });
if (unauthorized_res) {
return unauthorized_res;
}
try {
const port = await getNextAvailableListenPort();
return { success: true, numberRes: port };
+8 -1
View File
@@ -1,5 +1,5 @@
import grabHostPublicIPAddress from "@/src/functions/backend/setup/grab-host-public-ip-address";
import type { ApiReqParams } from "@/src/types";
import adminApiGuard from "@/src/functions/backend/auth/admin-api-guard";
import type {
APIResponseObject,
BunextAPIRouteHandler,
@@ -11,6 +11,13 @@ export const handler: BunextAPIRouteHandler<APIResponseObject> = async (
if (params.req.method !== "GET") {
return { success: false };
}
const unauthorized_res = await adminApiGuard({ req: params.req });
if (unauthorized_res) {
return unauthorized_res;
}
const url = new URL(params.req.url || "");
const iface = url.searchParams.get("interface") || undefined;
try {