On March 29, 2024, Microsoft engineer Andres Freund noticed SSH logins on a Debian testing box were running about 500 milliseconds slower than expected. That small anomaly led him to CVE-2024-3094, a backdoor deliberately inserted into xz-utils versions 5.6.0 and 5.6.1 by a contributor persona known as "Jia Tan," who had spent roughly two years building maintainer trust with legitimate patches before slipping obfuscated code into the build system. The flaw scored a maximum CVSS of 10.0 and would have given an attacker remote code execution across a huge swath of Linux systems, had Freund not caught it first, largely by luck. It is the starkest recent proof that open-source supply-chain risk doesn't always arrive as a sloppy dependency — sometimes it arrives as a patient, well-reviewed-looking pull request. New contributors rarely think of themselves as a security surface, but every PR you open, every dependency you add, and every commit you push is part of the trust chain a project's maintainers and downstream users rely on. This guide covers the habits that keep your contributions from becoming someone else's incident: signing your commits, vetting what you add to a manifest, and catching what shouldn't leave your machine in the first place.
Why does commit signing matter if anyone can still write malicious code?
Commit signing doesn't stop malicious code from being written — nothing about GPG or SSH signatures reviews your diff — but it does stop someone from impersonating you or forging your authorship after the fact. GitHub supports GPG, SSH, and S/MIME signing, and a correctly signed commit gets a "Verified" badge tied cryptographically to a key on your account, according to GitHub's own documentation on commit signature verification. That verification record persists even if you later rotate or revoke the key, so a project's history stays auditable years later. This matters directly in incidents like xz-utils: part of what investigators reconstructed afterward was which commits came from which identity, and unsigned history makes that reconstruction slower and less certain. If you're new to a project, set up signing before your first PR — it's a five-minute git config change, and it's one of the few controls that makes your identity, not just your code, verifiable.
What makes a "boring" maintenance PR worth extra scrutiny?
The xz-utils backdoor is the clearest evidence that boring PRs deserve as much scrutiny as flashy ones — the malicious payload was hidden inside build-system and test-file changes, not in application logic anyone would read line by line during a feature review. Autoconf macros, .m4 files, and binary test fixtures are exactly the kind of change reviewers tend to rubber-stamp because they're tedious and rarely touched. As a contributor, the defensive habit is symmetry: hold your own "boring" PRs — CI config tweaks, build scripts, test fixtures — to the same clarity standard as feature code. Explain why a build-system change is needed in the PR description, keep it minimal, and avoid bundling unrelated changes into a single commit. If you're reviewing someone else's boring PR, don't let "it's just CI" be a reason to skim it; that exact framing is what let a two-year social-engineering campaign succeed against one of the most widely used compression libraries on Linux.
How do typosquatting and dependency confusion sneak into a PR?
Typosquatting and dependency confusion are well-established, named attack classes against public registries like npm and PyPI, and they succeed specifically at the moment someone adds a new dependency to a manifest — often in a routine PR nobody scrutinizes closely. Typosquatting relies on a contributor mistyping or misremembering a package name (reqeusts instead of requests) and the malicious package executing during install. Dependency confusion relies on a public registry entry outranking an intended internal or scoped package during resolution. The defense is mechanical, not judgment-based: pin exact versions and commit your lockfile (package-lock.json, poetry.lock, Cargo.lock) so transitive resolution can't silently substitute a different package on someone else's machine or in CI, and double-check any newly added dependency name against the project you actually intended to use before it lands in a diff. Reviewers should treat "new dependency added" as a PR category that always gets a second look, the same way a security-relevant code path would.
What should never reach a commit in the first place?
Credentials, tokens, and private keys should never reach a commit, and the reason this keeps happening isn't ignorance — it's that local development constantly generates real secrets (a .env file, a test AWS key, a personal access token pasted into a script) that are easy to git add -A by accident. GitHub's own scanning and multiple vendor telemetry sources have documented millions of secrets exposed in public commits every year, and once a secret is pushed, it's compromised the moment it's visible in history, even if you force-push it away seconds later. Safeguard's pre-push git hook (safeguard install-hook --hook pre-push) and IDE extension catch this class of mistake before it leaves your machine, scanning new commits locally and verifying issuer-specific patterns — AWS, GitHub PATs, Stripe, Slack, OpenAI keys among them — against the actual issuing service so you know immediately whether a flagged string is a live credential or test noise. As a contributor, the cheapest fix is prevention: a pre-push hook costs nothing and catches the mistake before a maintainer, or an attacker scraping public commits, ever sees it.
How can a project catch a malicious dependency before it reaches disk?
A project can catch a malicious or typosquatted dependency by evaluating it at install time rather than after it's already sitting in node_modules or a virtualenv. Safeguard's package firewall runs as an install-time proxy in front of npm and pip, so every fetch — including transitive dependencies your PR never explicitly lists — is checked against typosquat detection, dependency/namespace-confusion signals, and malware/blocklist matches before the package reaches disk, with allow, warn, or block outcomes and a quarantine mode for packages pending review. That coverage matters because a contributor adding one new top-level dependency has no realistic way to manually audit the dozens of transitive packages it pulls in; an install-time check is the only point in the pipeline that sees every one of them. For a project maintainer, starting the firewall in audit mode establishes a baseline before moving to warn or block, so legitimate contributor PRs aren't disrupted while the policy is tuned.
What's the one habit that ties all of this together?
The one habit is treating your own contribution as something a stranger will have to trust without meeting you — because that's exactly what open-source review is. Sign your commits so your authorship is verifiable years later. Give boring infrastructure changes the same scrutiny as feature code. Pin and lock your dependencies so a name collision can't substitute something malicious. Run a pre-push secret scan so a leaked credential never becomes someone else's incident response. None of these individually would have stopped the xz-utils backdoor by itself — it took a maintainer years to build the trust that made the attack possible — but together they raise the cost of exactly that kind of patient, quiet supply-chain compromise, for a new contributor and a ten-year maintainer alike.