What Happened
A denial of service vulnerability was present in urllib3, a widely-used Python HTTP library, between versions 1.25.2 and 1.25.8. This flaw allowed attackers to exhaust resources through a specific method in the library's code. Since more than 1,200 packages depend on urllib3, the vulnerability's impact extended beyond projects that directly imported the library.
The vulnerable method was updated in version 1.25.8 to improve efficiency, eliminating the denial of service vulnerability.
Timeline
Version 1.25.2: Vulnerability introduced into urllib3 codebase.
Versions 1.25.2 - 1.25.7: Vulnerable code present in production releases.
Version 1.25.8: Fix deployed, vulnerable method rewritten for efficiency.
Post-fix: Many downstream projects remained vulnerable until they updated their dependencies.
The gap between the fix's availability and its implementation across the ecosystem represents the real incident window. Your project could have been patched quickly or remained exposed for months, depending on your dependency management practices.
Which Controls Failed or Were Missing
Dependency Inventory and Tracking
Most affected projects lacked visibility into their full dependency tree. If you're only tracking direct dependencies in your requirements.txt, you miss the transitive chain. urllib3 often appears as an indirect dependency through libraries like requests or boto3. Without tools that map the complete graph, you don't know you're vulnerable.
Automated Vulnerability Scanning
Teams without continuous dependency scanning had no alert mechanism. The vulnerability existed in production code, but without automated checks against vulnerability databases, there was no trigger to investigate or patch.
Update Cadence and Testing
Projects without a defined process for dependency updates likely remained vulnerable long after the fix. Developers often freeze dependencies for stability, avoid updates to prevent breaking changes, and only upgrade when necessary for new features.
What the Relevant Standards Require
PCI DSS v4.0.1 Requirement 6.3.2 mandates maintaining an inventory of bespoke and custom software, and third-party software components. This includes your Python dependencies. You need a current, complete inventory—not just what's in your top-level requirements file.
PCI DSS v4.0.1 Requirement 6.3.3 requires protecting all system components and software from known vulnerabilities by installing applicable security patches or updates. This means you need a process to identify when urllib3 1.25.8 becomes available and a timeline to deploy it.
OWASP Top 10 2021: A06:2021 – Vulnerable and Outdated Components highlights this scenario. Using components with known vulnerabilities, including libraries deep in your dependency tree, creates exploitable risk. The guidance explicitly mentions that you're at risk when you don't know the versions of all components you use.
ISO/IEC 27001:2022 Annex A.8.31 requires applying security throughout the development lifecycle, including managing third-party components and their vulnerabilities.
NIST Cybersecurity Framework v2.0 under the Identify function (ID.RA-01) requires identifying and documenting asset vulnerabilities. Your dependency tree is an asset. urllib3 sitting three layers deep in your imports is still your vulnerability to manage.
Lessons and Action Items for Your Team
1. Generate a Complete Dependency Graph
Run pip install pipdeptree and execute pipdeptree in each of your project environments. Export the output to see not just that you depend on requests, but that requests depends on urllib3 version X. Tools like pip-audit or safety can automate this, but start by understanding what you're actually running.
Action: Create a weekly automated job that generates and stores your dependency tree. Compare it against the previous week to catch unexpected additions.
2. Implement Automated Vulnerability Scanning
Integrate dependency checking into your CI/CD pipeline. Snyk offers improved support for Python, allowing developers to remediate vulnerabilities in dependencies with automated fix pull requests. Alternatives include GitHub's Dependabot, pip-audit, or safety-cli.
Action: Add a CI step that fails builds when high or critical vulnerabilities are detected. Set a 30-day remediation SLA for medium-severity findings.
3. Establish an Update Cadence
Waiting for a security incident to update dependencies is a control failure. You need a regular schedule: review and test dependency updates monthly for production systems, weekly for active development.
Action: Dedicate one afternoon per month for dependency updates. Run your test suite against updated dependencies in a staging environment. Document any breaking changes and the effort required to address them.
4. Pin Versions, But Not Forever
Use exact version pinning (urllib3==1.25.8) in production requirements to ensure reproducible builds, but don't let those pins fossilize. Your pinned versions should be the result of deliberate choices, not neglect.
Action: Add comments to your requirements.txt with the date each dependency was last reviewed. If a pin is older than 90 days, it needs justification or an update.
5. Map Vulnerability Findings to Business Impact
When you discover you're running urllib3 1.25.4, don't just file a ticket. Document which services use the vulnerable dependency, what data they process, and what the DoS impact would be. This turns an abstract CVE into a concrete risk discussion.
Action: Create a template that links each dependency vulnerability to the systems affected, data classification, and potential business impact. Use this to prioritize remediation.
The urllib3 incident demonstrates that your security posture includes code you didn't write, maintained by people you've never met, pulled in by libraries you barely knew you were using. That's not a reason to avoid dependencies—it's a reason to manage them like the critical infrastructure they are.



