Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Feb 10, 2026

  • Analyze the security issue in generate_git_patch.cjs
  • Identify all vulnerable execSync calls using string interpolation
  • Refactor execSync to use safe command-args array syntax
  • Add security test cases for shell injection prevention
  • Run linting and tests to verify changes
  • Manual verification of the fix
  • Apply same fix to push_repo_memory.cjs per @pelikhan's request
  • Move execGitSync helper to shared git_helpers.cjs
  • Add enhanced logging to execGitSync
  • Update setup.sh (already copies all .cjs files automatically)
  • Fix credential logging in git_helpers.cjs
  • Fix git rm failure on empty orphan branches
  • Fix flaky test in git_helpers.test.cjs
  • Exclude test files from setup.sh deployment
  • Add git_helpers.cjs to SAFE_OUTPUTS_FILES list

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 requires generate_git_patch.cjs, which in turn requires git_helpers.cjs. Without git_helpers.cjs in the SAFE_OUTPUTS_FILES list, the module fails to load with:

Error: Cannot find module './git_helpers.cjs'

Added git_helpers.cjs after error_helpers.cjs in the SAFE_OUTPUTS_FILES array (line 215) so it gets copied to /opt/gh-aw/safeoutputs/ along with the other dependencies.

Dependency Chain

safe_outputs_handlers.cjs
  └─> generate_git_patch.cjs
       └─> git_helpers.cjs  (was missing from SAFE_OUTPUTS_FILES)

This fixes the CI failure where safe-outputs MCP server could not start due to the missing module.

Original prompt

The call to execSync in generate_git_patch.cjs should use the command, args array syntax to avoid shell injection through branch name and other strings


✨ 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

  • Type: patch
  • Description: Add git_helpers.cjs to SAFE_OUTPUTS_FILES so safe outputs MCP server can load the shared helper along with generate_git_patch.cjs.

Generated by Changeset Generator

@pelikhan
Copy link
Contributor

@copilot same for push_repo_memory.cjs

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix shell injection risk in execSync call Fix shell injection in generate_git_patch.cjs via spawnSync args array Feb 10, 2026
Copilot AI requested a review from pelikhan February 10, 2026 03:43
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 10, 2026

@copilot same for push_repo_memory.cjs

Applied the same shell injection fix to push_repo_memory.cjs in commit 6cc4eea.

Replaced 8 vulnerable execSync calls with safe spawnSync using command-args array:

  • Branch operations: fetch, checkout, checkout --orphan
  • File operations: status, add, commit, pull, push
  • Added execGitSync() helper with error handling
  • Added 5 security test cases covering malicious branch names, repo URLs, and commit messages

All 54 tests pass including new security tests.

Copilot AI changed the title Fix shell injection in generate_git_patch.cjs via spawnSync args array Fix shell injection in generate_git_patch.cjs and push_repo_memory.cjs via spawnSync args array Feb 10, 2026
Copy link
Contributor

@pelikhan pelikhan left a 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 = {}) {
Copy link
Contributor

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

Copy link
Contributor Author

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, {
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot more logging

Copy link
Contributor Author

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 = {}) {
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot refactor helper

Copy link
Contributor Author

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>
Copy link
Contributor Author

Copilot AI commented Feb 10, 2026

@copilot update setup.sh for new files

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 "${JS_SOURCE_DIR}"/*.cjs picks up any new .cjs files added to the directory.

Copilot AI changed the title Fix shell injection in generate_git_patch.cjs and push_repo_memory.cjs via spawnSync args array Fix shell injection in generate_git_patch.cjs and push_repo_memory.cjs via shared git_helpers.cjs Feb 10, 2026
Copilot AI requested a review from pelikhan February 10, 2026 04:57
@pelikhan pelikhan marked this pull request as ready for review February 10, 2026 04:58
Copilot AI review requested due to automatic review settings February 10, 2026 04:58
Copy link
Contributor

Copilot AI left a 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.cjs providing execGitSync(args, options) for safe git invocation without shell interpretation.
  • Migrated git invocations in generate_git_patch.cjs and push_repo_memory.cjs to use execGitSync.
  • 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 fetch against 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.

Comment on lines 65 to 67
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)
Copy link

Copilot AI Feb 10, 2026

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.

Suggested change
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)

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

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).

Comment on lines +1039 to +1043
const { spawnSync } = require("child_process");
const result = spawnSync("git", ["checkout", branchName], {
encoding: "utf8",
stdio: "pipe",
});
Copy link

Copilot AI Feb 10, 2026

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.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

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.

Comment on lines 14 to 18
// 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}`);
}
Copy link

Copilot AI Feb 10, 2026

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.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

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" });
Copy link

Copilot AI Feb 10, 2026

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.

Suggested change
execGitSync(["rm", "-rf", "."], { stdio: "pipe" });
execGitSync(["rm", "-r", "-f", "--ignore-unmatch", "."], { stdio: "pipe" });

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

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.

@pelikhan pelikhan added the smoke label Feb 10, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 10, 2026

💫 TO BE CONTINUED... Smoke Claude failed! Our hero faces unexpected challenges...

@github-actions
Copy link
Contributor

🧪 Smoke Project is now testing project operations...

@github-actions
Copy link
Contributor

📰 BREAKING: Smoke Copilot is now investigating this pull request. Sources say the story is developing...

@github-actions
Copy link
Contributor

github-actions bot commented Feb 10, 2026

Changeset Generator failed. Please review the logs for details.

…est files from setup.sh

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 10, 2026

@copilot Fix setup.sh https://github.com/github/gh-aw/actions/runs/21852489965/job/63062187689#step:25:1

Fixed in commit f758270. Updated setup.sh to skip *.test.cjs files when copying to the runtime environment. Test files are now excluded from deployment, which fixes the CI failure and reduces deployment size.

@pelikhan
Copy link
Contributor

@copilot you need to edit the list of files for safe outputs in setup.sh

See

Error: Cannot find module './git_helpers.cjs'
Require stack:

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 10, 2026

@copilot you need to edit the list of files for safe outputs in setup.sh

See...

Fixed in commit 1d59bd2. Added git_helpers.cjs to the SAFE_OUTPUTS_FILES array in setup.sh.

The safe-outputs MCP server uses safe_outputs_handlers.cjsgenerate_git_patch.cjsgit_helpers.cjs, so git_helpers.cjs needed to be in the list for the module to load correctly.

@github-actions
Copy link
Contributor

🧪 Smoke Project is now testing project operations...

@github-actions
Copy link
Contributor

github-actions bot commented Feb 10, 2026

🎬 THE ENDSmoke Claude MISSION: ACCOMPLISHED! The hero saves the day! ✨

@github-actions
Copy link
Contributor

github-actions bot commented Feb 10, 2026

✨ The prophecy is fulfilled... Smoke Codex has completed its mystical journey. The stars align. 🌟

@github-actions
Copy link
Contributor

github-actions bot commented Feb 10, 2026

Changeset Generator completed successfully!

@github-actions
Copy link
Contributor

📰 BREAKING: Smoke Copilot is now investigating this pull request. Sources say the story is developing...

@github-actions
Copy link
Contributor

Agent Container Tool Check

Tool Status Version
bash 5.2.21(1)-release
sh ok
git 2.52.0
jq jq-1.7
yq v4.52.2
curl curl
gh 2.86.0
node v20.20.0
python3 Python
go go1.24.12
java not found
dotnet not found

Result: 10/12 tools available ❌

Missing tools:

  • java - Java runtime not available
  • dotnet - .NET runtime not available

AI generated by Agent Container Smoke Test

@github-actions
Copy link
Contributor

Smoke Project completed successfully. All project operations validated.

@github-actions
Copy link
Contributor

🎭 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! 👻

AI generated by Smoke Copilot

@github-actions
Copy link
Contributor

🤖 Smoke Test Results for Run §21853071854

PR Titles:

Test Results:

  • ✅ GitHub MCP Testing (reviewed 2 merged PRs)
  • ✅ Safe Inputs GH CLI Testing (queried 2 PRs)
  • ✅ Serena MCP Testing (found 16 symbols in main.go)
  • ✅ Playwright Testing (navigated to GitHub - title verified)
  • ✅ File Writing Testing (created test file)
  • ✅ Bash Tool Testing (verified file contents)
  • ✅ Discussion Interaction Testing (commented on discussion [prompt-clustering] Daily Copilot Agent Prompt Clustering Analysis - 2026-02-10 #14728)
  • ✅ Build gh-aw (successful build)
  • ✅ Workflow Dispatch Testing (triggered haiku-printer)

Overall Status: PASS

cc @pelikhan

AI generated by Smoke Copilot

@github-actions
Copy link
Contributor

📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤

@pelikhan pelikhan merged commit 9855c24 into main Feb 10, 2026
1 check passed
@pelikhan pelikhan deleted the copilot/fix-execsync-shell-injection branch February 10, 2026 05:40
@github-actions
Copy link
Contributor

PRs: #14702 Apply strict matching to slash commands (startsWith + exact equality); #14701 Fix API key masking timing vulnerability in MCP setup generation
Tests: GitHub MCP ✅; Serena activate/find_symbol ✅; Playwright title ✅; File write ✅; Bash cat ✅; Build ✅
Overall: PASS

AI generated by Smoke Codex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants