{"sha":"a5db5ed156355e3088e7d1ceb0533d4bae922def","node_id":"C_kwDOBtnUP9oAKGE1ZGI1ZWQxNTYzNTVlMzA4OGU3ZDFjZWIwNTMzZDRiYWU5MjJkZWY","commit":{"author":{"name":"jc21","email":"jc@jc21.com","date":"2026-06-07T22:51:46Z"},"committer":{"name":"GitHub","email":"noreply@github.com","date":"2026-06-07T22:51:46Z"},"message":"Merge pull request #5498 from Yasha-ops/develop\n\nfix: Changed order of escape to prevent RCE","tree":{"sha":"c91f46c7f659fdfaa79017de4c44cd99725019fb","url":"https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/git/trees/c91f46c7f659fdfaa79017de4c44cd99725019fb"},"url":"https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/git/commits/a5db5ed156355e3088e7d1ceb0533d4bae922def","comment_count":0,"verification":{"verified":true,"reason":"valid","signature":"-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJqJfYCCRC1aQ7uu5UhlAAAotcQAFl+IN/GonWvGTgbDp3ed0rB\n2K5DJLE2vefWTE4y2ITvLbQHKSTtNFpbQdDY8+C1Z5iTmA4om38tkuxhjq8P76iL\np/0BeVJJBatUn5Bon9XE4zaIY8xeLTUcjKMPLJmStsal83kCXGhIAFqRolRxXxwz\n5JUmvFdMxdDRLJ0QprSWDMcNGwhgzU5+XTy95t9VOFnVyDyh1bCU9N470+cz6AEw\nm+x9f/2vgcZx0GQyFipKFYeefs+M7E4EX4hz0iA+FYMwhoXzdVbbBUOK2YeWsQ2a\nAKVk9lBoUakflAuAKHFGi0rflxTUuOundoH0rckgGUzwehf0Xr4t/wFAIjcxR3zv\nBA1TWPdsuQmaXJ13nPi/Y2aQKkP2rQwYKC4OSfrPIF+hwgl2kO5p3LF7Jmgwsx0a\nIUjEuc7uiVZt2d6BbRlT8nBhczKgn1VO86e5zbKxB2R5D2Unt1bh+yeZWsHhZUs+\n/SBxCDTt/aDXqsVEHcCH6fkdbWvX+TiQcajIJl5aKTW+gyBvWEfV98PEZUaRoRiO\n40Cb79CVGKCrLeInIgeHushmL6cRNa/CYRMqy20PS9FYYx3ov8WNOrQKPydUilgU\nmrMzWTWL4DtrUofAp26nXuRT2v/ibBLUd0ZXzlNsXHMtXm3OJoKuRo/roZcFyJaw\nq3iqYLvlufe3Ye3r7K0k\n=EBEX\n-----END PGP SIGNATURE-----\n","payload":"tree c91f46c7f659fdfaa79017de4c44cd99725019fb\nparent 101595b90b207dc63d8051420233e115db332fd5\nparent 45a3659ffdff2e7eabac0e1fb20ec224a65a13ce\nauthor jc21 <jc@jc21.com> 1780872706 +1000\ncommitter GitHub <noreply@github.com> 1780872706 +1000\n\nMerge pull request #5498 from Yasha-ops/develop\n\nfix: Changed order of escape to prevent RCE","verified_at":"2026-06-07T22:51:46Z"}},"url":"https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/commits/a5db5ed156355e3088e7d1ceb0533d4bae922def","html_url":"https://github.com/NginxProxyManager/nginx-proxy-manager/commit/a5db5ed156355e3088e7d1ceb0533d4bae922def","comments_url":"https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/commits/a5db5ed156355e3088e7d1ceb0533d4bae922def/comments","author":{"login":"jc21","id":1518257,"node_id":"MDQ6VXNlcjE1MTgyNTc=","avatar_url":"https://avatars.githubusercontent.com/u/1518257?v=4","gravatar_id":"","url":"https://api.github.com/users/jc21","html_url":"https://github.com/jc21","followers_url":"https://api.github.com/users/jc21/followers","following_url":"https://api.github.com/users/jc21/following{/other_user}","gists_url":"https://api.github.com/users/jc21/gists{/gist_id}","starred_url":"https://api.github.com/users/jc21/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jc21/subscriptions","organizations_url":"https://api.github.com/users/jc21/orgs","repos_url":"https://api.github.com/users/jc21/repos","events_url":"https://api.github.com/users/jc21/events{/privacy}","received_events_url":"https://api.github.com/users/jc21/received_events","type":"User","user_view_type":"public","site_admin":false},"committer":{"login":"web-flow","id":19864447,"node_id":"MDQ6VXNlcjE5ODY0NDQ3","avatar_url":"https://avatars.githubusercontent.com/u/19864447?v=4","gravatar_id":"","url":"https://api.github.com/users/web-flow","html_url":"https://github.com/web-flow","followers_url":"https://api.github.com/users/web-flow/followers","following_url":"https://api.github.com/users/web-flow/following{/other_user}","gists_url":"https://api.github.com/users/web-flow/gists{/gist_id}","starred_url":"https://api.github.com/users/web-flow/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/web-flow/subscriptions","organizations_url":"https://api.github.com/users/web-flow/orgs","repos_url":"https://api.github.com/users/web-flow/repos","events_url":"https://api.github.com/users/web-flow/events{/privacy}","received_events_url":"https://api.github.com/users/web-flow/received_events","type":"User","user_view_type":"public","site_admin":false},"parents":[{"sha":"101595b90b207dc63d8051420233e115db332fd5","url":"https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/commits/101595b90b207dc63d8051420233e115db332fd5","html_url":"https://github.com/NginxProxyManager/nginx-proxy-manager/commit/101595b90b207dc63d8051420233e115db332fd5"},{"sha":"45a3659ffdff2e7eabac0e1fb20ec224a65a13ce","url":"https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/commits/45a3659ffdff2e7eabac0e1fb20ec224a65a13ce","html_url":"https://github.com/NginxProxyManager/nginx-proxy-manager/commit/45a3659ffdff2e7eabac0e1fb20ec224a65a13ce"}],"stats":{"total":12,"additions":5,"deletions":7},"files":[{"sha":"c0418e170b205c42a03642913e74f36f7bf7b554","filename":"backend/setup.js","status":"modified","additions":5,"deletions":7,"changes":12,"blob_url":"https://github.com/NginxProxyManager/nginx-proxy-manager/blob/a5db5ed156355e3088e7d1ceb0533d4bae922def/backend%2Fsetup.js","raw_url":"https://github.com/NginxProxyManager/nginx-proxy-manager/raw/a5db5ed156355e3088e7d1ceb0533d4bae922def/backend%2Fsetup.js","contents_url":"https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/contents/backend%2Fsetup.js?ref=a5db5ed156355e3088e7d1ceb0533d4bae922def","patch":"@@ -6,6 +6,7 @@ import certificateModel from \"./models/certificate.js\";\n import settingModel from \"./models/setting.js\";\n import userModel from \"./models/user.js\";\n import userPermissionModel from \"./models/user_permission.js\";\n+import fs from \"fs/promises\";\n \n export const isSetup = async () => {\n \tconst row = await userModel.query().select(\"id\").where(\"is_deleted\", 0).first();\n@@ -120,18 +121,15 @@ const setupCertbotPlugins = async () => {\n \n \t\t\t\t// Make sure credentials file exists\n \t\t\t\tconst credentials_loc = `/etc/letsencrypt/credentials/credentials-${certificate.id}`;\n-\t\t\t\t// Escape single quotes and backslashes\n \t\t\t\tif (typeof certificate.meta.dns_provider_credentials === \"string\") {\n-\t\t\t\t\tconst escapedCredentials = certificate.meta.dns_provider_credentials\n-\t\t\t\t\t\t.replaceAll(\"'\", \"\\\\'\")\n-\t\t\t\t\t\t.replaceAll(\"\\\\\", \"\\\\\\\\\");\n-\t\t\t\t\tconst credentials_cmd = `[ -f '${credentials_loc}' ] || { mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo '${escapedCredentials}' > '${credentials_loc}' && chmod 600 '${credentials_loc}'; }`;\n-\t\t\t\t\tpromises.push(utils.exec(credentials_cmd));\n+\t\t\t\t\tpromises.push(fs.mkdir(\"/etc/letsencrypt/credentials\", { recursive: true })\n+\t\t\t\t\t\t\t\t  .then(() => fs.writeFile(credentials_loc, certificate.meta.dns_provider_credentials, { mode: 0o600, flag: \"wx\" }))\n+\t\t\t\t\t\t\t\t  .catch((err) => { if (err.code !== \"EEXIST\") throw err; }));\n \t\t\t\t}\n \t\t\t}\n \t\t\treturn true;\n \t\t});\n-\n+\t\t\n \t\tawait installPlugins(plugins);\n \n \t\tif (promises.length) {"}]}