Safeguard
Security Guides•

How do you check a Python sdist for secrets and stray files?

Before uploading to PyPI, list your sdist and wheel, look for secrets and stray files, and test the install. A step by step checklist that takes minutes.

Safeguard Security Team
4 min read

Build your distributions, list every file inside the sdist and wheel, search them for secrets and files you did not mean to ship, then install the wheel in a clean environment. PyPI does not allow deleted files to be uploaded again, so catching a mistake before upload matters.

Why this check exists

Uploads to PyPI are public. The PyPI help page says deletion is permanent and irreversible, and that a distribution filename cannot be reused, even after the project is deleted and recreated. So the cheapest time to find a leaked token or a stray database dump is before you run upload.

The sdist specification defines only a few required files and says no other content is required or defined. Your build backend decides what else goes in. If your repository has a local config file, test fixtures or exported data, an overly broad include rule can pick them up.

The checklist

  1. Build from a clean tree. Run python -m build from a fresh checkout of the tagged commit, not your working directory. Untracked files then cannot slip in.
  2. List the sdist. Run tar tzf dist/yourpackage-1.0.0.tar.gz.
  3. List the wheel. Run unzip -l dist/yourpackage-1.0.0-py3-none-any.whl.
  4. Scan the names. Look for .env, key and certificate files, .pypirc, cloud credential files, local databases, notebooks with outputs, and editor folders.
  5. Scan the contents. Run a secret scanner over the extracted archives, not just your repository. A file generated at build time is invisible to repo scans.
  6. Check metadata. Open PKG-INFO in the sdist and the METADATA file in the wheel dist-info folder. Confirm name, version and license are right.
  7. Run twine check dist/*. It reports description rendering problems, and PyPI rejects uploads whose description fails to render.
  8. Install and test. Create a new virtual environment, install the wheel, and run your tests from a different directory.
  9. Upload to TestPyPI first. See how to test a release on TestPyPI.

Files that often cause trouble

Found in the archiveWhy it matters
Environment or credential filesCredentials become public
Test data with real recordsPrivacy and contract exposure
Large binaries or datasetsSize limits and slow installs
Duplicate vendored codeConfusing licensing and advisories
Build output foldersStale or unexpected code

If you find something after upload

Rotate any credential first, because yanking does not hide files. PyPI's own docs say yanking is non destructive: the files stay available and installers can still fetch them when pinned exactly. Delete only as a considered step, because deletion breaks pinned installs for your downstream users.

Make it routine

Put steps 1 to 7 in your release job so they run on every tag. A short allowlist of expected file patterns, checked in CI, catches new surprises.

Safeguard can run SBOM and dependency scanning and policy gates in your pipeline. You can also try the free scan first.

Quick check

Does yanking remove a leaked file from PyPI?

No. A yanked release stays downloadable, and installers can still select it when a version is pinned exactly. Revoke the credential.

Can I re-upload a file after deleting it?

No. PyPI states deleted files cannot be re-uploaded, so bump the version and rebuild.

Is the wheel safer than the sdist?

Neither is private. Check both, because their contents can differ.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.