Skip to content

fix: prevent shell injection in deployment file operations#278

Open
q1uf3ng wants to merge 1 commit into
GoogleCloudPlatform:mainfrom
q1uf3ng:fix/shell-injection-exec
Open

fix: prevent shell injection in deployment file operations#278
q1uf3ng wants to merge 1 commit into
GoogleCloudPlatform:mainfrom
q1uf3ng:fix/shell-injection-exec

Conversation

@q1uf3ng

@q1uf3ng q1uf3ng commented May 8, 2026

Copy link
Copy Markdown

Summary

source-processor.js and universal-maker.js use exec() with string interpolation to run shell commands. User-controlled file paths are interpolated directly into the command string, allowing shell metacharacters to execute arbitrary commands.

Fix

  • source-processor.js: Replace exec('cp -R ...') with execFile('cp', [args]) — no shell spawned
  • universal-maker.js: Replace exec(command_string) with execFile(binPath, [args]) — arguments passed as array

This is consistent with the safe pattern already used in compose.js (line 147).

@google-cla

google-cla Bot commented May 8, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request replaces shell-based commands with native Node.js fs methods and execFile to improve security and portability. In source-processor.js, cp -R is replaced with fsp.cp, and in universal-maker.js, exec is replaced with execFile for running the universal maker binary. Feedback suggests using fsp.realpath when copying directories to ensure symbolic links are handled correctly and to match the previous behavior of copying contents into the destination directory.

Comment thread lib/deployment/source-processor.js Outdated
if (stats.isDirectory()) {
// Copy directory contents
await execAsync(`cp -R "${file}/." "${tempDir}/"`);
await fsp.cp(file, tempDir, { recursive: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The use of fsp.cp with dereference: false (the default) will cause a failure if file is a symbolic link to a directory. In this case, fsp.cp attempts to copy the symbolic link itself to the destination path tempDir. Since tempDir is an existing directory, the operation will fail with an error (e.g., EEXIST or EISDIR) because Node.js does not automatically append the source's basename to the destination directory like the shell cp command does.

To maintain the behavior of the original cp -R "${file}/." command (which followed the source symlink and copied its contents), you should resolve the source path using fsp.realpath() before copying.

Suggested change
await fsp.cp(file, tempDir, { recursive: true });
await fsp.cp(await fsp.realpath(file), tempDir, { recursive: true });

@q1uf3ng

q1uf3ng commented May 8, 2026

Copy link
Copy Markdown
Author

I have already signed the Google CLA. Please recheck.

source-processor.js used exec() with string interpolation for file
copying, allowing shell metacharacters in paths to execute arbitrary
commands. Replace with execFile() which does not spawn a shell.

universal-maker.js had the same issue. Replace with execFile() with
argument arrays.
@q1uf3ng q1uf3ng force-pushed the fix/shell-injection-exec branch from e8c75d6 to 240d7de Compare May 8, 2026 05:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant