Skip to content

Commit c5e3b00

Browse files
authored
Merge pull request #2125 from Dokploy/fix/issues
Fix/issues
2 parents 715e441 + fb5d2bd commit c5e3b00

5 files changed

Lines changed: 143 additions & 18 deletions

File tree

apps/dokploy/server/api/routers/docker.ts

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
containerRestart,
3+
findServerById,
34
getConfig,
45
getContainers,
56
getContainersByAppLabel,
@@ -9,6 +10,9 @@ import {
910
} from "@dokploy/server";
1011
import { z } from "zod";
1112
import { createTRPCRouter, protectedProcedure } from "../trpc";
13+
import { TRPCError } from "@trpc/server";
14+
15+
export const containerIdRegex = /^[a-zA-Z0-9.\-_]+$/;
1216

1317
export const dockerRouter = createTRPCRouter({
1418
getContainers: protectedProcedure
@@ -17,14 +21,23 @@ export const dockerRouter = createTRPCRouter({
1721
serverId: z.string().optional(),
1822
}),
1923
)
20-
.query(async ({ input }) => {
24+
.query(async ({ input, ctx }) => {
25+
if (input.serverId) {
26+
const server = await findServerById(input.serverId);
27+
if (server.organizationId !== ctx.session?.activeOrganizationId) {
28+
throw new TRPCError({ code: "UNAUTHORIZED" });
29+
}
30+
}
2131
return await getContainers(input.serverId);
2232
}),
2333

2434
restartContainer: protectedProcedure
2535
.input(
2636
z.object({
27-
containerId: z.string().min(1),
37+
containerId: z
38+
.string()
39+
.min(1)
40+
.regex(containerIdRegex, "Invalid container id."),
2841
}),
2942
)
3043
.mutation(async ({ input }) => {
@@ -34,11 +47,20 @@ export const dockerRouter = createTRPCRouter({
3447
getConfig: protectedProcedure
3548
.input(
3649
z.object({
37-
containerId: z.string().min(1),
50+
containerId: z
51+
.string()
52+
.min(1)
53+
.regex(containerIdRegex, "Invalid container id."),
3854
serverId: z.string().optional(),
3955
}),
4056
)
41-
.query(async ({ input }) => {
57+
.query(async ({ input, ctx }) => {
58+
if (input.serverId) {
59+
const server = await findServerById(input.serverId);
60+
if (server.organizationId !== ctx.session?.activeOrganizationId) {
61+
throw new TRPCError({ code: "UNAUTHORIZED" });
62+
}
63+
}
4264
return await getConfig(input.containerId, input.serverId);
4365
}),
4466

@@ -48,11 +70,17 @@ export const dockerRouter = createTRPCRouter({
4870
appType: z
4971
.union([z.literal("stack"), z.literal("docker-compose")])
5072
.optional(),
51-
appName: z.string().min(1),
73+
appName: z.string().min(1).regex(containerIdRegex, "Invalid app name."),
5274
serverId: z.string().optional(),
5375
}),
5476
)
55-
.query(async ({ input }) => {
77+
.query(async ({ input, ctx }) => {
78+
if (input.serverId) {
79+
const server = await findServerById(input.serverId);
80+
if (server.organizationId !== ctx.session?.activeOrganizationId) {
81+
throw new TRPCError({ code: "UNAUTHORIZED" });
82+
}
83+
}
5684
return await getContainersByAppNameMatch(
5785
input.appName,
5886
input.appType,
@@ -63,12 +91,18 @@ export const dockerRouter = createTRPCRouter({
6391
getContainersByAppLabel: protectedProcedure
6492
.input(
6593
z.object({
66-
appName: z.string().min(1),
94+
appName: z.string().min(1).regex(containerIdRegex, "Invalid app name."),
6795
serverId: z.string().optional(),
6896
type: z.enum(["standalone", "swarm"]),
6997
}),
7098
)
71-
.query(async ({ input }) => {
99+
.query(async ({ input, ctx }) => {
100+
if (input.serverId) {
101+
const server = await findServerById(input.serverId);
102+
if (server.organizationId !== ctx.session?.activeOrganizationId) {
103+
throw new TRPCError({ code: "UNAUTHORIZED" });
104+
}
105+
}
72106
return await getContainersByAppLabel(
73107
input.appName,
74108
input.type,
@@ -79,22 +113,34 @@ export const dockerRouter = createTRPCRouter({
79113
getStackContainersByAppName: protectedProcedure
80114
.input(
81115
z.object({
82-
appName: z.string().min(1),
116+
appName: z.string().min(1).regex(containerIdRegex, "Invalid app name."),
83117
serverId: z.string().optional(),
84118
}),
85119
)
86-
.query(async ({ input }) => {
120+
.query(async ({ input, ctx }) => {
121+
if (input.serverId) {
122+
const server = await findServerById(input.serverId);
123+
if (server.organizationId !== ctx.session?.activeOrganizationId) {
124+
throw new TRPCError({ code: "UNAUTHORIZED" });
125+
}
126+
}
87127
return await getStackContainersByAppName(input.appName, input.serverId);
88128
}),
89129

90130
getServiceContainersByAppName: protectedProcedure
91131
.input(
92132
z.object({
93-
appName: z.string().min(1),
133+
appName: z.string().min(1).regex(containerIdRegex, "Invalid app name."),
94134
serverId: z.string().optional(),
95135
}),
96136
)
97-
.query(async ({ input }) => {
137+
.query(async ({ input, ctx }) => {
138+
if (input.serverId) {
139+
const server = await findServerById(input.serverId);
140+
if (server.organizationId !== ctx.session?.activeOrganizationId) {
141+
throw new TRPCError({ code: "UNAUTHORIZED" });
142+
}
143+
}
98144
return await getServiceContainersByAppName(input.appName, input.serverId);
99145
}),
100146
});

apps/dokploy/server/api/routers/settings.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,15 @@ export const settingsRouter = createTRPCRouter({
459459
throw new TRPCError({ code: "UNAUTHORIZED" });
460460
}
461461
}
462+
463+
if (input.serverId) {
464+
const server = await findServerById(input.serverId);
465+
466+
if (server.organizationId !== ctx.session?.activeOrganizationId) {
467+
throw new TRPCError({ code: "UNAUTHORIZED" });
468+
}
469+
}
470+
462471
return readConfigInPath(input.path, input.serverId);
463472
}),
464473
getIp: protectedProcedure.query(async ({ ctx }) => {

apps/dokploy/server/api/routers/swarm.ts

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import {
66
} from "@dokploy/server";
77
import { z } from "zod";
88
import { createTRPCRouter, protectedProcedure } from "../trpc";
9+
import { TRPCError } from "@trpc/server";
10+
import { findServerById } from "@dokploy/server";
11+
import { containerIdRegex } from "./docker";
912

1013
export const swarmRouter = createTRPCRouter({
1114
getNodes: protectedProcedure
@@ -14,12 +17,24 @@ export const swarmRouter = createTRPCRouter({
1417
serverId: z.string().optional(),
1518
}),
1619
)
17-
.query(async ({ input }) => {
20+
.query(async ({ input, ctx }) => {
21+
if (input.serverId) {
22+
const server = await findServerById(input.serverId);
23+
if (server.organizationId !== ctx.session?.activeOrganizationId) {
24+
throw new TRPCError({ code: "UNAUTHORIZED" });
25+
}
26+
}
1827
return await getSwarmNodes(input.serverId);
1928
}),
2029
getNodeInfo: protectedProcedure
2130
.input(z.object({ nodeId: z.string(), serverId: z.string().optional() }))
22-
.query(async ({ input }) => {
31+
.query(async ({ input, ctx }) => {
32+
if (input.serverId) {
33+
const server = await findServerById(input.serverId);
34+
if (server.organizationId !== ctx.session?.activeOrganizationId) {
35+
throw new TRPCError({ code: "UNAUTHORIZED" });
36+
}
37+
}
2338
return await getNodeInfo(input.nodeId, input.serverId);
2439
}),
2540
getNodeApps: protectedProcedure
@@ -28,17 +43,29 @@ export const swarmRouter = createTRPCRouter({
2843
serverId: z.string().optional(),
2944
}),
3045
)
31-
.query(async ({ input }) => {
46+
.query(async ({ input, ctx }) => {
47+
if (input.serverId) {
48+
const server = await findServerById(input.serverId);
49+
if (server.organizationId !== ctx.session?.activeOrganizationId) {
50+
throw new TRPCError({ code: "UNAUTHORIZED" });
51+
}
52+
}
3253
return getNodeApplications(input.serverId);
3354
}),
3455
getAppInfos: protectedProcedure
3556
.input(
3657
z.object({
37-
appName: z.string(),
58+
appName: z.string().min(1).regex(containerIdRegex, "Invalid app name."),
3859
serverId: z.string().optional(),
3960
}),
4061
)
41-
.query(async ({ input }) => {
62+
.query(async ({ input, ctx }) => {
63+
if (input.serverId) {
64+
const server = await findServerById(input.serverId);
65+
if (server.organizationId !== ctx.session?.activeOrganizationId) {
66+
throw new TRPCError({ code: "UNAUTHORIZED" });
67+
}
68+
}
4269
return await getApplicationInfo(input.appName, input.serverId);
4370
}),
4471
});

apps/dokploy/server/api/routers/user.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,24 @@ export const userRouter = createTRPCRouter({
7575
},
7676
});
7777

78+
// If user not found in the organization, deny access
79+
if (!memberResult) {
80+
throw new TRPCError({
81+
code: "NOT_FOUND",
82+
message: "User not found in this organization",
83+
});
84+
}
85+
86+
// Allow access if:
87+
// 1. User is requesting their own information
88+
// 2. User has owner role (admin permissions) AND user is in the same organization
89+
if (memberResult.userId !== ctx.user.id && ctx.user.role !== "owner") {
90+
throw new TRPCError({
91+
code: "UNAUTHORIZED",
92+
message: "You are not authorized to access this user",
93+
});
94+
}
95+
7896
return memberResult;
7997
}),
8098
get: protectedProcedure.query(async ({ ctx }) => {

packages/server/src/db/schema/user.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { backups } from "./backups";
1515
import { projects } from "./project";
1616
import { schedules } from "./schedule";
1717
import { certificateType } from "./shared";
18+
import { paths } from "@dokploy/server/constants";
1819
/**
1920
* This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same
2021
* database instance for multiple projects.
@@ -236,7 +237,31 @@ export const apiModifyTraefikConfig = z.object({
236237
serverId: z.string().optional(),
237238
});
238239
export const apiReadTraefikConfig = z.object({
239-
path: z.string().min(1),
240+
path: z
241+
.string()
242+
.min(1)
243+
.refine(
244+
(path) => {
245+
// Prevent directory traversal attacks
246+
if (path.includes("../") || path.includes("..\\")) {
247+
return false;
248+
}
249+
250+
const { MAIN_TRAEFIK_PATH } = paths();
251+
if (path.startsWith("/") && !path.startsWith(MAIN_TRAEFIK_PATH)) {
252+
return false;
253+
}
254+
// Prevent null bytes and other dangerous characters
255+
if (path.includes("\0") || path.includes("\x00")) {
256+
return false;
257+
}
258+
return true;
259+
},
260+
{
261+
message:
262+
"Invalid path: path traversal or unauthorized directory access detected",
263+
},
264+
),
240265
serverId: z.string().optional(),
241266
});
242267

0 commit comments

Comments
 (0)