fix: use bash and warn about shell injection (issue #12836)#12871
fix: use bash and warn about shell injection (issue #12836)#12871ambicuity wants to merge 1 commit into
Conversation
| it("should default to bash if available, not sh", () => { | ||
| // Current implementation defaults to 'sh' if SHELL is not set or if it falls through. | ||
| // We want to verify this behavior (and then changes it). | ||
|
|
||
| // This test will fail after the fix if we assert it IS bash, | ||
| // so we start by asserting the CURRENT broken state (or checking what it is). | ||
|
|
||
| const { shell } = getShellConfig(); | ||
| const baseName = path.basename(shell); | ||
|
|
||
| // If the system has bash, we want to use it. | ||
| // On most *nix systems, bash is available. | ||
| const bashAvailable = spawnSync("which", ["bash"]).status === 0; | ||
|
|
||
| if (bashAvailable) { | ||
| console.log(`Bash is available. Current shell config uses: ${baseName}`); | ||
| } | ||
| }); |
There was a problem hiding this comment.
Tests never assert
This test only logs when bash is available and has no expect(...), so it will always pass even if getShellConfig() regresses. Same issue pattern appears in the third test where the sh/dash branch only logs and doesn’t assert failure/success. These should be turned into deterministic assertions (by controlling process.env.SHELL/PATH) so CI actually verifies the intended bash-preference behavior.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/shell-integration.test.ts
Line: 7:24
Comment:
**Tests never assert**
This test only logs when `bash` is available and has no `expect(...)`, so it will always pass even if `getShellConfig()` regresses. Same issue pattern appears in the third test where the `sh/dash` branch only logs and doesn’t assert failure/success. These should be turned into deterministic assertions (by controlling `process.env.SHELL`/`PATH`) so CI actually verifies the intended bash-preference behavior.
How can I resolve this? If you propose a fix, please make it concise.| // Fish rejects common bashisms used by tools, so prefer bash when detected. | ||
| // Fish rejects common bashisms used by tools, so prefer bash when detected. | ||
| if (shellName === "fish") { | ||
| const bash = resolveShellFromPath("bash"); |
There was a problem hiding this comment.
Duplicate comment line
The fish-shell comment is duplicated back-to-back, which looks accidental and adds noise. Removing the duplicate would keep the intent clear.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/shell-utils.ts
Line: 37:40
Comment:
**Duplicate comment line**
The fish-shell comment is duplicated back-to-back, which looks accidental and adds noise. Removing the duplicate would keep the intent clear.
How can I resolve this? If you propose a fix, please make it concise.
Additional Comments (1)
Prompt To Fix With AIThis is a comment left during a code review.
Path: src/agents/shell-utils.test.ts
Line: 75:80
Comment:
**Outdated contract in test**
`getShellConfig()` now prefers `bash` when `SHELL` is unset (if `bash` is found on `PATH`). This test hard-codes `PATH = ""` and expects `"sh"`, which no longer reflects the new behavior in typical environments and also doesn’t cover the new preferred-bash path. Consider splitting into two deterministic cases: (1) `SHELL` unset + `PATH` contains `bash` → expect `bash`; (2) `SHELL` unset + `PATH` contains no `bash` → expect `sh`.
How can I resolve this? If you propose a fix, please make it concise. |
There was a problem hiding this comment.
Pull request overview
Updates embedded-agent shell execution guidance and defaults to reduce script breakage (bashisms under /bin/sh) and mitigate shell injection risks by encouraging env usage.
Changes:
- Prefer
bashovershingetShellConfig()whenSHELLis unset or explicitlysh, while keeping the existingfishfallback behavior. - Add a system-prompt warning to use the exec tool’s
envparameter for dynamic data to reduce shell injection risk. - Add a new integration-style test file for shell selection behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| src/agents/system-prompt.ts | Adds exec tool guidance to pass dynamic data via env to reduce shell injection risk. |
| src/agents/shell-utils.ts | Adjusts shell selection to upgrade sh/unset SHELL to bash when available; keeps fish handling. |
| src/agents/shell-integration.test.ts | Introduces tests intended to validate the new shell selection behavior (currently has correctness/portability issues). |
| PR_DESCRIPTION_12836.md | Adds a standalone markdown description of the PR and motivation. |
| @@ -35,6 +35,7 @@ export function getShellConfig(): { shell: string; args: string[] } { | |||
| const envShell = process.env.SHELL?.trim(); | |||
| const shellName = envShell ? path.basename(envShell) : ""; | |||
| // Fish rejects common bashisms used by tools, so prefer bash when detected. | |||
| // Fish rejects common bashisms used by tools, so prefer bash when detected. | |||
There was a problem hiding this comment.
The fish-shell comment is duplicated on two consecutive lines, which looks like an accidental copy/paste. Remove the duplicate line to avoid noise and keep future diffs clean.
| // Fish rejects common bashisms used by tools, so prefer bash when detected. |
| it("should default to bash if available, not sh", () => { | ||
| // Current implementation defaults to 'sh' if SHELL is not set or if it falls through. | ||
| // We want to verify this behavior (and then changes it). | ||
|
|
||
| // This test will fail after the fix if we assert it IS bash, | ||
| // so we start by asserting the CURRENT broken state (or checking what it is). | ||
|
|
||
| const { shell } = getShellConfig(); | ||
| const baseName = path.basename(shell); | ||
|
|
||
| // If the system has bash, we want to use it. | ||
| // On most *nix systems, bash is available. | ||
| const bashAvailable = spawnSync("which", ["bash"]).status === 0; | ||
|
|
||
| if (bashAvailable) { | ||
| console.log(`Bash is available. Current shell config uses: ${baseName}`); | ||
| } | ||
| }); |
There was a problem hiding this comment.
This test doesn't assert anything (it only logs when bash is available), so it will always pass and won't catch regressions. Replace the console logging with an assertion of the expected shell selection, or remove the test if it's not providing signal.
| expect(baseName).toBe("bash"); | ||
| } | ||
| } finally { | ||
| process.env.SHELL = originalShell; |
There was a problem hiding this comment.
The finally block restores process.env.SHELL via assignment even when the original value was undefined. In Node, assigning undefined to an env var typically results in the string "undefined", which can leak into later tests and change behavior. Use delete process.env.SHELL when originalShell == null, matching the pattern used in shell-utils.test.ts.
| process.env.SHELL = originalShell; | |
| if (originalShell == null) { | |
| delete process.env.SHELL; | |
| } else { | |
| process.env.SHELL = originalShell; | |
| } |
| it("should handle bash-isms if using bash", () => { | ||
| const { shell, args } = getShellConfig(); | ||
| // Bash-specific syntax: Arrays, [[ ]] | ||
| const command = 'if [[ "a" == "a" ]]; then echo "success"; else echo "fail"; fi'; | ||
|
|
||
| const result = spawnSync(shell, [...args, command], { encoding: "utf-8" }); | ||
|
|
||
| if (path.basename(shell) === "sh" || path.basename(shell) === "dash") { | ||
| // sh/dash usually fails on [[ ]] | ||
| // expect(result.status).not.toBe(0); | ||
| console.log("Running bash-ism on", shell, "result status:", result.status); | ||
| } else { | ||
| expect(result.status).toBe(0); | ||
| expect(result.stdout.trim()).toBe("success"); | ||
| } |
There was a problem hiding this comment.
This test will fail on Windows CI: getShellConfig() returns PowerShell on win32, but the test runs a bash-specific command and then expects exit code 0 for any shell that's not sh/dash. Guard/skip this test on win32 (and potentially when the selected shell isn't bash), or force a bash shell in the test via a controlled PATH like shell-utils.test.ts does.
| // If the system has bash, we want to use it. | ||
| // On most *nix systems, bash is available. | ||
| const bashAvailable = spawnSync("which", ["bash"]).status === 0; | ||
|
|
||
| if (bashAvailable) { | ||
| console.log(`Bash is available. Current shell config uses: ${baseName}`); | ||
| } | ||
| }); | ||
|
|
||
| it("should upgrade sh to bash if bash is available", () => { | ||
| // Simulate SHELL=/bin/sh | ||
| const originalShell = process.env.SHELL; | ||
| process.env.SHELL = "/bin/sh"; | ||
|
|
||
| try { | ||
| const { shell } = getShellConfig(); | ||
| const baseName = path.basename(shell); | ||
| const bashAvailable = spawnSync("which", ["bash"]).status === 0; | ||
|
|
There was a problem hiding this comment.
Using spawnSync("which", ["bash"]) makes the test non-portable (Windows runners typically don't have which) and non-deterministic (depends on the runner image). Prefer a deterministic setup by creating a temp PATH entry containing a fake bash (as done in shell-utils.test.ts) and asserting getShellConfig() returns that path.
4ca45c3 to
702fc23
Compare
bfc1ccb to
f92900f
Compare
|
This pull request has been automatically marked as stale due to inactivity. |
702fc23 to
9e4ed6d
Compare
PR Description: Fix Embedded Agent Script Generation (Issue #12836)
Problem
The embedded agent was generating broken scripts due to:
/bin/sh(which can bedashor other minimal shells) even whenbashis available. This caused scripts using bash-specific syntax (e.g.,[[ ]], arrays,set -o pipefail) to fail.Changes
src/agents/shell-utils.ts: UpdatedgetShellConfigto explicitly preferbashif available on the system, falling back toshonly ifbashis not found. Also restored specific invalidation logic forfishshell users to ensure they fall back tobashorshproperly.src/agents/system-prompt.ts: Updated theexectool description to include a warning: "use 'env' param for dynamic data to avoid shell injection". This instructs the agent to use environment variables for passing data safely.Verification
src/agents/shell-integration.test.tsto verify thatgetShellConfigupgradesshtobashwhen valid.src/agents/shell-utils.test.tswas updated/verified to ensurefishshell users still get a valid compatible shell.pnpm testto ensure no regressions across the codebase.Greptile Overview
Greptile Summary
This PR updates shell selection so agent-generated scripts run under
bashwhen available (avoiding/bin/shincompatibilities), keeps special handling forfish, and adds a prompt warning to pass dynamic data via the exec tool’senvparameter to reduce shell injection risk.Main concern is test quality/coverage:
src/agents/shell-integration.test.tscurrently includes tests that don’t assert (or only assert conditionally), so CI can pass even if the behavior regresses. Additionally, existing unit tests should be updated to reflect the new “prefer bash when SHELL is unset or sh” contract in a deterministic way by controllingPATH.Confidence Score: 3/5
(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!