fix: prevent registry password from appearing in error messages and shell commands (#4579)

This commit is contained in:
Mauricio Siu
2026-06-08 09:20:34 -06:00
committed by GitHub
parent e9a0932b23
commit 1f4f94042f
3 changed files with 41 additions and 11 deletions

View File

@@ -27,6 +27,16 @@ export function safeDockerLoginCommand(
return `printf %s ${escapedPassword} | docker login ${escapedRegistry} -u ${escapedUser} --password-stdin`; return `printf %s ${escapedPassword} | docker login ${escapedRegistry} -u ${escapedUser} --password-stdin`;
} }
function sanitizeRegistryError(
error: unknown,
password: string | null | undefined,
): string {
const message =
error instanceof Error ? error.message : "Error with registry login";
if (!password) return message;
return message.split(password).join("***");
}
export const createRegistry = async ( export const createRegistry = async (
input: z.infer<typeof apiCreateRegistry>, input: z.infer<typeof apiCreateRegistry>,
organizationId: string, organizationId: string,
@@ -59,10 +69,15 @@ export const createRegistry = async (
input.username, input.username,
input.password, input.password,
); );
if (input.serverId && input.serverId !== "none") { try {
await execAsyncRemote(input.serverId, loginCommand); if (input.serverId && input.serverId !== "none") {
} else if (newRegistry.registryType === "cloud") { await execAsyncRemote(input.serverId, loginCommand);
await execAsync(loginCommand); } else if (newRegistry.registryType === "cloud") {
await execAsync(loginCommand);
}
} catch (error) {
const sanitized = sanitizeRegistryError(error, input.password);
throw new TRPCError({ code: "BAD_REQUEST", message: sanitized });
} }
return newRegistry; return newRegistry;
@@ -129,16 +144,24 @@ export const updateRegistry = async (
}); });
} }
if (registryData?.serverId && registryData?.serverId !== "none") { try {
await execAsyncRemote(registryData.serverId, loginCommand); if (registryData?.serverId && registryData?.serverId !== "none") {
} else if (response?.registryType === "cloud") { await execAsyncRemote(registryData.serverId, loginCommand);
await execAsync(loginCommand); } else if (response?.registryType === "cloud") {
await execAsync(loginCommand);
}
} catch (execError) {
throw new Error(sanitizeRegistryError(execError, response?.password));
} }
return response; return response;
} catch (error) { } catch (error) {
const message = const message =
error instanceof Error ? error.message : "Error updating this registry"; error instanceof TRPCError
? error.message
: error instanceof Error
? error.message
: "Error updating this registry";
throw new TRPCError({ throw new TRPCError({
code: "BAD_REQUEST", code: "BAD_REQUEST",
message, message,

View File

@@ -1,6 +1,7 @@
import { findAllDeploymentsByApplicationId } from "@dokploy/server/services/deployment"; import { findAllDeploymentsByApplicationId } from "@dokploy/server/services/deployment";
import { import {
findRegistryByIdWithCredentials, findRegistryByIdWithCredentials,
safeDockerLoginCommand,
type Registry, type Registry,
} from "@dokploy/server/services/registry"; } from "@dokploy/server/services/registry";
import { createRollback } from "@dokploy/server/services/rollbacks"; import { createRollback } from "@dokploy/server/services/rollbacks";
@@ -117,9 +118,14 @@ const getRegistryCommands = (
imageName: string, imageName: string,
registryTag: string, registryTag: string,
): string => { ): string => {
const loginCmd = safeDockerLoginCommand(
registry.registryUrl,
registry.username,
registry.password,
);
return ` return `
echo "📦 [Enabled Registry] Uploading image to '${registry.registryType}' | '${registryTag}'" ; echo "📦 [Enabled Registry] Uploading image to '${registry.registryType}' | '${registryTag}'" ;
echo "${registry.password}" | docker login ${registry.registryUrl} -u '${registry.username}' --password-stdin || { ${loginCmd} || {
echo "❌ DockerHub Failed" ; echo "❌ DockerHub Failed" ;
exit 1; exit 1;
} }

View File

@@ -1,3 +1,4 @@
import { safeDockerLoginCommand } from "@dokploy/server/services/registry";
import type { ApplicationNested } from "../builders"; import type { ApplicationNested } from "../builders";
export const buildRemoteDocker = async (application: ApplicationNested) => { export const buildRemoteDocker = async (application: ApplicationNested) => {
@@ -13,7 +14,7 @@ echo "Pulling ${dockerImage}";
if (username && password) { if (username && password) {
command += ` command += `
if ! echo "${password}" | docker login --username "${username}" --password-stdin "${registryUrl || ""}" 2>&1; then if ! ${safeDockerLoginCommand(registryUrl || "", username, password)} 2>&1; then
echo "❌ Login failed"; echo "❌ Login failed";
exit 1; exit 1;
fi fi