mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-09-12 19:51:00 +05:00
fix: close remaining backup credential and FTPS bypasses
This commit is contained in:
parent
2feccc3912
commit
c14cbcec71
@ -332,7 +332,18 @@ describe("FTP TLS certificate verification", () => {
|
||||
endpoint: "storage.example.com",
|
||||
};
|
||||
|
||||
test.each(["--ftp-no-check-certificate", "--no-check-certificate"])(
|
||||
test.each([
|
||||
"--ftp-no-check-certificate",
|
||||
"--ftp-no-check-certificate=true",
|
||||
"--ftp-no-check-certificate=TRUE",
|
||||
"--ftp-no-check-certificate=1",
|
||||
"--ftp-no-check-certificate=t",
|
||||
"--no-check-certificate",
|
||||
"--no-check-certificate=true",
|
||||
"--no-check-certificate=TRUE",
|
||||
"--no-check-certificate=1",
|
||||
"--no-check-certificate=t",
|
||||
])(
|
||||
"rejects certificate-verification bypass %s at schema validation",
|
||||
(flag) => {
|
||||
expect(
|
||||
@ -344,28 +355,38 @@ describe("FTP TLS certificate verification", () => {
|
||||
},
|
||||
);
|
||||
|
||||
test.each(["--ftp-no-check-certificate", "--no-check-certificate"])(
|
||||
"rejects certificate-verification bypass %s at runtime",
|
||||
async (flag) => {
|
||||
await expect(
|
||||
getRclonePathAndFlags(
|
||||
destination({
|
||||
provider: RCLONE_DESTINATION_PROVIDERS.FTP,
|
||||
endpoint: "storage.example.com",
|
||||
accessKey: "backup-user",
|
||||
secretAccessKey: "",
|
||||
region: "",
|
||||
bucket: "backups",
|
||||
additionalFlags: ["--ftp-explicit-tls", flag],
|
||||
}),
|
||||
),
|
||||
).rejects.toThrow("FTP TLS certificate verification cannot be disabled");
|
||||
},
|
||||
);
|
||||
test.each([
|
||||
"--ftp-no-check-certificate",
|
||||
"--ftp-no-check-certificate=TRUE",
|
||||
"--ftp-no-check-certificate=1",
|
||||
"--no-check-certificate",
|
||||
"--no-check-certificate=TRUE",
|
||||
"--no-check-certificate=1",
|
||||
])("rejects certificate-verification bypass %s at runtime", async (flag) => {
|
||||
await expect(
|
||||
getRclonePathAndFlags(
|
||||
destination({
|
||||
provider: RCLONE_DESTINATION_PROVIDERS.FTP,
|
||||
endpoint: "storage.example.com",
|
||||
accessKey: "backup-user",
|
||||
secretAccessKey: "",
|
||||
region: "",
|
||||
bucket: "backups",
|
||||
additionalFlags: ["--ftp-explicit-tls", flag],
|
||||
}),
|
||||
),
|
||||
).rejects.toThrow("FTP TLS certificate verification cannot be disabled");
|
||||
});
|
||||
|
||||
test.each([
|
||||
"--ftp-no-check-certificate=false",
|
||||
"--ftp-no-check-certificate=FALSE",
|
||||
"--ftp-no-check-certificate=0",
|
||||
"--ftp-no-check-certificate=f",
|
||||
"--no-check-certificate=false",
|
||||
"--no-check-certificate=FALSE",
|
||||
"--no-check-certificate=0",
|
||||
"--no-check-certificate=f",
|
||||
])("allows explicitly safe certificate flag %s", (flag) => {
|
||||
expect(
|
||||
apiCreateDestination.safeParse({
|
||||
@ -374,4 +395,16 @@ describe("FTP TLS certificate verification", () => {
|
||||
}).success,
|
||||
).toBe(true);
|
||||
});
|
||||
test.each([
|
||||
"--ftp-explicit-tls=TRUE",
|
||||
"--ftp-explicit-tls=1",
|
||||
"--ftp-explicit-tls=t",
|
||||
])("accepts pflag-compatible TLS true value %s", (flag) => {
|
||||
expect(
|
||||
apiCreateDestination.safeParse({
|
||||
...input,
|
||||
additionalFlags: [flag],
|
||||
}).success,
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@ -42,12 +42,32 @@ export const FTP_CERTIFICATE_VERIFICATION_REQUIRED_ERROR =
|
||||
export const SFTP_HOST_KEY_REQUIRED_ERROR =
|
||||
"SFTP destinations must verify the server host key. Add --sftp-known-hosts-file=/path/to/known_hosts.";
|
||||
|
||||
const parseBooleanFlagValue = (
|
||||
flag: string,
|
||||
flagName: string,
|
||||
): boolean | undefined => {
|
||||
if (flag === flagName) return true;
|
||||
const prefix = `${flagName}=`;
|
||||
if (!flag.startsWith(prefix)) return undefined;
|
||||
|
||||
const value = flag.slice(prefix.length).toLowerCase();
|
||||
if (["1", "t", "true"].includes(value)) return true;
|
||||
if (["0", "f", "false"].includes(value)) return false;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const getBooleanFlagValues = (flags: readonly string[], flagName: string) =>
|
||||
flags
|
||||
.filter((flag) => flag === flagName || flag.startsWith(`${flagName}=`))
|
||||
.map((flag) => parseBooleanFlagValue(flag, flagName));
|
||||
|
||||
const isBooleanFlagEnabled = (
|
||||
flags: readonly string[],
|
||||
flagName: string,
|
||||
): boolean =>
|
||||
(flags.includes(flagName) || flags.includes(`${flagName}=true`)) &&
|
||||
!flags.includes(`${flagName}=false`);
|
||||
): boolean => {
|
||||
const values = getBooleanFlagValues(flags, flagName);
|
||||
return values.length > 0 && values.every((value) => value === true);
|
||||
};
|
||||
|
||||
export const getFtpTlsState = (flags: readonly string[] | null | undefined) => {
|
||||
const values = flags ?? [];
|
||||
@ -62,7 +82,13 @@ export const hasDisabledFtpCertificateVerification = (
|
||||
): boolean => {
|
||||
const values = flags ?? [];
|
||||
return ["--ftp-no-check-certificate", "--no-check-certificate"].some(
|
||||
(flag) => values.includes(flag) || values.includes(`${flag}=true`),
|
||||
(flagName) => {
|
||||
const matchingValues = getBooleanFlagValues(values, flagName);
|
||||
return (
|
||||
matchingValues.length > 0 &&
|
||||
matchingValues.some((value) => value !== false)
|
||||
);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import { join } from "node:path";
|
||||
import { IS_CLOUD, paths } from "@dokploy/server/constants";
|
||||
import type { Destination } from "@dokploy/server/services/destination";
|
||||
import { quote } from "shell-quote";
|
||||
import { getSafeRcloneErrorMessage } from "../backups/redact";
|
||||
import { getRclonePathAndFlags } from "../backups/utils";
|
||||
import { execAsync } from "../process/execAsync";
|
||||
|
||||
@ -146,14 +147,9 @@ export const restoreWebServerBackup = async (
|
||||
await execAsync(`rm -rf ${quote([tempDir])}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
emit(
|
||||
`Error: ${
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Error restoring web server backup"
|
||||
}`,
|
||||
);
|
||||
throw error;
|
||||
const safeErrorMessage = getSafeRcloneErrorMessage(error);
|
||||
console.error("Restore error:", safeErrorMessage);
|
||||
emit(`Error: ${safeErrorMessage}`);
|
||||
throw new Error(safeErrorMessage);
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user