mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-10-22 08:25:26 +00:00
fix: keep commands and secrets out of ExecError messages and inspect output
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
import { inspect } from "node:util";
|
||||
import { execAsync, ExecError } from "@dokploy/server/utils/process/execAsync";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("execAsync failure", () => {
|
||||
it("does not leak secrets embedded in the command", async () => {
|
||||
const secret = "s3cr3t-value";
|
||||
const error = await execAsync(`printf %s '${secret}' >/dev/null; exit 3`)
|
||||
.then(() => null)
|
||||
.catch((e) => e);
|
||||
|
||||
expect(error).toBeInstanceOf(ExecError);
|
||||
expect(error.exitCode).toBe(3);
|
||||
expect(error.message).not.toContain(secret);
|
||||
expect(inspect(error)).not.toContain(secret);
|
||||
});
|
||||
});
|
||||
@@ -25,6 +25,11 @@ export class ExecError extends Error {
|
||||
this.originalError = details.originalError;
|
||||
this.serverId = details.serverId;
|
||||
|
||||
// Keep the command (may embed secrets) out of console/inspect output.
|
||||
for (const key of ["command", "originalError"] as const) {
|
||||
Object.defineProperty(this, key, { enumerable: false });
|
||||
}
|
||||
|
||||
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, ExecError);
|
||||
|
||||
@@ -23,6 +23,10 @@ export { ExecError } from "./ExecError";
|
||||
|
||||
const execAsyncBase = util.promisify(exec);
|
||||
|
||||
// Node embeds the full command in error.message; it may carry secrets.
|
||||
const withoutCommand = (message: string, command: string) =>
|
||||
message.split(command).join("[command omitted]");
|
||||
|
||||
export const execAsync = async (
|
||||
command: string,
|
||||
options?: { cwd?: string; env?: NodeJS.ProcessEnv; shell?: string },
|
||||
@@ -42,13 +46,16 @@ export const execAsync = async (
|
||||
// @ts-expect-error
|
||||
const stderr = error.stderr?.toString() || "";
|
||||
|
||||
throw new ExecError(`Command execution failed: ${error.message}`, {
|
||||
command,
|
||||
stdout,
|
||||
stderr,
|
||||
exitCode,
|
||||
originalError: error,
|
||||
});
|
||||
throw new ExecError(
|
||||
`Command execution failed: ${withoutCommand(error.message, command)}`,
|
||||
{
|
||||
command,
|
||||
stdout,
|
||||
stderr,
|
||||
exitCode,
|
||||
originalError: error,
|
||||
},
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
@@ -71,13 +78,16 @@ export const execAsyncStream = (
|
||||
const childProcess = exec(command, options, (error) => {
|
||||
if (error) {
|
||||
reject(
|
||||
new ExecError(`Command execution failed: ${error.message}`, {
|
||||
command,
|
||||
stdout: stdoutComplete,
|
||||
stderr: stderrComplete,
|
||||
exitCode: error.code,
|
||||
originalError: error,
|
||||
}),
|
||||
new ExecError(
|
||||
`Command execution failed: ${withoutCommand(error.message, command)}`,
|
||||
{
|
||||
command,
|
||||
stdout: stdoutComplete,
|
||||
stderr: stderrComplete,
|
||||
exitCode: error.code,
|
||||
originalError: error,
|
||||
},
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user