From 46bf3274d811fd5ad02b7e0fbec587b73d4bc045 Mon Sep 17 00:00:00 2001 From: Yassine DAMIRI Date: Mon, 20 Apr 2026 01:00:43 +0200 Subject: [PATCH 1/2] fix: Changed order of escape to prevent RCE --- backend/setup.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/setup.js b/backend/setup.js index 84f42793ea..20d628ca97 100644 --- a/backend/setup.js +++ b/backend/setup.js @@ -123,8 +123,8 @@ const setupCertbotPlugins = async () => { // Escape single quotes and backslashes if (typeof certificate.meta.dns_provider_credentials === "string") { const escapedCredentials = certificate.meta.dns_provider_credentials - .replaceAll("'", "\\'") - .replaceAll("\\", "\\\\"); + .replaceAll("\\", "\\\\") + .replaceAll("'", "\\'"); const credentials_cmd = `[ -f '${credentials_loc}' ] || { mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo '${escapedCredentials}' > '${credentials_loc}' && chmod 600 '${credentials_loc}'; }`; promises.push(utils.exec(credentials_cmd)); } From 6629abed3250c5c82e2d1537a811f56f91c8f405 Mon Sep 17 00:00:00 2001 From: Yassine Damiri <54901715+Yasha-ops@users.noreply.github.com> Date: Mon, 1 Jun 2026 17:02:19 +0200 Subject: [PATCH 2/2] Use fs module for credential file operations Refactor (from the review) credential file handling to use fs module for directory creation and file writing --- backend/setup.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/backend/setup.js b/backend/setup.js index 20d628ca97..c46e5743e1 100644 --- a/backend/setup.js +++ b/backend/setup.js @@ -6,6 +6,7 @@ import certificateModel from "./models/certificate.js"; import settingModel from "./models/setting.js"; import userModel from "./models/user.js"; import userPermissionModel from "./models/user_permission.js"; +import fs from "fs"; export const isSetup = async () => { const row = await userModel.query().select("id").where("is_deleted", 0).first(); @@ -120,18 +121,14 @@ const setupCertbotPlugins = async () => { // Make sure credentials file exists const credentials_loc = `/etc/letsencrypt/credentials/credentials-${certificate.id}`; - // Escape single quotes and backslashes if (typeof certificate.meta.dns_provider_credentials === "string") { - const escapedCredentials = certificate.meta.dns_provider_credentials - .replaceAll("\\", "\\\\") - .replaceAll("'", "\\'"); - const credentials_cmd = `[ -f '${credentials_loc}' ] || { mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo '${escapedCredentials}' > '${credentials_loc}' && chmod 600 '${credentials_loc}'; }`; - promises.push(utils.exec(credentials_cmd)); + promises.push(fs.mkdir("/etc/letsencrypt/credentials", { recursive: true }) + .then(() => fs.writeFile(credentials_loc, certificate.meta.dns_provider_credentials, { mode: 0o600, flag: "wx" }))); } } return true; }); - + await installPlugins(plugins); if (promises.length) {