Skip to content

Prevent PR body injection during transfer - #58034

Merged
pelikhan merged 11 commits into
mainfrom
copilot/vulnhunter-fix-git-mailbox-injection
Sep 2, 2026
Merged

Prevent PR body injection during transfer#58034
pelikhan merged 11 commits into
mainfrom
copilot/vulnhunter-fix-git-mailbox-injection

Conversation

Copilot AI commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

PR bodies were embedded in a hand-built git mailbox, allowing a malicious --- line to inject patch content during gh aw pr transfer.

  • Patch handling

    • Write only raw gh pr diff output to the patch file.
    • Remove git am mailbox parsing and always use git apply.
  • Commit metadata

    • Create the commit separately with PR title, body, and attribution passed to git commit -m.
    • Untrusted body text is no longer interpreted as patch structure.
diffContent, _ := prRunGH(...)

os.WriteFile(patchFile, diffContent, constants.FilePermPublic)
exec.Command("git", "apply", "--3way", patchFile).Run()
exec.Command("git", "commit", "-m", commitMsg).Run()
  • Regression coverage
    • Added a test confirming mailbox separators in PR bodies are excluded from the patch.

Copilot AI and others added 4 commits September 2, 2026 21:33
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix Git mailbox patch injection vulnerability Prevent PR body injection during transfer Sep 2, 2026
Copilot AI requested a review from pelikhan September 2, 2026 21:42
@pelikhan
pelikhan marked this pull request as ready for review September 2, 2026 21:43
Copilot AI balanced review requested due to automatic review settings September 2, 2026 21:43
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

Test Quality Sentinel skipped because pre-fetch PR data was unavailable: unable to fetch test file diff

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

⚠️ Security scanning failed for Ponytail Reviewer. Review the logs for details.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • ab.chatgpt.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "ab.chatgpt.com"

See Network Configuration for more information.

Generated by Ponytail Reviewer for #58034

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • proxy.golang.org

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "proxy.golang.org"

See Network Configuration for more information.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

Completed PR review locally; using noop because safe-output review submission tools are not exposed as callable developer tools in this environment.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check. See the comment below for the result and any generated ADR draft.

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A tiny package-global seam adds indirection for a single call and makes the test path more stateful than necessary. Call workflow.RunGH directly and keep the test at the package boundary. net: -2 lines possible.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • ab.chatgpt.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "ab.chatgpt.com"

See Network Configuration for more information.

Generated by ✂️ Ponytail Reviewer for #58034 · codex · mai10 · 4.06 AIC · ⌖ 8.07 AIC · ⊞ 12.8K
Comment /ponytail to run again

Comment thread pkg/cli/pr_command.go Outdated
)

var prLog = logger.New("cli:pr_command")
var prRunGH = workflow.RunGH

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

pkg/cli/pr_command.go:26: yagni: package-global prRunGH seam for one call. Call workflow.RunGH directly and keep the test at the package boundary.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 1b6dc96: removed the package-level prRunGH seam and the test now exercises workflow.RunGH through a fake gh binary at the package boundary.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the git mailbox injection fix. The change correctly removes the hand-built mailbox format and replaces it with a raw gh pr diff write plus a separately-constructed git commit -m invocation via exec.Command (argv, not shell), which prevents untrusted PR body/title content from being interpreted as patch or mailbox structure. The regression test confirms a --- separator plus injected diff content in the PR body no longer leaks into the patch file. No blocking issues found in the changed lines.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • proxy.golang.org

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "proxy.golang.org"

See Network Configuration for more information.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · copilot · sonnet50 · 26.3 AIC · ⌖ 13.2 AIC · ⊞ 8.3K

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Broad staging and unsafe retry cleanup could leak unrelated files or leave the checkout conflicted.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Prevents PR-body injection during transfer by separating patch application from commit metadata.

Changes:

  • Applies raw gh pr diff output with git apply.
  • Commits PR metadata separately.
  • Adds regression coverage for mailbox separators.
File summaries
File Review
pkg/cli/pr_command.go Critical (2 votes): git add . may stage unrelated local files; scope staging and add regression coverage. Moderate (1 vote): reset conflicted state before retries and failure cleanup.
pkg/cli/pr_command_test.go Verifies patch files contain only raw PR diffs.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread pkg/cli/pr_command.go Outdated
} else if verbose {
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Applied patch using git am (includes commit)"))
// Stage all changes and create the commit separately from patch application.
cmd = exec.Command("git", "add", ".")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 1b6dc96: transfer now rejects a dirty target worktree, applies patches with git apply --index, removes the broad git add ., and adds a regression proving unrelated ignored files are not committed.

Comment thread pkg/cli/pr_command.go Outdated
Comment on lines +311 to +312
cmd = exec.Command("git", "apply", "--3way", patchFile)
if err := cmd.Run(); err != nil {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 1b6dc96: failed 3-way applies are reset to HEAD before fallback, and failed fallback/reject diagnostics reset before switching back and deleting the transfer branch.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /tdd and /diagnosing-bugs — this is a solid, well-scoped fix for the mailbox-injection vulnerability (CVE-worthy: untrusted PR body text was previously spliced into a git am mailbox and interpreted as patch/header structure). The core fix is correct: writing only the raw diff to the patch file, always using git apply instead of git am, and building the commit message via git commit -m (an argv, not shell/patch text) means untrusted Body/Title content can no longer be parsed as patch structure.

📋 Key Themes & Highlights

Key Themes

  • Root cause addressed, not just symptom: removing git am/mailbox parsing entirely (rather than just sanitizing the body) eliminates the injection vector at its source — good root-cause fix per /diagnosing-bugs.
  • Test coverage gap: the new TestCreatePatchFromPRWritesOnlyDiff test only covers the patch-file-writing half (createPatchFromPR). The commit-message-building half in applyPatchToRepo, where prInfo.Body/prInfo.Title are interpolated into git commit -m, remains untested by this PR. Since -m message text isn't parsed by git as patch structure, this is low risk, but a regression test would make the security property fully verifiable in CI rather than only partially.
  • Dependency injection pattern (prRunGH) is a nice, minimal seam for testability — consistent with good /codebase-design practice of adding a small seam rather than a larger interface.

Positive Highlights

  • ✅ Clean removal of ~100 lines of fallback/mailbox-detection logic — net negative diff, simpler control flow, easier to reason about.
  • ✅ Commit message construction is unchanged in content (Original-PR/Original-Author trailers preserved) while eliminating the injection surface — good backward compatibility.
  • ✅ Regression test explicitly encodes the vulnerability scenario (Body containing ---\ndiff --git ...), which is exactly the right kind of test per /tdd.

@copilot please address the review comments above.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • proxy.golang.org

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "proxy.golang.org"

See Network Configuration for more information.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · 76.7 AIC · ⌖ 14.7 AIC · ⊞ 10.3K
Comment /matt to run again

if string(got) != string(diff) {
t.Fatalf("patch contents = %q, want raw diff %q", got, diff)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[/tdd] Good regression test for createPatchFromPR, but the applyPatchToRepo commit-building path (the other half of this fix — where prInfo.Body/prInfo.Title now flow into git commit -m) has no test coverage in this PR.

💡 Suggested addition

A test that stubs the git commands (or runs against a temp git repo) and asserts a malicious Body such as "msg\n---\ndiff --git a/injected b/injected" ends up verbatim inside the commit message rather than being reinterpreted, would close the loop on the vulnerability end-to-end (patch application and commit creation), not just the patch-file write.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 1b6dc96: added coverage that runs the commit path in a temp git repo and verifies a malicious body containing --- and diff text remains verbatim in the commit message instead of creating injected files.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot Please address the current blocking review feedback, refresh the branch if needed, and run the pr-finisher skill before handing back to maintainers.

Please summarize the fix and rerun outcome for maintainers after the next push.

Generated by PR Sous Chef

Generated by 👨‍🍳 PR Sous Chef · pi · gpt54 · 35.2 AIC · ⌖ 8.82 AIC · ⊞ 8.7K ·
Comment /souschef to run again

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot Please review the latest head and address the remaining blocking feedback before handing back to maintainers.

Please run the pr-finisher skill and leave a concise maintainer-facing summary after the next push.

Generated by PR Sous Chef

Generated by 👨‍🍳 PR Sous Chef · pi · gpt54 · 40.4 AIC · ⌖ 8.88 AIC · ⊞ 9.2K ·
Comment /souschef to run again

Copilot AI and others added 2 commits September 2, 2026 22:43
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Please address the current blocking review feedback, refresh the branch if needed, and run the pr-finisher skill before handing back to maintainers.

  • Blocking review: copilot-pull-request...

Addressed in 1b6dc96 and bcb63b3: patch application now requires a clean worktree, uses indexed git apply instead of git add ., resets failed apply attempts before fallbacks/cleanup, and includes regressions for unrelated ignored files plus malicious PR-body commit text. Prior CI failures were root-caused to generated skill/fallback metadata; bcb63b3 restores the fallback file list. Local validation: targeted PR-transfer tests, make lint, make test-unit, make test, make recompile, and final make agent-report-progress passed. CI is stale after the agent push and needs a maintainer re-trigger.

@pelikhan

pelikhan commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

@copilot add git integration tests

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Please review the latest head and address the remaining blocking feedback before handing back to maintainers.

  • Blocking review: Copilot reviewer on pkg/cli/pr_command.go still flags broad...

Addressed at latest head 79d6121. The transfer path already removes broad staging and uses indexed git apply; this pass added explicit failed-apply cleanup coverage proving conflicts/unmerged index entries are reset, the temporary transfer branch is deleted, and the checkout returns clean to the original branch. Local validation passed: targeted PR-transfer tests, make lint, make test-unit, make test, make recompile, and final make agent-report-progress. Code review found no issues; CodeQL was skipped as the latest change is test-only. CI is stale after the agent push and needs a maintainer re-trigger.

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

Copilot AI commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

@copilot add git integration tests

Added in d29c953: new git integration tests exercise the full transferPR path against a temp bare remote and working clone with a fake gh CLI. Coverage now verifies scoped indexed patch application, hostile body/title text staying in commit metadata, transfer branch push, and failed-apply cleanup restoring a clean original branch with no unmerged index entries. Validation passed: targeted go test -tags integration, make lint, make test-unit, make test, make recompile, and final make agent-report-progress.

@pelikhan
pelikhan merged commit 86c0e5a into main Sep 2, 2026
32 checks passed
@pelikhan
pelikhan deleted the copilot/vulnhunter-fix-git-mailbox-injection branch September 2, 2026 23:44
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.88.2

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.

[vulnhunter] VulnHunter findings in github/gh-aw

4 participants