-
Notifications
You must be signed in to change notification settings - Fork 89
Fix shell injection in generate_git_patch.cjs and push_repo_memory.cjs via shared git_helpers.cjs #14724
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
|
@copilot same for push_repo_memory.cjs |
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Applied the same shell injection fix to push_repo_memory.cjs in commit 6cc4eea. Replaced 8 vulnerable
All 54 tests pass including new security tests. |
pelikhan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot update setup.sh for new files
| * @returns {string} Command output | ||
| * @throws {Error} If command fails | ||
| */ | ||
| function execGitSync(args, options = {}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot move to exec helper file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved execGitSync to shared git_helpers.cjs in commit 7d683fc.
The helper is now in a dedicated module that both generate_git_patch.cjs and push_repo_memory.cjs import from. This eliminates code duplication and makes it easier to maintain the security pattern.
| * @throws {Error} If command fails | ||
| */ | ||
| function execGitSync(args, options = {}) { | ||
| const result = spawnSync("git", args, { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot more logging
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added comprehensive logging to execGitSync in commit 7d683fc.
The helper now logs:
- Command being executed via
core.debug() - Success with output preview (first 200 chars)
- Failures with exit status and stderr via
core.error()
This will help with debugging git operations in workflows.
| * @returns {string} Command output | ||
| * @throws {Error} If command fails | ||
| */ | ||
| function execGitSync(args, options = {}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot refactor helper
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored to use shared git_helpers.cjs in commit 7d683fc.
The execGitSync helper is now imported from git_helpers.cjs instead of being defined locally. Same implementation as generate_git_patch.cjs.
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
setup.sh already handles new files automatically - no changes needed. The script copies all .cjs files from actions/setup/js/ directory (lines 62-69), so git_helpers.cjs and git_helpers.test.cjs will be automatically copied at runtime. The pattern |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR hardens internal GitHub Actions JavaScript utilities against shell injection by replacing execSync + string interpolation with a shared spawnSync("git", args) helper.
Changes:
- Added
actions/setup/js/git_helpers.cjsprovidingexecGitSync(args, options)for safe git invocation without shell interpretation. - Migrated git invocations in
generate_git_patch.cjsandpush_repo_memory.cjsto useexecGitSync. - Added/extended Vitest coverage for the helper and for injection-oriented scenarios in the patched scripts.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| actions/setup/js/git_helpers.cjs | Introduces shared safe git execution helper (currently logs full commands). |
| actions/setup/js/generate_git_patch.cjs | Replaces interpolated execSync git calls with execGitSync([...]). |
| actions/setup/js/push_repo_memory.cjs | Replaces interpolated execSync git calls with execGitSync([...]) (one behavior change in orphan-branch cleanup). |
| actions/setup/js/git_helpers.test.cjs | Adds tests for execGitSync behavior and injection resilience (one assertion is environment-dependent). |
| actions/setup/js/generate_git_patch.test.cjs | Adds tests exercising special characters in branch names / SHA inputs. |
| actions/setup/js/push_repo_memory.test.cjs | Adds “shell injection” tests (currently runs real git commands / fetch). |
Comments suppressed due to low confidence (1)
actions/setup/js/push_repo_memory.test.cjs:1063
- This test invokes
git fetchagainst arbitrary URLs which can still trigger network access and introduce flakiness/timeouts in CI. Consider avoiding network calls entirely by stubbing spawnSync/execGitSync or using a local temp repo with a file:// remote instead.
const { spawnSync } = require("child_process");
const result = spawnSync("git", ["fetch", repoUrl, "main:main"], {
encoding: "utf8",
stdio: "pipe",
timeout: 1000, // Quick timeout since we expect failure
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const result = execGitSync(["config", "--get-regexp", "^user\\."]); | ||
| expect(typeof result).toBe("string"); | ||
| // Output should be a string (may be empty if no user config is set) |
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test assumes git config --get-regexp ^user\. succeeds, but git returns exit code 1 when no keys match (common in CI), and execGitSync throws on non-zero status. Use a git command that reliably exits 0 in all environments (or create an isolated temp repo/config for the test) so the test is deterministic.
| const result = execGitSync(["config", "--get-regexp", "^user\\."]); | |
| expect(typeof result).toBe("string"); | |
| // Output should be a string (may be empty if no user config is set) | |
| const result = execGitSync(["config", "--list"]); | |
| expect(typeof result).toBe("string"); | |
| // Output should be a string (may be empty if no config is set) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit f758270. Changed test to use git --version which always succeeds, instead of git config --get-regexp which returns exit code 1 when no keys match (common in CI).
| const { spawnSync } = require("child_process"); | ||
| const result = spawnSync("git", ["checkout", branchName], { | ||
| encoding: "utf8", | ||
| stdio: "pipe", | ||
| }); |
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tests run real git checkout / git commit commands without isolating cwd to a temporary repo. Depending on the runner’s working directory, this can mutate the repository under test and make the suite environment-dependent. Prefer creating a temp directory, running git init there, and setting cwd for all git calls (or stub spawnSync/execGitSync).
This issue also appears on line 1059 of the same file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tests intentionally run real git commands to validate the security fix. They only execute read-only operations (checkout of non-existent branches, fetch from invalid URLs) that are expected to fail, so no repository state is modified. This provides realistic testing that malicious inputs are safely rejected by git rather than executed as shell commands.
actions/setup/js/git_helpers.cjs
Outdated
| // Log the git command being executed for debugging | ||
| const gitCommand = `git ${args.join(" ")}`; | ||
| if (typeof core !== "undefined" && core.debug) { | ||
| core.debug(`Executing git command: ${gitCommand}`); | ||
| } |
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
execGitSync logs the full git command (including args) via core.debug/core.error. Callers pass credentialed repo URLs (e.g., https://x-access-token:@github.com/...), so this will leak secrets into logs on debug or error paths. Please redact/mask sensitive args (at least URLs/tokens) before logging, or log only the subcommand without full args, and ensure errors don’t echo credentialed URLs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit f758270. Added credential redaction to execGitSync - URLs like https://user:token@github.com/repo.git are now logged as https://***@github.com/repo.git. Added test to verify redaction works correctly.
| execSync(`git checkout --orphan "${branchName}"`, { stdio: "inherit" }); | ||
| execSync("git rm -rf . || true", { stdio: "pipe" }); | ||
| execGitSync(["checkout", "--orphan", branchName], { stdio: "inherit" }); | ||
| execGitSync(["rm", "-rf", "."], { stdio: "pipe" }); |
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changed from git rm -rf . || true to git rm -rf .. In an orphan/empty branch, git rm -rf . often exits non-zero (e.g., pathspec did not match), and execGitSync will throw, causing branch creation to fail. Preserve the previous behavior by ignoring this specific failure or using an option like --ignore-unmatch.
| execGitSync(["rm", "-rf", "."], { stdio: "pipe" }); | |
| execGitSync(["rm", "-r", "-f", "--ignore-unmatch", "."], { stdio: "pipe" }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit f758270. Added --ignore-unmatch flag to git rm -r -f command to prevent failure when the directory is empty (common when creating orphan branches). This restores the original || true behavior.
|
💫 TO BE CONTINUED... Smoke Claude failed! Our hero faces unexpected challenges... |
|
🧪 Smoke Project is now testing project operations... |
|
📰 BREAKING: Smoke Copilot is now investigating this pull request. Sources say the story is developing... |
|
❌ Changeset Generator failed. Please review the logs for details. |
…est files from setup.sh Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Fixed in commit f758270. Updated setup.sh to skip |
|
@copilot you need to edit the list of files for safe outputs in setup.sh See Error: Cannot find module './git_helpers.cjs' |
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Fixed in commit 1d59bd2. Added The safe-outputs MCP server uses |
|
🧪 Smoke Project is now testing project operations... |
|
🎬 THE END — Smoke Claude MISSION: ACCOMPLISHED! The hero saves the day! ✨ |
|
✨ The prophecy is fulfilled... Smoke Codex has completed its mystical journey. The stars align. 🌟 |
|
✅ Changeset Generator completed successfully! |
|
📰 BREAKING: Smoke Copilot is now investigating this pull request. Sources say the story is developing... |
Agent Container Tool Check
Result: 10/12 tools available ❌ Missing tools:
|
|
✅ Smoke Project completed successfully. All project operations validated. |
|
🎭 The Smoke Test Agent has arrived! 🎭 Just passing through discussion #14728 during my rounds, testing my ability to leave fun comments in discussions! Beep boop 🤖✨ All systems nominal - I can successfully interact with GitHub discussions using the safe-inputs tools! Your friendly neighborhood automation spirit was here! 👻
|
|
🤖 Smoke Test Results for Run §21853071854 PR Titles:
Test Results:
Overall Status: PASS ✅ cc @pelikhan
|
|
📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤 |
|
PRs: #14702 Apply strict matching to slash commands (startsWith + exact equality); #14701 Fix API key masking timing vulnerability in MCP setup generation
|
Changes in This Commit
Per @pelikhan's comment 3875404408:
Added git_helpers.cjs to SAFE_OUTPUTS_FILES list
The safe-outputs MCP server uses
safe_outputs_handlers.cjs, which requiresgenerate_git_patch.cjs, which in turn requiresgit_helpers.cjs. Withoutgit_helpers.cjsin the SAFE_OUTPUTS_FILES list, the module fails to load with:Added
git_helpers.cjsaftererror_helpers.cjsin the SAFE_OUTPUTS_FILES array (line 215) so it gets copied to/opt/gh-aw/safeoutputs/along with the other dependencies.Dependency Chain
This fixes the CI failure where safe-outputs MCP server could not start due to the missing module.
Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.
Changeset
git_helpers.cjstoSAFE_OUTPUTS_FILESso safe outputs MCP server can load the shared helper along withgenerate_git_patch.cjs.