chore(lambda-nodejs): input sanitisation for bundling options#37019
chore(lambda-nodejs): input sanitisation for bundling options#37019vishaalmehrishi wants to merge 14 commits intomainfrom
Conversation
✅ Updated pull request passes all PRLinter validations. Dismissing previous PRLinter review.
eabb485 to
9b5fd2d
Compare
496ad55 to
de35360
Compare
…ling Refactored local bundling to execute esbuild via spawnSync with shell=false, passing arguments as an array instead of interpolating into a shell command string. For Docker bundling, all esbuild arguments are shell-escaped before interpolation into the bash -c command string. - Added execDirect() and shellEscapeForBundlingCommand() utilities - Extracted buildEsbuildArgs() for reuse across both bundling paths - Added runBinCommandAsArray() to PackageManager - Refactored toCliArgs() to return string[] without shell quoting - Refactored getLocalBundlingProvider() into 3 phases (hooks/tsc, esbuild, deps) - Added comprehensive tests for shell metacharacter handling
| * Spawn sync with error handling. | ||
| * | ||
| * NOTE: This function is typically used with a shell interpreter (bash/cmd) as the command, | ||
| * which means the args are interpreted by the shell. When user input is involved, |
Check warning
Code scanning / CodeQL
Shell command built from environment values Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 days ago
General approach: Avoid giving the shell a command string containing unescaped, user‑influenced values. Either pass arguments directly as an array (bypassing the shell) or, if we must use bash -c / cmd /c, rigorously escape any data that can contain special characters before embedding it into the command string.
Best fix here: We already have shellEscapeForBundlingCommand(value, platform) and we already use it for the esbuild command. The remaining surface that CodeQL traces into is the OsCommand helper plus the chain combination used to build depsCommand and then localCommand. The tainted data are:
filePath,src,dest,dirpath arguments inOsCommandmethods.datainOsCommand.write/writeJson(JSON ofdependenciesbuilt fromextractDependencies).- The resulting per-step command strings combined by
chainand finally passed aslocalCommandtobash -c/cmd /c.
We can fix this without altering observable behavior by:
- Making
OsCommandbuild its commands usingshellEscapeForBundlingCommandfor both Unix and Windows paths/data instead of naive"${...}"or raw'${data}'. This prevents quotes, semicolons,&&, etc., in file paths or JSON data from breaking out of their intended position in the command. - Leaving
chainas a simple join, but ensuring that every command fragment it receives is already correctly shell‑escaped by construction. SincedepsCommandelements come exclusively fromOsCommandandpackageManager.installCommand.join(' ')(controlled by the library), tighteningOsCommandis sufficient for the tainted flow CodeQL identified.
This keeps the overall structure the same: we still generate one string localCommand and pass it to bash -c/cmd /c, but now all user‑influenced values embedded in that string are safely escaped, so the “shell command built from environment values” issue is mitigated.
Concretely:
- In
packages/aws-cdk-lib/aws-lambda-nodejs/lib/bundling.ts, within theOsCommandclass, update all string templates that interpolatefilePath,src,dest,dir, ordatato first run them throughshellEscapeForBundlingCommand(…, this.osPlatform)and then use those escaped values. For Unix, this replaces manual'${data}'quoting with robust escaping; for Windows, it protects paths and content when invoking viacmd /c. No other files need changes.
…ate methods Split getLocalBundlingProvider into: - executePreBundlingCommands: hooks and tsc pre-compilation (shell) - executeEsbuild: direct spawnSync without shell - executePostBundlingCommands: deps installation and hooks (shell)
…ution Extract executeShellCommands() helper that encapsulates the win32/posix shell selection, flag, windowsVerbatimArguments, and shared exec options. Used by both executePreBundlingCommands and executePostBundlingCommands.
…bundling Use a single shell session for local bundling (same as Docker) so that environment variables and shell state propagate across hooks, tsc, esbuild, and deps installation. Esbuild arguments are shell-escaped via shellEscapeForBundlingCommand() to neutralize metacharacters while preserving backward compatibility for hooks. This removes the need for execDirect(), runBinCommandAsArray(), and the multi-phase execution model. Both bundling paths now share the same createBundlingCommand() logic with shell escaping.
Issue # (if applicable)
Closes #.
Reason for this change
Description of changes
Describe any new or updated permissions being added
Description of how you validated changes
Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license