What Poetry Gets Right: Deterministic Builds and Lockfile Pinning
Poetry has become one of the most popular dependency management tools in the Python ecosystem, and for good reason. Unlike the traditional pip install workflow where versions can shift between installs, Poetry enforces deterministic builds through its lockfile mechanism. When you run poetry lock, every single dependency — direct and transitive — gets pinned to an exact version with a cryptographic hash.
This is a massive improvement over plain requirements.txt files. The poetry.lock file captures the entire dependency graph: every package, every version, every hash. When a teammate runs poetry install, they get exactly the same dependency tree you have. No surprises. No "works on my machine" issues caused by a quietly updated sub-dependency.
Poetry also handles dependency resolution intelligently. If package A requires requests>=2.25 and package B requires requests<2.30, Poetry computes the intersection and pins a compatible version. This conflict resolution happens at lock time, not at install time, meaning you catch incompatibilities before they hit production.
The hash verification built into poetry.lock adds another layer of protection. Each package archive is recorded with its SHA-256 hash, so if a package on PyPI were somehow tampered with between your lock and your install, Poetry would refuse to proceed. This guards against supply-chain attacks at the distribution level.
Why Locking Versions Is Not Enough
Here is the uncomfortable truth: a locked dependency is not a safe dependency. Your poetry.lock might pin cryptography==41.0.3 today, and tomorrow a critical CVE could be published against that exact version. The lockfile did its job — it gave you reproducibility — but it cannot protect you from vulnerabilities discovered after you locked.
This is the core tension of poetry.lock security. The lockfile is designed to freeze your dependency graph, which is exactly what you want for stability. But freezing also means you stop receiving patches. Every day your lockfile ages, the probability of it containing a known vulnerability increases.
Consider a real scenario: you ship version 1.0 of your application in January. Your poetry.lock pins 147 packages. By March, eight of those packages have received security patches. By June, that number is likely over twenty. Your application is still running the January versions — deterministic, reproducible, and increasingly vulnerable.
The problem compounds with the speed of CVE disclosure. The OSV database receives thousands of new Python vulnerability reports each year. Many affect popular packages that almost every project depends on: requests, urllib3, cryptography, Pillow, certifi. If you are not actively monitoring your locked versions against newly published CVEs, you are flying blind.
How to Audit Poetry Dependencies
There are several approaches to perform a poetry vulnerability check, ranging from built-in tools to dedicated security scanners.
poetry audit (Poetry Plugin)
Poetry itself gained a poetry audit command through the poetry-audit-plugin. Install it with:
poetry self add poetry-audit-plugin
Then run:
poetry audit
This queries vulnerability databases and reports known CVEs for your locked dependencies. It is the most natural integration since it reads poetry.lock directly. However, its vulnerability database coverage may lag behind dedicated tools.
pip-audit
Google's pip-audit is one of the most thorough Python security scanners available. It queries the OSV.dev database and supports Poetry projects:
# Export to requirements format first
poetry export -f requirements.txt --output requirements.txt
pip-audit -r requirements.txt
Or, if you have the environment installed:
pip-audit
pip-audit provides detailed CVE identifiers, severity scores, and fix versions. Its main limitation is that it operates on the installed environment or a requirements file, not on poetry.lock directly. This means you need an extra export step, which can miss dev dependencies or extras.
safety check
The safety package from SafetyCLI checks your dependencies against the Safety DB:
poetry export -f requirements.txt --output requirements.txt
safety check -r requirements.txt
Safety provides clear, human-readable output with vulnerability descriptions. The free tier uses a database that may be up to 30 days behind the commercial version.
Manual Review of poetry.lock
For critical deployments, manual review of your poetry.lock diff before merging is valuable. Use git diff poetry.lock to see what changed. Look for:
- Version downgrades (could reintroduce patched vulnerabilities)
- New packages you did not explicitly add (pulled in as transitive deps)
- Packages with known security histories (
PyYAML,Jinja2,Pillow) - Hash changes without version changes (potential supply-chain concern)
The Transitive Dependency Problem
This is where poetry lock vulnerabilities most often hide. You carefully choose your direct dependencies — maybe ten or fifteen packages. But each of those pulls in its own dependencies, and those pull in more. A typical Python project with 10 direct dependencies can easily have 80 to 150 total packages in poetry.lock.
You have vetted Django and celery and boto3. But have you vetted kombu, amqp, vine, botocore, jmespath, s3transfer, python-dateutil? These transitive dependencies are in your lockfile, running in your production environment, and they carry their own CVE surface area.
The challenge is that you did not choose these packages. They were chosen for you by your direct dependencies. You may not even know they exist until a vulnerability scanner flags one. And when a CVE hits a transitive dependency, the fix path is not always clear: you may need to wait for your direct dependency to update its own constraints before you can pick up the patched version.
Poetry handles this better than plain pip because it at least makes the full tree visible. Run poetry show --tree to see the complete dependency graph. But visibility is not the same as security. You need active monitoring to catch when any package in that tree — direct or transitive — becomes vulnerable.
Poetry vs pip vs Pipenv: Security Comparison
How does Poetry compare to other Python dependency managers when it comes to security?
| Feature | Poetry | pip + requirements.txt | Pipenv |
|---|---|---|---|
| Lockfile | poetry.lock (automatic) | Manual pip freeze | Pipfile.lock (automatic) |
| Hash verification | Yes (built-in) | Optional (--require-hashes) | Yes (built-in) |
| Dependency resolution | SAT solver | Basic (pip 23+ improved) | Custom resolver |
| Built-in audit | Via plugin | pip-audit (separate) | pipenv check |
| Dev/prod separation | Groups (dev, test, etc.) | Separate files | [dev-packages] section |
| Transitive visibility | Full tree in lockfile | Flat list only | Full tree in lockfile |
Poetry and Pipenv both provide strong lockfile-based security with hash verification. Poetry's advantage is its dependency groups feature, which lets you separate dev, test, and production dependencies cleanly. This matters for security because dev-only packages (linters, test frameworks) should not run in production, reducing your attack surface.
pip alone offers the least security out of the box. Without a lockfile, you have no reproducibility guarantee. Even with pip freeze > requirements.txt, you lose the dependency tree structure — you cannot tell which packages are direct and which are transitive. The --require-hashes flag adds hash verification but must be manually maintained.
Regardless of which tool you use, none of them provide continuous vulnerability monitoring. They can tell you the state of your dependencies right now, but they cannot alert you when a new CVE is published tomorrow against a package you locked last week. That requires an external monitoring solution.
Setting Up Continuous Monitoring with OptiBot
Point-in-time audits are a good start, but they only catch vulnerabilities known at the moment you run the scan. What about the CVE published the day after your last audit? This is where continuous monitoring changes the game.
CVE OptiBot provides daily automated scans of your poetry.lock file against the OSV.dev vulnerability database. Here is how to set it up:
- Create an account at cve.optibot.re
- Create a project and upload your
poetry.lockfile - OptiBot parses your lockfile, extracting every package name and pinned version
- Daily scans check all your dependencies against newly published CVEs
- Email alerts notify you immediately when a vulnerability is found
The key difference from one-time tools like pip-audit is persistence. OptiBot does not just check your dependencies once — it watches them continuously. When a researcher discovers a vulnerability in urllib3 at 3 AM and it gets added to the OSV database, your next daily scan picks it up and you receive an alert before your morning coffee.
For agencies managing multiple client projects, OptiBot's dashboard shows the security posture of all your projects at a glance. No more running poetry audit individually on fifteen different repos.
Best Practices for Poetry Security
1. Update Regularly, But Deliberately
Run poetry update on a regular cadence — weekly or biweekly for active projects. But never blindly update everything. Use poetry update --dry-run first to see what would change. Review the changelog of each updated package, especially for major version bumps.
2. Review Lockfile Diffs in Code Review
Treat poetry.lock changes as security-relevant code changes. When a PR modifies the lockfile, review the diff. Ask: What packages changed? Were any new transitive dependencies introduced? Did any versions go backward?
3. Separate Dev and Production Dependencies
Use Poetry's dependency groups rigorously:
[tool.poetry.group.dev.dependencies]
pytest = "^8.0"
mypy = "^1.8"
ruff = "^0.3"
[tool.poetry.group.test.dependencies]
factory-boy = "^3.3"
faker = "^24.0"
In production, install with poetry install --only main to exclude dev and test packages entirely. Fewer packages mean fewer potential vulnerabilities.
4. Monitor Continuously
A weekly manual audit is not enough for production systems. Vulnerabilities are disclosed daily. Use a continuous monitoring service like OptiBot to bridge the gap between your audit cadence and the CVE disclosure cadence.
5. Pin Direct Dependencies Thoughtfully
In pyproject.toml, use caret constraints (^) for libraries where you want patch updates, and exact pins for packages where stability is critical. The lockfile pins everything exactly regardless, but your constraints determine what poetry update is allowed to change.
6. Automate Lockfile Refresh in CI
Set up a scheduled CI job (weekly) that runs poetry update, creates a PR with the diff, and runs your test suite. This ensures lockfile freshness does not depend on developers remembering to update.
Real-World Workflow: From Commit to Production Monitoring
Here is a complete security workflow for a team using Poetry:
Phase 1: Development
A developer adds a new dependency with poetry add httpx. Poetry resolves the dependency tree, updates pyproject.toml and poetry.lock. The developer commits both files.
Phase 2: Code Review
The PR reviewer checks the poetry.lock diff. They see httpx plus its transitive deps: httpcore, h11, anyio, sniffio. The reviewer verifies these are legitimate dependencies and not unexpected additions.
Phase 3: CI Pipeline
The CI pipeline runs the audit step:
# .github/workflows/security.yml
- name: Audit dependencies
run: |
pip install pip-audit
poetry export -f requirements.txt --output requirements.txt
pip-audit -r requirements.txt --fail-on-vuln
If any locked dependency has a known CVE, the pipeline fails. The developer must update the vulnerable package before merging.
Phase 3: Production Monitoring
After deployment, the poetry.lock file is uploaded to CVE OptiBot. OptiBot scans daily and alerts the team if any dependency becomes vulnerable — even months after the initial deployment. When an alert fires, the team creates a priority PR to update the affected package, re-runs CI, and deploys the fix.
This three-layer approach — code review, CI audit, and continuous monitoring — catches vulnerabilities at every stage. Code review catches suspicious changes. CI catches known vulnerabilities before merge. OptiBot catches vulnerabilities discovered after deployment. Together, they provide comprehensive poetry.lock security coverage.
Ce que Poetry fait bien : builds deterministes et verrouillage des dependances
Poetry est devenu l'un des outils de gestion de dependances les plus populaires de l'ecosysteme Python, et pour de bonnes raisons. Contrairement au workflow traditionnel pip install ou les versions peuvent varier d'une installation a l'autre, Poetry impose des builds deterministes grace a son mecanisme de lockfile. Quand vous executez poetry lock, chaque dependance — directe et transitive — est verrouillée sur une version exacte avec un hash cryptographique.
C'est une amelioration majeure par rapport aux simples fichiers requirements.txt. Le fichier poetry.lock capture l'intégralité du graphe de dependances : chaque package, chaque version, chaque hash. Quand un collegue execute poetry install, il obtient exactement le meme arbre de dependances que vous. Pas de surprises. Pas de problemes "ca marche sur ma machine" causes par une sous-dependance silencieusement mise a jour.
Poetry gere aussi la resolution de dependances de maniere intelligente. Si le package A requiert requests>=2.25 et le package B requiert requests<2.30, Poetry calcule l'intersection et verrouille une version compatible. Cette resolution de conflits se fait au moment du lock, pas a l'installation, ce qui vous permet de detecter les incompatibilites avant qu'elles n'atteignent la production.
La verification des hash integree a poetry.lock ajoute une couche de protection supplementaire. Chaque archive de package est enregistree avec son hash SHA-256, donc si un package sur PyPI etait modifie entre votre lock et votre installation, Poetry refuserait de continuer. Cela protege contre les attaques supply-chain au niveau de la distribution.
Pourquoi verrouiller les versions ne suffit pas
Voici la verite inconfortable : une dependance verrouillée n'est pas une dependance sure. Votre poetry.lock peut verrouiller cryptography==41.0.3 aujourd'hui, et demain une CVE critique pourrait etre publiee contre cette version exacte. Le lockfile a fait son travail — il vous a donne la reproductibilite — mais il ne peut pas vous proteger contre les vulnerabilites decouvertes apres le verrouillage.
C'est la tension centrale de la securite de poetry.lock. Le lockfile est concu pour geler votre graphe de dependances, ce qui est exactement ce que vous voulez pour la stabilite. Mais geler signifie aussi que vous ne recevez plus les correctifs. Chaque jour que votre lockfile vieillit, la probabilite qu'il contienne une vulnerabilite connue augmente.
Considerez un scenario reel : vous livrez la version 1.0 de votre application en janvier. Votre poetry.lock verrouille 147 packages. En mars, huit de ces packages ont recu des correctifs de securite. En juin, ce nombre depasse probablement vingt. Votre application utilise toujours les versions de janvier — deterministes, reproductibles, et de plus en plus vulnerables.
Le probleme s'aggrave avec la vitesse de divulgation des CVE. La base de donnees OSV recoit des milliers de nouveaux rapports de vulnerabilites Python chaque annee. Beaucoup affectent des packages populaires dont presque tous les projets dependent : requests, urllib3, cryptography, Pillow, certifi. Si vous ne surveillez pas activement vos versions verrouillées contre les CVE nouvellement publiees, vous volez a l'aveugle.
Comment auditer les dependances Poetry
Il existe plusieurs approches pour effectuer une verification de vulnerabilites Poetry, allant des outils integres aux scanners de securite dedies.
poetry audit (Plugin Poetry)
Poetry lui-meme a gagne une commande poetry audit via le poetry-audit-plugin. Installez-le avec :
poetry self add poetry-audit-plugin
Puis executez :
poetry audit
Cela interroge les bases de donnees de vulnerabilites et signale les CVE connues pour vos dependances verrouillées. C'est l'integration la plus naturelle puisqu'elle lit directement poetry.lock. Cependant, la couverture de sa base de vulnerabilites peut etre en retard par rapport aux outils dedies.
pip-audit
pip-audit de Google est l'un des scanners de securite Python les plus complets disponibles. Il interroge la base de donnees OSV.dev et supporte les projets Poetry :
# Exporter au format requirements d'abord
poetry export -f requirements.txt --output requirements.txt
pip-audit -r requirements.txt
Ou, si vous avez l'environnement installe :
pip-audit
pip-audit fournit des identifiants CVE detailles, des scores de severite et des versions de correction. Sa principale limitation est qu'il opere sur l'environnement installe ou un fichier requirements, pas directement sur poetry.lock. Cela signifie que vous avez besoin d'une etape d'export supplementaire, qui peut manquer les dependances de dev ou les extras.
safety check
Le package safety de SafetyCLI verifie vos dependances contre la Safety DB :
poetry export -f requirements.txt --output requirements.txt
safety check -r requirements.txt
Safety fournit une sortie claire et lisible avec des descriptions de vulnerabilites. La version gratuite utilise une base de donnees qui peut avoir jusqu'a 30 jours de retard par rapport a la version commerciale.
Revue manuelle de poetry.lock
Pour les deploiements critiques, la revue manuelle du diff de votre poetry.lock avant le merge est precieuse. Utilisez git diff poetry.lock pour voir ce qui a change. Recherchez :
- Les retours en arriere de version (pourraient reintroduire des vulnerabilites corrigees)
- De nouveaux packages que vous n'avez pas explicitement ajoutes (importes comme deps transitives)
- Les packages avec un historique de securite connu (
PyYAML,Jinja2,Pillow) - Des changements de hash sans changement de version (preoccupation potentielle de supply-chain)
Le probleme des dependances transitives
C'est la que les vulnerabilites de poetry.lock se cachent le plus souvent. Vous choisissez soigneusement vos dependances directes — peut-etre dix ou quinze packages. Mais chacun d'eux importe ses propres dependances, et celles-ci en importent d'autres. Un projet Python typique avec 10 dependances directes peut facilement avoir 80 a 150 packages au total dans poetry.lock.
Vous avez verifie Django et celery et boto3. Mais avez-vous verifie kombu, amqp, vine, botocore, jmespath, s3transfer, python-dateutil ? Ces dependances transitives sont dans votre lockfile, s'executent dans votre environnement de production, et portent leur propre surface d'attaque CVE.
Le defi est que vous n'avez pas choisi ces packages. Ils ont ete choisis pour vous par vos dependances directes. Vous ne savez peut-etre meme pas qu'ils existent jusqu'a ce qu'un scanner de vulnerabilites en signale un. Et quand une CVE touche une dependance transitive, le chemin de correction n'est pas toujours clair : vous devrez peut-etre attendre que votre dependance directe mette a jour ses propres contraintes avant de pouvoir adopter la version corrigee.
Poetry gere cela mieux que pip simple car il rend au moins l'arbre complet visible. Executez poetry show --tree pour voir le graphe de dependances complet. Mais la visibilite n'est pas la meme chose que la securite. Vous avez besoin d'une surveillance active pour detecter quand un package dans cet arbre — direct ou transitif — devient vulnerable.
Poetry vs pip vs Pipenv : comparaison securite
Comment Poetry se compare-t-il aux autres gestionnaires de dependances Python en matiere de securite ?
| Fonctionnalite | Poetry | pip + requirements.txt | Pipenv |
|---|---|---|---|
| Lockfile | poetry.lock (automatique) | pip freeze manuel | Pipfile.lock (automatique) |
| Verification des hash | Oui (integree) | Optionnelle (--require-hashes) | Oui (integree) |
| Resolution de dependances | Solveur SAT | Basique (pip 23+ ameliore) | Resolveur personnalise |
| Audit integre | Via plugin | pip-audit (separe) | pipenv check |
| Separation dev/prod | Groupes (dev, test, etc.) | Fichiers separes | Section [dev-packages] |
| Visibilite transitive | Arbre complet dans le lockfile | Liste plate uniquement | Arbre complet dans le lockfile |
Poetry et Pipenv fournissent tous deux une securite solide basee sur le lockfile avec verification de hash. L'avantage de Poetry est sa fonctionnalite de groupes de dependances, qui permet de separer proprement les dependances dev, test et production. C'est important pour la securite car les packages dev-only (linters, frameworks de test) ne devraient pas s'executer en production, reduisant votre surface d'attaque.
pip seul offre le moins de securite par defaut. Sans lockfile, vous n'avez aucune garantie de reproductibilite. Meme avec pip freeze > requirements.txt, vous perdez la structure de l'arbre de dependances — vous ne pouvez pas distinguer les packages directs des transitifs. Le flag --require-hashes ajoute la verification de hash mais doit etre maintenu manuellement.
Quel que soit l'outil que vous utilisez, aucun d'entre eux ne fournit de surveillance continue des vulnerabilites. Ils peuvent vous dire l'etat de vos dependances maintenant, mais ils ne peuvent pas vous alerter quand une nouvelle CVE est publiee demain contre un package que vous avez verrouille la semaine derniere. Cela necessite une solution de surveillance externe.
Mettre en place une surveillance continue avec OptiBot
Les audits ponctuels sont un bon debut, mais ils ne detectent que les vulnerabilites connues au moment ou vous executez le scan. Qu'en est-il de la CVE publiee le lendemain de votre dernier audit ? C'est la que la surveillance continue change la donne.
CVE OptiBot fournit des scans automatises quotidiens de votre fichier poetry.lock contre la base de donnees de vulnerabilites OSV.dev. Voici comment le mettre en place :
- Creez un compte sur cve.optibot.re
- Creez un projet et telechargez votre fichier
poetry.lock - OptiBot analyse votre lockfile, extrayant chaque nom de package et version verrouillée
- Des scans quotidiens verifient toutes vos dependances contre les CVE nouvellement publiees
- Des alertes email vous notifient immediatement quand une vulnerabilite est trouvee
La difference cle avec les outils ponctuels comme pip-audit est la persistance. OptiBot ne verifie pas vos dependances une seule fois — il les surveille en continu. Quand un chercheur decouvre une vulnerabilite dans urllib3 a 3h du matin et qu'elle est ajoutee a la base OSV, votre prochain scan quotidien la detecte et vous recevez une alerte avant votre cafe du matin.
Pour les agences gerant plusieurs projets clients, le tableau de bord d'OptiBot montre la posture de securite de tous vos projets en un coup d'oeil. Plus besoin d'executer poetry audit individuellement sur quinze repos differents.
Bonnes pratiques pour la securite Poetry
1. Mettre a jour regulierement, mais deliberement
Executez poetry update sur une cadence reguliere — hebdomadaire ou bihebdomadaire pour les projets actifs. Mais ne mettez jamais tout a jour aveuglement. Utilisez d'abord poetry update --dry-run pour voir ce qui changerait. Consultez le changelog de chaque package mis a jour, surtout pour les changements de version majeure.
2. Revoir les diffs du lockfile en code review
Traitez les modifications de poetry.lock comme des changements de code lies a la securite. Quand une PR modifie le lockfile, examinez le diff. Demandez-vous : quels packages ont change ? De nouvelles dependances transitives ont-elles ete introduites ? Des versions sont-elles revenues en arriere ?
3. Separer les dependances dev et production
Utilisez rigoureusement les groupes de dependances de Poetry :
[tool.poetry.group.dev.dependencies]
pytest = "^8.0"
mypy = "^1.8"
ruff = "^0.3"
[tool.poetry.group.test.dependencies]
factory-boy = "^3.3"
faker = "^24.0"
En production, installez avec poetry install --only main pour exclure entierement les packages dev et test. Moins de packages signifie moins de vulnerabilites potentielles.
4. Surveiller en continu
Un audit manuel hebdomadaire ne suffit pas pour les systemes en production. Les vulnerabilites sont divulguees quotidiennement. Utilisez un service de surveillance continue comme OptiBot pour combler l'ecart entre votre cadence d'audit et la cadence de divulgation des CVE.
5. Verrouiller les dependances directes avec reflexion
Dans pyproject.toml, utilisez les contraintes caret (^) pour les bibliotheques ou vous voulez les mises a jour de correctifs, et des versions exactes pour les packages ou la stabilite est critique. Le lockfile verrouille tout exactement dans tous les cas, mais vos contraintes determinent ce que poetry update est autorise a changer.
6. Automatiser le rafraichissement du lockfile en CI
Configurez une tache CI programmee (hebdomadaire) qui execute poetry update, cree une PR avec le diff, et lance votre suite de tests. Cela garantit que la fraicheur du lockfile ne depend pas de la memoire des developpeurs.
Workflow reel : du commit a la surveillance en production
Voici un workflow de securite complet pour une equipe utilisant Poetry :
Phase 1 : Developpement
Un developpeur ajoute une nouvelle dependance avec poetry add httpx. Poetry resout l'arbre de dependances, met a jour pyproject.toml et poetry.lock. Le developpeur commit les deux fichiers.
Phase 2 : Code review
Le relecteur de la PR verifie le diff de poetry.lock. Il voit httpx plus ses deps transitives : httpcore, h11, anyio, sniffio. Le relecteur verifie que ce sont des dependances legitimes et non des ajouts inattendus.
Phase 3 : Pipeline CI
Le pipeline CI execute l'etape d'audit :
# .github/workflows/security.yml
- name: Audit des dependances
run: |
pip install pip-audit
poetry export -f requirements.txt --output requirements.txt
pip-audit -r requirements.txt --fail-on-vuln
Si une dependance verrouillée a une CVE connue, le pipeline echoue. Le developpeur doit mettre a jour le package vulnerable avant le merge.
Phase 4 : Surveillance en production
Apres le deploiement, le fichier poetry.lock est telecharge sur CVE OptiBot. OptiBot scanne quotidiennement et alerte l'equipe si une dependance devient vulnerable — meme des mois apres le deploiement initial. Quand une alerte se declenche, l'equipe cree une PR prioritaire pour mettre a jour le package affecte, relance la CI, et deploie le correctif.
Cette approche a trois couches — code review, audit CI, et surveillance continue — detecte les vulnerabilites a chaque etape. La code review detecte les changements suspects. La CI detecte les vulnerabilites connues avant le merge. OptiBot detecte les vulnerabilites decouvertes apres le deploiement. Ensemble, ils fournissent une couverture complete de securite poetry.lock.