Skip to content

Commit f126871

Browse files
authored
fix: Escape shell arguments for safety (#10)
* fix: Escape shell arguments for safety * refactor: Template literal string concat in `escapeShellArg` function
1 parent f733bc1 commit f126871

File tree

1 file changed

+25
-5
lines changed

1 file changed

+25
-5
lines changed

backend/dockerMailserver.js

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ function debugLog(message, data = null) {
2222
}
2323
}
2424

25+
/**
26+
* Escapes a string for safe use in shell commands by wrapping it in single quotes
27+
* and escaping any single quotes within the string
28+
* @param {string} arg - Argument to escape
29+
* @return {string} Escaped argument safe for shell execution
30+
*/
31+
function escapeShellArg(arg) {
32+
// Replace single quotes with '\'' (end quote, escaped quote, start quote)
33+
// Then wrap the entire string in single quotes
34+
return `'${arg.replace(/'/g, "'\\''")}'`;
35+
}
36+
2537
/**
2638
* Executes a command in the docker-mailserver container
2739
* @param {string} command Command to execute
@@ -143,7 +155,9 @@ async function getAccounts() {
143155
async function addAccount(email, password) {
144156
try {
145157
debugLog(`Adding new email account: ${email}`);
146-
await execSetup(`email add ${email} ${password}`);
158+
await execSetup(
159+
`email add ${escapeShellArg(email)} ${escapeShellArg(password)}`
160+
);
147161
debugLog(`Account created: ${email}`);
148162
return { success: true, email };
149163
} catch (error) {
@@ -157,7 +171,9 @@ async function addAccount(email, password) {
157171
async function updateAccountPassword(email, password) {
158172
try {
159173
debugLog(`Updating password for account: ${email}`);
160-
await execSetup(`email update ${email} ${password}`);
174+
await execSetup(
175+
`email update ${escapeShellArg(email)} ${escapeShellArg(password)}`
176+
);
161177
debugLog(`Password updated for account: ${email}`);
162178
return { success: true, email };
163179
} catch (error) {
@@ -171,7 +187,7 @@ async function updateAccountPassword(email, password) {
171187
async function deleteAccount(email) {
172188
try {
173189
debugLog(`Deleting email account: ${email}`);
174-
await execSetup(`email del ${email}`);
190+
await execSetup(`email del ${escapeShellArg(email)}`);
175191
debugLog(`Account deleted: ${email}`);
176192
return { success: true, email };
177193
} catch (error) {
@@ -229,7 +245,9 @@ async function getAliases() {
229245
async function addAlias(source, destination) {
230246
try {
231247
debugLog(`Adding new alias: ${source} -> ${destination}`);
232-
await execSetup(`alias add ${source} ${destination}`);
248+
await execSetup(
249+
`alias add ${escapeShellArg(source)} ${escapeShellArg(destination)}`
250+
);
233251
debugLog(`Alias created: ${source} -> ${destination}`);
234252
return { success: true, source, destination };
235253
} catch (error) {
@@ -243,7 +261,9 @@ async function addAlias(source, destination) {
243261
async function deleteAlias(source, destination) {
244262
try {
245263
debugLog(`Deleting alias: ${source} => ${destination}`);
246-
await execSetup(`alias del ${source} ${destination}`);
264+
await execSetup(
265+
`alias del ${escapeShellArg(source)} ${escapeShellArg(destination)}`
266+
);
247267
debugLog(`Alias deleted: ${source} => ${destination}`);
248268
return { success: true, source, destination };
249269
} catch (error) {

0 commit comments

Comments
 (0)