What Happened
In August 2019, an attacker compromised a maintainer's RubyGems account for the rest-client library and inserted malicious code into versions 1.6.11 through 1.6.13. This backdoor allowed remote code execution on any system that installed these versions, enabling attackers to execute arbitrary commands and steal sensitive data.
The maintainer's account lacked two-factor authentication (2FA), a missing control that facilitated a supply chain attack affecting every application using the compromised versions.
Timeline
The incident unfolded rapidly after the malicious versions were published:
- Compromise: Attacker accessed the maintainer's RubyGems account.
- Malicious release: Versions 1.6.11, 1.6.12, and 1.6.13 were published with backdoor code.
- Detection: Security researchers identified the malicious code.
- Response: CVE requested and corrupted versions removed from RubyGems.
- Remediation: Users advised to upgrade to the rest-client 2.x.x series.
The compromised versions were from an older branch—rest-client had already moved to the 2.x.x series. Teams using outdated dependencies were primarily affected, highlighting both an authentication and a dependency management failure.
Which Controls Failed or Were Missing
Account authentication: The maintainer's RubyGems account had no 2FA. Password-only authentication created a single point of failure. Whether the password was phished, reused, or brute-forced, the lack of 2FA meant one credential gave the attacker full publishing rights.
Dependency pinning and monitoring: Organizations that installed rest-client without version constraints automatically pulled the malicious updates. Teams running the 1.6.x branch despite 2.x being available lacked vulnerability scanning or a dependency update process to flag outdated software.
Code signing and verification: RubyGems had no mandatory signing requirement for published packages. Unlike ecosystems where maintainers sign releases with cryptographic keys, there was no technical control to verify the publisher's authorization.
Automated security scanning: Organizations without dependency scanning tools had no automated detection when the malicious versions appeared in their dependency trees.
What the Relevant Standards Require
PCI DSS v4.0.1 Requirement 8.4.2 mandates multi-factor authentication for all access into the cardholder data environment. If your application processes payments and pulls dependencies from public repositories, your build pipeline touches that environment. The maintainer account that publishes your dependencies is part of your supply chain—and supply chain security falls under your compliance scope.
NIST 800-53 Rev 5 IA-2(1) requires multi-factor authentication for network access to privileged accounts. A package maintainer account is privileged: it can push code that runs on thousands of systems. This extends to any account that can inject code into your production path.
ISO/IEC 27001:2022 Annex A.9.4.2 addresses secure authentication procedures. Your risk assessment should identify third-party package repositories as a threat vector. If you're relying on packages from maintainers who don't use MFA, document that risk and the compensating controls you've implemented.
OWASP Top 10 2021 A08:2021 – Software and Data Integrity Failures directly addresses this scenario. The guidance calls for verifying that software components come from trusted sources and haven't been tampered with. Without MFA on maintainer accounts, you cannot trust the source.
Lessons and Action Items for Your Team
Mandate 2FA on all package manager accounts immediately. If your team publishes to RubyGems, npm, PyPI, or any other registry, enable two-factor authentication today. Use hardware tokens (YubiKey, Titan) rather than SMS. Document this as a requirement in your secure development lifecycle policy. If you're working toward SOC 2 Type II compliance, your auditor will ask about authentication controls for privileged accounts.
Implement dependency pinning with hash verification. Your Gemfile.lock (or package-lock.json, requirements.txt) should specify exact versions and cryptographic hashes. When rest-client 1.6.11 appeared, teams with pinned dependencies didn't automatically pull it. Configure your package manager to reject installations where the hash doesn't match. In Bundler, this happens automatically with the lock file. In npm, use npm ci instead of npm install in CI/CD.
Deploy automated dependency scanning. Tools like Snyk, Dependabot, or OWASP Dependency-Check should run on every pull request and nightly against your production dependencies. Configure them to fail builds when they detect known vulnerabilities. After the rest-client incident, these tools added signatures for the malicious versions—but only teams running the scans got the alerts.
Establish a maximum dependency age policy. The compromised versions affected the 1.6.x branch. The current stable version was 2.x.x. If you're running dependencies more than two major versions behind, you're outside the security support window for most projects. Set a policy: dependencies must be updated within 90 days of a new major version, 30 days for security patches. Track this in your vulnerability management program.
Require signed commits and releases for your internal packages. If you publish private gems or packages internally, implement GPG signing for all commits and releases. This creates a cryptographic audit trail. When someone's account gets compromised, you can verify which releases were legitimately signed and which weren't. GitHub and GitLab both support commit signature verification.
Build an incident response plan for dependency compromises. When a malicious version appears in your dependency tree, you need a playbook: Who gets notified? How do you identify affected systems? What's the rollback procedure? How do you verify the integrity of your production builds? Document this before the incident happens. Include your legal and communications teams—if you're processing customer data and a backdoored library was running in production, you may have breach notification obligations.
The rest-client incident wasn't sophisticated. No zero-day exploit, no nation-state actor. Just a missing second factor on a maintainer account. That's the lesson: supply chain security fails at the simplest controls. Fix the simple things first.



