Merge pull request #4298 from Dokploy/fix/GHSA-f8wj-5c4w-frhg-cross-org-idor

Fix/ghsa f8wj 5c4w frhg cross org idor
This commit is contained in:
Mauricio Siu
2026-04-24 12:49:24 -06:00
committed by GitHub
9 changed files with 110 additions and 8 deletions

View File

@@ -458,9 +458,26 @@ export const backupRouter = createTRPCRouter({
serverId: z.string().optional(), serverId: z.string().optional(),
}), }),
) )
.query(async ({ input }) => { .query(async ({ input, ctx }) => {
try { try {
const destination = await findDestinationById(input.destinationId); const destination = await findDestinationById(input.destinationId);
if (destination.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have access to this destination.",
});
}
if (input.serverId) {
const targetServer = await findServerById(input.serverId);
if (
targetServer.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have access to this server.",
});
}
}
const rcloneFlags = getS3Credentials(destination); const rcloneFlags = getS3Credentials(destination);
const bucketPath = `:s3:${destination.bucket}`; const bucketPath = `:s3:${destination.bucket}`;

View File

@@ -18,7 +18,16 @@ export const clusterRouter = createTRPCRouter({
serverId: z.string().optional(), serverId: z.string().optional(),
}), }),
) )
.query(async ({ input }) => { .query(async ({ input, ctx }) => {
if (input.serverId) {
const targetServer = await findServerById(input.serverId);
if (targetServer.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have access to this server.",
});
}
}
const docker = await getRemoteDocker(input.serverId); const docker = await getRemoteDocker(input.serverId);
const workers: DockerNode[] = await docker.listNodes(); const workers: DockerNode[] = await docker.listNodes();
return workers; return workers;
@@ -32,6 +41,15 @@ export const clusterRouter = createTRPCRouter({
}), }),
) )
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
if (input.serverId) {
const targetServer = await findServerById(input.serverId);
if (targetServer.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have access to this server.",
});
}
}
try { try {
const drainCommand = `docker node update --availability drain ${input.nodeId}`; const drainCommand = `docker node update --availability drain ${input.nodeId}`;
const removeCommand = `docker node rm ${input.nodeId} --force`; const removeCommand = `docker node rm ${input.nodeId} --force`;
@@ -65,7 +83,16 @@ export const clusterRouter = createTRPCRouter({
serverId: z.string().optional(), serverId: z.string().optional(),
}), }),
) )
.query(async ({ input }) => { .query(async ({ input, ctx }) => {
if (input.serverId) {
const targetServer = await findServerById(input.serverId);
if (targetServer.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have access to this server.",
});
}
}
const docker = await getRemoteDocker(input.serverId); const docker = await getRemoteDocker(input.serverId);
const result = await docker.swarmInspect(); const result = await docker.swarmInspect();
const docker_version = await docker.version(); const docker_version = await docker.version();
@@ -88,7 +115,16 @@ export const clusterRouter = createTRPCRouter({
serverId: z.string().optional(), serverId: z.string().optional(),
}), }),
) )
.query(async ({ input }) => { .query(async ({ input, ctx }) => {
if (input.serverId) {
const targetServer = await findServerById(input.serverId);
if (targetServer.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have access to this server.",
});
}
}
const docker = await getRemoteDocker(input.serverId); const docker = await getRemoteDocker(input.serverId);
const result = await docker.swarmInspect(); const result = await docker.swarmInspect();
const docker_version = await docker.version(); const docker_version = await docker.version();

View File

@@ -16,6 +16,7 @@ import {
checkServicePermissionAndAccess, checkServicePermissionAndAccess,
findMemberByUserId, findMemberByUserId,
} from "@dokploy/server/services/permission"; } from "@dokploy/server/services/permission";
import { findServerById } from "@dokploy/server/services/server";
import { TRPCError } from "@trpc/server"; import { TRPCError } from "@trpc/server";
import { desc, eq } from "drizzle-orm"; import { desc, eq } from "drizzle-orm";
import { z } from "zod"; import { z } from "zod";
@@ -52,7 +53,14 @@ export const deploymentRouter = createTRPCRouter({
}), }),
allByServer: withPermission("deployment", "read") allByServer: withPermission("deployment", "read")
.input(apiFindAllByServer) .input(apiFindAllByServer)
.query(async ({ input }) => { .query(async ({ input, ctx }) => {
const targetServer = await findServerById(input.serverId);
if (targetServer.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have access to this server.",
});
}
return await findAllDeploymentsByServerId(input.serverId); return await findAllDeploymentsByServerId(input.serverId);
}), }),
allCentralized: withPermission("deployment", "read").query( allCentralized: withPermission("deployment", "read").query(

View File

@@ -15,7 +15,9 @@ import {
updateVolumeBackupSchema, updateVolumeBackupSchema,
volumeBackups, volumeBackups,
} from "@dokploy/server/db/schema"; } from "@dokploy/server/db/schema";
import { findDestinationById } from "@dokploy/server/services/destination";
import { checkServicePermissionAndAccess } from "@dokploy/server/services/permission"; import { checkServicePermissionAndAccess } from "@dokploy/server/services/permission";
import { findServerById } from "@dokploy/server/services/server";
import { import {
execAsyncRemote, execAsyncRemote,
execAsyncStream, execAsyncStream,
@@ -265,7 +267,23 @@ export const volumeBackupsRouter = createTRPCRouter({
serverId: z.string().optional(), serverId: z.string().optional(),
}), }),
) )
.subscription(async ({ input }) => { .subscription(async ({ input, ctx }) => {
const destination = await findDestinationById(input.destinationId);
if (destination.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have access to this destination.",
});
}
if (input.serverId) {
const targetServer = await findServerById(input.serverId);
if (targetServer.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have access to this server.",
});
}
}
return observable<string>((emit) => { return observable<string>((emit) => {
const runRestore = async () => { const runRestore = async () => {
try { try {

View File

@@ -85,6 +85,11 @@ export const setupDockerContainerLogsWebSocketServer = (
if (serverId) { if (serverId) {
const server = await findServerById(serverId); const server = await findServerById(serverId);
if (server.organizationId !== session.activeOrganizationId) {
ws.close();
return;
}
if (!server.sshKeyId) return; if (!server.sshKeyId) return;
const client = new Client(); const client = new Client();
client client

View File

@@ -61,6 +61,12 @@ export const setupDockerContainerTerminalWebSocketServer = (
try { try {
if (serverId) { if (serverId) {
const server = await findServerById(serverId); const server = await findServerById(serverId);
if (server.organizationId !== session.activeOrganizationId) {
ws.close();
return;
}
if (!server.sshKeyId) if (!server.sshKeyId)
throw new Error("No SSH key available for this server"); throw new Error("No SSH key available for this server");

View File

@@ -57,6 +57,11 @@ export const setupDeploymentLogsWebSocketServer = (
if (serverId) { if (serverId) {
const server = await findServerById(serverId); const server = await findServerById(serverId);
if (server.organizationId !== session.activeOrganizationId) {
ws.close();
return;
}
if (!server.sshKeyId) { if (!server.sshKeyId) {
ws.close(); ws.close();
return; return;

View File

@@ -154,6 +154,11 @@ export const setupTerminalWebSocketServer = (
return; return;
} }
if (server.organizationId !== session.activeOrganizationId) {
ws.close();
return;
}
const { ipAddress: host, port, username, sshKey, sshKeyId } = server; const { ipAddress: host, port, username, sshKey, sshKeyId } = server;
if (!sshKeyId) { if (!sshKeyId) {

View File

@@ -481,8 +481,10 @@ export const validateRequest = async (request: IncomingMessage) => {
}; };
} }
const organizationId = JSON.parse( const organizationId = (
apiKeyRecord.metadata || "{}", JSON.parse(apiKeyRecord.metadata || "{}") as {
organizationId?: string;
}
).organizationId; ).organizationId;
if (!organizationId) { if (!organizationId) {