-
-
Notifications
You must be signed in to change notification settings - Fork 80
update proxy e2e tests for new ui #1644
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
return Boolean(containers.length); | ||
const dbRestore = () => { | ||
const restore = `${dockerCompose} exec db pg_restore --clean -U defguard -d defguard /tmp/db.dump`; | ||
execSync(restore); |
Check warning
Code scanning / CodeQL
Shell command built from environment values Medium
absolute path
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 7 hours ago
To fix this issue, we should avoid constructing the shell command using interpolation and passing it to execSync()
as a single string, as this allows shell interpretation of dynamic arguments such as file paths. Instead, we should use execFileSync()
and pass the command and its arguments as an array, which gives the shell no opportunity to misinterpret path values, regardless of what they contain.
Specifically, change the construction in the dbRestore
function on lines 12-15 so that:
- The base command (
docker
, notdockerCompose
) is used as the executable. - All subcommands ("compose", "-f",
<dockerFilePath>
, "exec", "db", "pg_restore", "--clean", "-U", "defguard", "-d", "defguard", "/tmp/db.dump") are passed as discrete arguments, with variables (paths) inserted only as elements of the array.
This requires adjusting both the use of dockerCompose
and string interpolation, to use proper argument arrays.
Summary:
- In the block for
dbRestore
, replace the dynamic/interpolated string forrestore
with an array of arguments forexecFileSync
. - Import
execFileSync
fromchild_process
(it is not currently imported), and use it in place ofexecSync
. - Similar changes should be considered for other uses of
execSync
with command strings containing dynamic paths (though only line 14 is flagged for now).
-
Copy modified line R1 -
Copy modified lines R13-R23
@@ -1,4 +1,4 @@ | ||
import { execSync } from 'child_process'; | ||
import { execSync, execFileSync } from 'child_process'; | ||
import path from 'path'; | ||
|
||
const defguardPath = __dirname.split('e2e')[0]; | ||
@@ -10,8 +10,17 @@ | ||
export const restorePath = path.resolve(defguardPath, 'e2e', 'defguard_backup.dump'); | ||
|
||
const dbRestore = () => { | ||
const restore = `${dockerCompose} exec db pg_restore --clean -U defguard -d defguard /tmp/db.dump`; | ||
execSync(restore); | ||
const args = [ | ||
'compose', | ||
'-f', dockerFilePath, | ||
'exec', 'db', | ||
'pg_restore', | ||
'--clean', | ||
'-U', 'defguard', | ||
'-d', 'defguard', | ||
'/tmp/db.dump' | ||
]; | ||
execFileSync('docker', args, { stdio: 'inherit' }); | ||
}; | ||
|
||
export const dockerRestart = () => { |
const wait_for_db = `${dockerCompose} exec db sh -c 'until pg_isready; do sleep 1; done'`; | ||
execSync(wait_for_db); | ||
} | ||
execSync(`${dockerCompose} stop core`); |
Check warning
Code scanning / CodeQL
Shell command built from environment values Medium
absolute path
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 7 hours ago
To fix this vulnerability, commands executed by execSync
should be refactored to avoid shell interpolation of environment-derived strings (a file path, in this case). Instead of constructing a shell command string with the file path embedded, use execFileSync
from the child_process
module, which accepts commands and arguments as separate parameters, preventing the shell from interpreting special characters in dynamic data. In file e2e/utils/docker.ts
, change calls such as execSync(
${dockerCompose} stop core)
to use execFileSync
, passing the command and relevant arguments as an array. This requires splitting up ${dockerCompose}
(which is itself a string) into its components:
- command: 'docker'
- arguments: ['compose', '-f', dockerFilePath, 'stop', 'core']
Do the same for all similar cases in the file, including dbRestore and dockerRestart steps.
The refactor only needs to change lines calling execSync
with shell-constructed string; other path calculation code remains untouched.
You'll also need to import execFileSync
instead of (or in addition to) execSync
.
Update the method implementations so all shell commands provide their dynamic data as arguments.
-
Copy modified line R1 -
Copy modified lines R8-R9 -
Copy modified lines R14-R24 -
Copy modified line R28 -
Copy modified line R30
@@ -1,21 +1,31 @@ | ||
import { execSync } from 'child_process'; | ||
import { execFileSync } from 'child_process'; | ||
import path from 'path'; | ||
|
||
const defguardPath = __dirname.split('e2e')[0]; | ||
|
||
const dockerFilePath = path.resolve(defguardPath, 'docker-compose.e2e.yaml'); | ||
|
||
export const dockerCompose = `docker compose -f ${dockerFilePath}`; | ||
export const dockerComposeCmd = 'docker'; | ||
export const dockerComposeArgsBase = ['compose', '-f', dockerFilePath]; | ||
|
||
export const restorePath = path.resolve(defguardPath, 'e2e', 'defguard_backup.dump'); | ||
|
||
const dbRestore = () => { | ||
const restore = `${dockerCompose} exec db pg_restore --clean -U defguard -d defguard /tmp/db.dump`; | ||
execSync(restore); | ||
const restoreArgs = [ | ||
...dockerComposeArgsBase, | ||
'exec', | ||
'db', | ||
'pg_restore', | ||
'--clean', | ||
'-U', 'defguard', | ||
'-d', 'defguard', | ||
'/tmp/db.dump' | ||
]; | ||
execFileSync(dockerComposeCmd, restoreArgs, { stdio: 'inherit' }); | ||
}; | ||
|
||
export const dockerRestart = () => { | ||
execSync(`${dockerCompose} stop core`); | ||
execFileSync(dockerComposeCmd, [...dockerComposeArgsBase, 'stop', 'core'], { stdio: 'inherit' }); | ||
dbRestore(); | ||
execSync(`${dockerCompose} start core`); | ||
execFileSync(dockerComposeCmd, [...dockerComposeArgsBase, 'start', 'core'], { stdio: 'inherit' }); | ||
}; |
} | ||
execSync(`${dockerCompose} stop core`); | ||
dbRestore(); | ||
execSync(`${dockerCompose} start core`); |
Check warning
Code scanning / CodeQL
Shell command built from environment values Medium
absolute path
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 7 hours ago
To fix this vulnerability, the shell command must avoid passing dynamic paths as part of the command string. Instead, use the execFileSync
method from child_process
, which allows you to specify the command and its arguments as an array, ensuring that special shell characters in paths are correctly handled as arguments and not interpreted by the shell.
- Edit all use of
execSync
shell commands that interpolatedockerFilePath
or otherwise use paths derived from the environment. - Specifically, update the
dockerRestart
function (lines 17–21) anddbRestore
(lines 12–15), replacing commands like`${dockerCompose} start core`
with argument arrays. - To do so, construct argument arrays representing:
["compose", "-f", dockerFilePath, "stop", "core"]
,["compose", "-f", dockerFilePath, "start", "core"]
, etc. - Change all corresponding
execSync
calls toexecFileSync
. - Import
execFileSync
fromchild_process
at the top of the file.
-
Copy modified line R1 -
Copy modified lines R8-R9 -
Copy modified lines R14-R26 -
Copy modified line R30 -
Copy modified line R32
@@ -1,21 +1,33 @@ | ||
import { execSync } from 'child_process'; | ||
import { execSync, execFileSync } from 'child_process'; | ||
import path from 'path'; | ||
|
||
const defguardPath = __dirname.split('e2e')[0]; | ||
|
||
const dockerFilePath = path.resolve(defguardPath, 'docker-compose.e2e.yaml'); | ||
|
||
export const dockerCompose = `docker compose -f ${dockerFilePath}`; | ||
export const dockerComposeCmd = "docker"; | ||
export const dockerComposeArgsBase = ["compose", "-f", dockerFilePath]; | ||
|
||
export const restorePath = path.resolve(defguardPath, 'e2e', 'defguard_backup.dump'); | ||
|
||
const dbRestore = () => { | ||
const restore = `${dockerCompose} exec db pg_restore --clean -U defguard -d defguard /tmp/db.dump`; | ||
execSync(restore); | ||
const args = [ | ||
...dockerComposeArgsBase, | ||
"exec", | ||
"db", | ||
"pg_restore", | ||
"--clean", | ||
"-U", | ||
"defguard", | ||
"-d", | ||
"defguard", | ||
"/tmp/db.dump" | ||
]; | ||
execFileSync(dockerComposeCmd, args, { stdio: 'inherit' }); | ||
}; | ||
|
||
export const dockerRestart = () => { | ||
execSync(`${dockerCompose} stop core`); | ||
execFileSync(dockerComposeCmd, [...dockerComposeArgsBase, "stop", "core"], { stdio: 'inherit' }); | ||
dbRestore(); | ||
execSync(`${dockerCompose} start core`); | ||
execFileSync(dockerComposeCmd, [...dockerComposeArgsBase, "start", "core"], { stdio: 'inherit' }); | ||
}; |
// Start Defguard stack with docker compose. | ||
export const dockerUp = () => { | ||
const command = `${dockerCompose} up --wait`; | ||
execSync(command); |
Check warning
Code scanning / CodeQL
Shell command built from environment values Medium
absolute path
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 7 hours ago
General approach:
Replace the use of execSync
with dynamically built shell commands with the safer alternative spawnSync
(or execFileSync
), providing the command and arguments as separate array entries. This avoids the shell interpreting the command string and any included special characters.
Detailed steps:
-
In
dockerUp
ine2e/utils/setup.ts
, instead of creating a literal string (command = ...
) and passing it toexecSync
, construct the Docker command and its arguments as an array, and callspawnSync
orexecFileSync
withshell: false
. -
Adapt the call so that the executable is
docker
, the arguments are the split up version ofdockerCompose
plus the actual operation (up
,--wait
), and the file path (-f
, ...). -
As
dockerCompose
is defined as a string likedocker compose -f ${dockerFilePath}
, for this fix, we should export the split-out elements instead (e.g., separate the command from its arguments).
However, since we can only edit the provided snippets, and sincedockerCompose
is imported from the other file, we cannot change its representation in general—so we must minimally adjust within the constraints. -
The best edit, given the constraints, is to parse the space-separated
dockerCompose
into command and arguments directly in the edited region, or reconstruct them locally to remove string interpolation of untrusted data. -
After splitting/extracting, run with
spawnSync
instead ofexecSync
for consistency and direct control of arguments.
Required changes:
- In
e2e/utils/setup.ts
, update the implementation ofdockerUp
(lines 20-22) to:- Replace the use of
execSync(command)
with a safe call tospawnSync
, providing the exec and args as an array, not through the shell. - Parse
dockerCompose
safely to extractdocker
, arguments, and append theup
/--wait
suffix.
- Replace the use of
- Handle errors (throw if process fails, matching prior behavior).
- No additional imports are required, as
spawnSync
andexecSync
are already imported.
-
Copy modified lines R20-R28
@@ -17,8 +17,15 @@ | ||
|
||
// Start Defguard stack with docker compose. | ||
export const dockerUp = () => { | ||
const command = `${dockerCompose} up --wait`; | ||
execSync(command); | ||
// Split dockerCompose into command and args to avoid shell interpolation | ||
const composeParts = dockerCompose.split(' '); | ||
const cmd = composeParts[0]; // 'docker' | ||
const baseArgs = composeParts.slice(1); // ['compose', '-f', '/abs/path/to/docker-compose.e2e.yaml'] | ||
const args = [...baseArgs, 'up', '--wait']; | ||
const res = spawnSync(cmd, args, { stdio: 'inherit' }); | ||
if (res.error) { | ||
throw res.error; | ||
} | ||
createSnapshot(); | ||
}; | ||
|
// clear the db dump file | ||
const teardownFunction = async (_: FullConfig) => { | ||
const command = `${dockerCompose} down`; | ||
execSync(command); |
Check warning
Code scanning / CodeQL
Shell command built from environment values Medium
absolute path
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 7 hours ago
To fix this issue, instead of building a shell command string by concatenating environment-derived paths, we should use execFileSync
, which executes the file directly with argument vectors instead of parsing a shell string. This ensures all special shell characters in arguments are handled safely, and cannot cause command injection.
- In
e2e/utils/docker.ts
, refactor usages ofexecSync
where shell commands are built with tainted paths (including the exporteddockerCompose
string). - Instead of exporting a string command (
dockerCompose
), export an object containing the command, and file path argument separately, i.e.:
{ cmd: 'docker', args: ['compose', '-f', dockerFilePath] }
- In dependent files (such as
e2e/utils/teardown.ts
), destructure and use those values withexecFileSync
, using the arguments array and adding any further CLI args (like"down"
) directly to the argument array. - Add any missing imports of
execFileSync
(and preserveexecSync
only if needed for legacy code).
You only need to update code that is directly shown in the snippets.
- In
e2e/utils/docker.ts
, update the export and relevant usages. - In
e2e/utils/teardown.ts
, update to useexecFileSync
, using arguments array.
No functionality change—just a safer way to invoke the same commands.
-
Copy modified line R2 -
Copy modified lines R9-R13
@@ -1,13 +1,16 @@ | ||
/* eslint-disable @typescript-eslint/no-unused-vars */ | ||
import { execSync } from 'child_process'; | ||
import { execFileSync } from 'child_process'; | ||
import { FullConfig } from 'playwright/test'; | ||
|
||
import { dockerCompose } from './docker'; | ||
|
||
// clear the db dump file | ||
const teardownFunction = async (_: FullConfig) => { | ||
const command = `${dockerCompose} down`; | ||
execSync(command); | ||
execFileSync( | ||
dockerCompose.cmd, | ||
[...dockerCompose.args, 'down'], | ||
{ stdio: 'inherit' } | ||
); | ||
}; | ||
|
||
export default teardownFunction; |
-
Copy modified line R1 -
Copy modified lines R8-R11 -
Copy modified lines R16-R31 -
Copy modified line R35 -
Copy modified line R37
@@ -1,21 +1,38 @@ | ||
import { execSync } from 'child_process'; | ||
import { execSync, execFileSync } from 'child_process'; | ||
import path from 'path'; | ||
|
||
const defguardPath = __dirname.split('e2e')[0]; | ||
|
||
const dockerFilePath = path.resolve(defguardPath, 'docker-compose.e2e.yaml'); | ||
|
||
export const dockerCompose = `docker compose -f ${dockerFilePath}`; | ||
export const dockerCompose = { | ||
cmd: 'docker', | ||
args: ['compose', '-f', dockerFilePath], | ||
}; | ||
|
||
export const restorePath = path.resolve(defguardPath, 'e2e', 'defguard_backup.dump'); | ||
|
||
const dbRestore = () => { | ||
const restore = `${dockerCompose} exec db pg_restore --clean -U defguard -d defguard /tmp/db.dump`; | ||
execSync(restore); | ||
execFileSync( | ||
dockerCompose.cmd, | ||
[ | ||
...dockerCompose.args, | ||
'exec', | ||
'db', | ||
'pg_restore', | ||
'--clean', | ||
'-U', | ||
'defguard', | ||
'-d', | ||
'defguard', | ||
'/tmp/db.dump', | ||
], | ||
{ stdio: 'inherit' } | ||
); | ||
}; | ||
|
||
export const dockerRestart = () => { | ||
execSync(`${dockerCompose} stop core`); | ||
execFileSync(dockerCompose.cmd, [...dockerCompose.args, 'stop', 'core'], { stdio: 'inherit' }); | ||
dbRestore(); | ||
execSync(`${dockerCompose} start core`); | ||
execFileSync(dockerCompose.cmd, [...dockerCompose.args, 'start', 'core'], { stdio: 'inherit' }); | ||
}; |
No description provided.