fix(security): command injection in registry.testRegistry/testRegistryById remote path

The remote (execAsyncRemote) path built `echo ${password} | docker ${args.join(" ")}`
with the password, registryUrl and username interpolated unescaped, so a password
like `pw; whoami` ran arbitrary commands as root on the target server. Reuse
safeDockerLoginCommand (already used by create/update), which shell-escapes each
field and feeds the password via --password-stdin. The local argv+stdin path was
already safe.
This commit is contained in:
Mauricio Siu
2026-07-20 17:17:45 -06:00
parent d02f34f9d4
commit d3f522b7a6

View File

@@ -5,6 +5,7 @@ import {
findRegistryById,
IS_CLOUD,
removeRegistry,
safeDockerLoginCommand,
updateRegistry,
} from "@dokploy/server";
import { db } from "@dokploy/server/db";
@@ -122,7 +123,11 @@ export const registryRouter = createTRPCRouter({
if (input.serverId && input.serverId !== "none") {
await execAsyncRemote(
input.serverId,
`echo ${input.password} | docker ${args.join(" ")}`,
safeDockerLoginCommand(
input.registryUrl,
input.username,
input.password,
),
);
} else {
await execFileAsync("docker", args, {
@@ -182,7 +187,11 @@ export const registryRouter = createTRPCRouter({
if (input.serverId && input.serverId !== "none") {
await execAsyncRemote(
input.serverId,
`echo ${registryData.password} | docker ${args.join(" ")}`,
safeDockerLoginCommand(
registryData.registryUrl,
registryData.username,
registryData.password,
),
);
} else {
await execFileAsync("docker", args, {