Malicious Pull Requests
The fix is real. The file it also touches is not.
What Is Malicious Pull Requests?
A malicious pull request attacks the review, not the code. The change is small, the description is helpful, and one edit sits in a file the description never mentions. You'll submit one yourself: a genuine flaky-test fix, plus a buried line in a deploy script that ships every CI variable to a server you control. You'll review it as the maintainer, read every changed file instead of trusting the summary. Then you'll make the catch structural with CODEOWNERS, required code-owner review, and limits on what fork pull requests can run.
What You'll Learn in Malicious Pull Requests
- Recognize the red flag of a malicious pull request: an edit to a file that has nothing to do with what the change claims to do, hidden among legitimate diff hunks
- Understand why reading the pull request description is not enough, and why a reviewer must open and read the full diff of every changed file
- Treat out-of-scope edits to deploy, build, or CI scripts as a signal that warrants a closer, more skeptical review
- Apply review-policy hardening: CODEOWNERS for sensitive files, required approval from more than one reviewer, and limits on what fork pull requests can run
- Understand why pull requests from forks sit outside the project's trust boundary and deserve extra scrutiny before their code is trusted
Malicious Pull Requests — Training Steps
-
Size up the project
Today Bob is targeting Fennroot, an open-source developer-tools project that takes pull requests from anyone. He starts where any contributor would, on the public repository for Harrow, the project's build tool. An open project is an open door: anyone can fork it, change it, and open a pull request. Bob is not here to help. He wants his code merged, and a busy maintainer's review is the only thing in his way.
-
Fork and clone
Bob forks the repository to his own account and clones the fork locally, so he can make his changes and push them back as a pull request. His plan is simple: include one genuinely useful change as cover, and hide a second, unrelated one alongside it.
-
The cover and the target
Bob's cover is real: he fixes a genuinely flaky retry test, the kind of small contribution a project welcomes. That change is honest and helpful, and it is the whole point. It makes the pull request look routine. His actual target is a different file entirely. He opens the deploy script, which has nothing to do with any test, and reads it clean.
-
Plant the backdoor
Bob adds a single line to the deploy script. It looks like a harmless bit of build telemetry, but it ships every environment variable to a server he controls. The deploy script runs in the project's CI, where those variables include deploy keys and registry tokens. One line, in a file the pull request never mentions. That is the entire attack.
-
Buried in plain sight
The line reads like something a build engineer might add, and it sits among ordinary deploy steps. Nothing about it screams attack, which is exactly why it survives a quick skim.
-
Commit both changes
Bob stages and commits both edits together: the real test fix and the deploy-script line, in one commit. Bundled together, the malicious change rides in on the back of the honest one.
-
Push to the fork
Bob pushes the branch up to his fork. The push goes to his own copy of the repository, and the platform answers with a link to open a pull request against Fennroot.
-
Open the pull request
Bob opens the pull request against Fennroot's repository. He writes a title and description that talk only about the flaky test, and never mention the deploy script. The test fix is the story he wants the reviewer to read. The backdoor is left out of it entirely.
-
Nudge for a merge
With the pull request open, Bob adds a friendly comment nudging for a quick merge. He leaves it on the test line, so the reviewer's eye lands there and not on the deploy script. A little social pressure to approve fast, before anyone reads too closely.
-
Knowledge check
You just watched a backdoor ride into a pull request behind a genuine fix. Lock in why it is dangerous.
Security Framework Coverage
CWE
- CWE-494 Download of Code Without Integrity Check
- CWE-345 Insufficient Verification of Data Authenticity
MITRE ATT&CK
- T1195.002 Supply Chain Compromise: Compromise Software Supply Chain
CIS Controls
- CIS 16 Application Software Security
NIST CSF
- PR.AT-02 Individuals in specialized roles are provided with awareness and training so that they possess the knowledge and skills to perform relevant tasks with cybersecurity risks in mind
- PR.PS Platform Security