šŸš€ LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
šŸŽ“ COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
HTML MASTER CLASS /// LEARN TAGS /// BUILD STRUCTURE /// SEMANTIC WEB /// HTML MASTER CLASS /// LEARN TAGS ///

Python Dependency Security

The code you didn't write is still your risk — vulnerability scanning, hash verification, and the discipline that keeps a dependency tree from becoming your weakest security link.

⚔ Total XP: 0|šŸ’» python XP: 0

Skill Matrix

UNLOCK NODES BY LEARNING NEW TAGS.

System Hub

Core logic.

Quick Quiz //

Does a Python dependency run in any kind of restricted, sandboxed environment by default?


šŸš€ LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
šŸŽ“ COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.

Every dependency you install runs with your code's full permissions — reading your files, making network calls, doing anything your own code could do. This lesson covers the specific practices (scanning, hash verification, minimizing unnecessary dependencies) that keep that trust from becoming a liability.

1Full Trust, By Default: What a Dependency Can Actually Do

It's easy to think of a dependency as 'just some code that provides a function I call' — but the reality is more consequential: once pip install-ed and imported, a dependency's code runs with *exactly* the same permissions as your own code, with no default sandboxing, no restricted filesystem access, no restricted network access, nothing preventing it from doing anything your own code could do. A dependency can read your environment variables (including any secrets stored there, tying directly back to the Secrets Management lesson), read arbitrary files on disk, make outbound network requests to anywhere, or execute arbitrary further code — all without your own application code needing to explicitly grant any of that access.

This is precisely why a real, well-documented and increasingly common attack vector targets the software supply chain rather than your own code directly: compromising a popular package (through a maintainer account takeover, a malicious update slipped past review, or a deliberately similarly-named 'typosquatting' package) gives an attacker the exact same broad access your own application has, distributed automatically to every downstream project that installs it — a single compromise reaching potentially thousands of systems, rather than needing to attack each one individually.

This doesn't mean dependencies should be avoided — the entire modern software ecosystem depends on reusing well-tested, community-maintained code rather than reimplementing everything from scratch. It means the trust extended to every dependency (and every one of *its* dependencies, recursively, down the full tree) is a genuine, real security consideration, not merely a correctness or licensing one — and the depth of the dependency tree doesn't reduce that responsibility: a vulnerability three levels deep in a transitive dependency you never directly named is still fully your production risk.

āœ•
—
+
# When you run: pip install some-package
# 'some-package' can, when imported and used, do ANYTHING your
# own code could do -- read your filesystem, make network requests,
# access environment variables (including secrets) -- there's no
# sandboxing by default at all
localhost:3000
Full, Unsandboxed Trust
Any installed dependency
Full filesystem/network/environment access — identical to your own code

2Vulnerability Scanning: Finding Known Issues Directly

pip-audit (and similar tools — safety, and uv's own increasingly integrated scanning capabilities) checks your actual, currently-installed dependency versions against a maintained, continuously-updated database of *known*, publicly-disclosed vulnerabilities (sourced from advisories like GitHub's Security Advisory database, referenced by the GHSA-... identifiers in its output). This is a direct, mechanical, and genuinely actionable check — not a guess or a heuristic, but a lookup against documented, real vulnerabilities specific to the exact package and version you have installed.

The output's specificity is what makes it genuinely useful rather than merely alarming: it names the exact affected package and its currently-installed version, references the specific vulnerability by its tracked identifier (letting you read the actual advisory for details on severity and exploitability), and — critically — states the exact fixed version to upgrade to, turning a vague 'you might have a security problem' into a concrete, actionable 'upgrade requests from 2.25.0 to 2.31.0 or later.'

Running this kind of scan regularly — as part of CI, ideally on every dependency change and on a scheduled basis independent of code changes (since a package that was safe when installed can have a new vulnerability discovered and disclosed *after* installation, with no code change on your end at all) — closes the loop on dependency risk: knowing about a vulnerability the moment it's disclosed and publicly tracked, rather than discovering it only when it's actually exploited.

āœ•
—
+
$ pip install pip-audit
$ pip-audit

Found 2 known vulnerabilities in 1 package
Name    Version  ID              Fix Versions
------- -------- --------------- -------------
requests 2.25.0  GHSA-abcd-1234  2.31.0
localhost:3000
Actionable Vulnerability Detection
pip-audit
Exact package, exact version, exact fix — a directly actionable finding

3Hash Verification: Defending Against a Compromised Registry, Not Just Version Drift

The Dependency Management lesson covered lock file hashes primarily through the lens of *reproducibility* — guaranteeing the exact same bytes install on every machine, every time. There's a second, distinctly security-focused reason those hashes matter: they defend against a compromised package registry (or a compromised connection to it) silently serving different bytes than what was originally resolved and verified, for what claims to be the same package name and version.

Without hash verification, pip install package==1.0.0 trusts that whatever bytes the registry currently serves for "package" version "1.0.0" are genuinely the same bytes that existed when that version was first published and reviewed — an assumption that a compromised registry, a compromised CDN in front of it, or a man-in-the-middle attacker on an insecure connection could violate, serving malicious, tampered content under a legitimate, trusted-sounding package name and version number. pip install --require-hashes (and uv sync's default, built-in behavior) verifies the downloaded bytes' cryptographic hash against what's recorded in the lock file *before* installing, refusing to proceed if they don't match exactly — catching this specific tampering scenario regardless of how convincing the tampered package's name and version claim to be.

This is a genuine defense-in-depth measure, distinct from and complementary to vulnerability scanning: scanning catches *known*, already-disclosed vulnerabilities in legitimate package versions; hash verification catches *tampering* — a package claiming to be a specific, previously-verified version but actually containing different, unverified, potentially malicious content. Both matter, and neither substitutes for the other.

āœ•
—
+
# A lock file with hashes doesn't just guarantee the SAME VERSION --
# it guarantees the EXACT SAME BYTES, verified cryptographically
# uv sync / pip install --require-hashes both verify this automatically

# Protects against: a compromised package index serving tampered
# content for a package name/version that should be unchanged
localhost:3000
Tamper Detection
Hash verification (uv sync, pip --require-hashes)
Catches a compromised registry serving different bytes under a trusted name

4Step-by-Step Breakdown

A dependency's own dependency's own dependency having a known vulnerability is still YOUR production incident — the depth of the tree doesn't reduce the responsibility.

Every installed dependency runs with your code's FULL permissions -- reading files, making network calls, anything your own code could do.

Checkpoint: Does a Python dependency run in any kind of restricted, sandboxed environment by default?

  • →No — an installed dependency runs with your code's full permissions, with no default sandboxing or restriction
  • →Yes — pip automatically sandboxes installed packages, restricting their filesystem and network access

pip-audit (or uv's built-in equivalent) scans your installed dependencies against a database of KNOWN vulnerabilities -- a direct, actionable check.

Checkpoint: What does pip-audit's output actually tell you, beyond just "there is a vulnerability somewhere"?

  • →The specific package, its currently-installed version, the specific vulnerability ID, AND the exact version that fixes it
  • →Only a generic warning that "some dependency" might have an issue, without further detail

Lock file hash verification (from the Dependency Management lesson) also defends against a compromised package registry silently serving different bytes than what you originally resolved.

That completes Python Security — secrets, safe file handling, secure serialization, pickle-specific risks, and now dependency security. Next, Python Developer Tools covers the tooling ecosystem that helps enforce all of this automatically.

Confirm Real Dependency Permissions. Finish dependency_is_sandboxed_by_default(): every installed dependency runs with full permissions.

Level Up šŸš€

Advanced cheat sheets, SEO tricks, and interview prep for this topic.

Browser Support

ChromeSupported

Fully supported (via server-side Python execution).

FirefoxSupported

Fully supported (via server-side Python execution).

SafariSupported

Fully supported (via server-side Python execution).

EdgeSupported

Fully supported (via server-side Python execution).

Best Practices

Run a dependency vulnerability scanner (pip-audit or equivalent) regularly in CI, not just at initial dependency-adding time

A package safe when first installed can have a new vulnerability disclosed later with no code change on your end — scanning on a schedule, not just at install time, catches vulnerabilities discovered after the fact.

Use a lock file with hash verification (uv sync, pip --require-hashes) for every install, not just for reproducibility

Hash verification is also a security control, defending against a compromised registry silently serving tampered bytes under a legitimate package name and version.

Frequent Bugs

THE BUG

Treating dependency scanning as a one-time check performed only when a dependency is first added, missing vulnerabilities disclosed later against already-installed, unchanged dependency versions.

THE FIX

Run vulnerability scanning on a recurring schedule in CI (not just triggered by dependency changes), since new vulnerabilities in existing, already-installed versions are disclosed continuously, independent of your own code changes.

Real-World Examples

Integrating Dependency Vulnerability Scanning Into CI

A team wants their CI pipeline to automatically catch known vulnerabilities in their dependency tree, both on every pull request and on a recurring schedule to catch newly disclosed issues.

# .github/workflows/security.yml (conceptual)
# on: [pull_request, schedule: cron '0 6 * * *']
# steps:
#   - run: pip install pip-audit
#   - run: pip-audit --require-hashes -r requirements.txt
#   # Fails the build if any KNOWN vulnerability is found in the
#   # resolved dependency tree, on every PR AND on a daily schedule

Interview Prep

?Frequently Asked Questions

Pascual Vila

Pascual Vila

Frontend Instructor // Code Syllabus

Common Pitfalls & Errors

The Error //

Running dependency vulnerability scanning only as a one-time check when a project is first set up, missing vulnerabilities disclosed later against dependencies that haven't otherwise changed.

# Insufficient: only scanned once, at initial setup $ pip-audit # run once, manually, when the project was created # Correct: scheduled, recurring scan in CI # CI config: run pip-audit on every PR AND on a daily cron schedule, # catching vulnerabilities disclosed after initial installation

The Solution //

Schedule vulnerability scanning to run recurringly in CI (e.g. daily), independent of dependency changes, so newly disclosed vulnerabilities in already-installed packages are caught promptly.

Lesson Glossary

[01]Software supply chain

The full set of dependencies (and their own dependencies) a project relies on, each carrying the same trust and risk as a project's own code.

Code Preview
// Software supply chain context

[02]pip-audit

A tool scanning installed Python dependencies against a database of known, publicly-disclosed vulnerabilities.

Code Preview
// pip-audit context

[03]Hash verification

Cryptographically confirming downloaded package bytes match a lock file's recorded hash, defending against a compromised registry serving tampered content.

Code Preview
// Hash verification context

[04]Typosquatting

A supply-chain attack publishing a malicious package under a name similar to a popular legitimate one, hoping for accidental installation.

Code Preview
// Typosquatting context

Continue Learning