# Compromised maintainer pushes malicious code

Unprotected default branch, dormant or single maintainer with push rights, no required review or signed commits

_Area: Software supply chain and CI/CD. Seen in 4 disclosed reports; latest 2026-10-09. Updated 2026-10-23._

## How it works
A maintainer account is taken over (phishing, credential theft, or a dormant account nobody watches). The attacker then writes directly to the default branch, bypassing the normal review path. The assumption that breaks is that "only trusted people can push to main" — that is true of the identity, but the identity is no longer the person. Without branch protection, required review, or signed commits, nothing distinguishes the attacker's push from a legitimate one, so malicious code lands as if a maintainer wrote it.

## Where it shows up
- Default branch accepts direct pushes or force-pushes; no required pull request or review [E1](https://bugbountylibrary.com/r/blog%3Astepsecurity-io-blog-pythagora-io-gpt-pilot-compromised-on-github-shai-hulud-credential-stealer-blocked-by-python-linter.md).
- Maintainer accounts idle for years but still holding publish/push rights [E3](https://bugbountylibrary.com/r/blog%3Aaikido-dev-blog-sleepergem-rubygems-supply-chain-attack.md).
- Single maintainer with sole write access and no second reviewer.
- New workflow files committed straight to default branches, running on any push [E2](https://bugbountylibrary.com/r/blog%3Asocket-dev-blog-ghostaction-cloud-credentials.md).
- Commits with rewritten history or backdated metadata that don't match the normal dev flow [E1](https://bugbountylibrary.com/r/blog%3Astepsecurity-io-blog-pythagora-io-gpt-pilot-compromised-on-github-shai-hulud-credential-stealer-blocked-by-python-linter.md).
- Code that disables TLS verification, contacts hardcoded hosts/IPs, or spawns shells on import/require [E2](https://bugbountylibrary.com/r/blog%3Asocket-dev-blog-ghostaction-cloud-credentials.md)[E3](https://bugbountylibrary.com/r/blog%3Aaikido-dev-blog-sleepergem-rubygems-supply-chain-attack.md).
- Packages that skip execution when CI environment variables are present [E3](https://bugbountylibrary.com/r/blog%3Aaikido-dev-blog-sleepergem-rubygems-supply-chain-attack.md).

## How to review
The invariant: every change to a release-bearing branch must pass through a path that a compromised single account cannot complete alone. Check that the default branch rejects direct and force pushes, requires review from someone other than the pusher, and that releases are built from reviewed commits. For accounts, confirm dormant or single-maintainer identities cannot publish without a second factor or co-maintainer. In code, treat any new import-time/require-time network or shell behavior, disabled TLS, or hardcoded endpoints as a red flag regardless of who committed it.

## Variants seen
- Force-push of a backdated trojan commit adding an import-time loader that runs an obfuscated credential stealer [E1](https://bugbountylibrary.com/r/blog%3Astepsecurity-io-blog-pythagora-io-gpt-pilot-compromised-on-github-shai-hulud-credential-stealer-blocked-by-python-linter.md).
- Direct commit of a plausibly named workflow (security-audit.yml) that runs on any push and exfiltrates secrets and cloud credentials to a hardcoded IP [E2](https://bugbountylibrary.com/r/blog%3Asocket-dev-blog-ghostaction-cloud-credentials.md).
- Hijacked dormant maintainer publishing a gem whose load path drops binaries from an attacker-controlled host with TLS verification off [E3](https://bugbountylibrary.com/r/blog%3Aaikido-dev-blog-sleepergem-rubygems-supply-chain-attack.md).

## Impact and severity
High: credential and cloud-secret theft, and malware distributed to downstream users via package installs [E1](https://bugbountylibrary.com/r/blog%3Astepsecurity-io-blog-pythagora-io-gpt-pilot-compromised-on-github-shai-hulud-credential-stealer-blocked-by-python-linter.md)[E2](https://bugbountylibrary.com/r/blog%3Asocket-dev-blog-ghostaction-cloud-credentials.md)[E3](https://bugbountylibrary.com/r/blog%3Aaikido-dev-blog-sleepergem-rubygems-supply-chain-attack.md). Severity rises with breadth (346 repos in one wave [E2](https://bugbountylibrary.com/r/blog%3Asocket-dev-blog-ghostaction-cloud-credentials.md)), with history-sweeping that recovers removed secrets [E2](https://bugbountylibrary.com/r/blog%3Asocket-dev-blog-ghostaction-cloud-credentials.md), and with dormant accounts that evade notice [E3](https://bugbountylibrary.com/r/blog%3Aaikido-dev-blog-sleepergem-rubygems-supply-chain-attack.md). Cheap CI lint/format checks caught the injected code in one case [E1](https://bugbountylibrary.com/r/blog%3Astepsecurity-io-blog-pythagora-io-gpt-pilot-compromised-on-github-shai-hulud-credential-stealer-blocked-by-python-linter.md), showing low-cost tripwires help but are not a substitute for branch protection.

## Evidence
- E1: https://bugbountylibrary.com/r/blog%3Astepsecurity-io-blog-pythagora-io-gpt-pilot-compromised-on-github-shai-hulud-credential-stealer-blocked-by-python-linter.md
- E2: https://bugbountylibrary.com/r/blog%3Asocket-dev-blog-ghostaction-cloud-credentials.md
- E3: https://bugbountylibrary.com/r/blog%3Aaikido-dev-blog-sleepergem-rubygems-supply-chain-attack.md
