Why Understanding Libraries Matters for Supply Chain Security
A typical web application claims to use 5 or 6 libraries. Scan it with a vulnerability tool and it reports 50 vulnerabilities. That surprise gap comes from transitive dependencies: libraries your libraries depend on, and libraries their libraries depend on. A single Flask installation can pull in 20 transitive dependencies. Each of those dependencies has a maintainer, a release cycle, and potentially vulnerabilities. Without understanding this dependency tree, you can't assess your actual attack surface. You might think you're using a small number of vetted libraries, but you're actually executing code from dozens of strangers' projects.
A software library is reusable code someone else wrote that you include in your project. When your code uses a library, that library becomes a dependency—your software depends on it to function. Most applications run 50–500 libraries directly or transitively.
graph TD YourApp["Your Application"] YourApp --> Direct1["Direct Dependency<br/>Flask"] YourApp --> Direct2["Direct Dependency<br/>Requests"] Direct1 --> Trans1["Transitive<br/>Werkzeug"] Direct1 --> Trans2["Transitive<br/>Jinja2"] Direct2 --> Trans3["Transitive<br/>urllib3"] Trans1 --> Trans4["Transitive<br/>MarkupSafe"] style YourApp fill:#fff9c4 style Direct1 fill:#c8e6c9 style Direct2 fill:#c8e6c9 style Trans1 fill:#bbdefb style Trans2 fill:#bbdefb style Trans3 fill:#bbdefb style Trans4 fill:#f0f4c3Libraries, Packages, Modules — What's the Difference?
These terms are used interchangeably in practice, but they have subtle distinctions. A library is a collection of pre-written code that provides specific functionality. Examples include OpenSSL (cryptography), React (UI rendering), and NumPy (math).
A package is a library that's been bundled and published to a registry so others can install it. When you run pip install flask, you're installing the Flask package from PyPI, which contains the Flask library.
A module is a single file or unit within a library. A library typically contains many modules. In Python, import os.path imports the path module from the os library.
A framework is a special kind of library that provides structure for your entire application. Instead of you calling the library, the framework calls your code. Examples include Django, Spring Boot, and Express.js.
For security purposes, they all mean the same thing: code you didn't write that runs inside your application.
Why Libraries Exist
The Reuse Problem
Consider building a web application from scratch without libraries. HTTP parsing would require you to parse raw TCP bytes into HTTP requests, which is approximately 5,000 lines of code. TLS/SSL cryptographic handshakes need roughly 50,000 lines (OpenSSL alone is 500,000+ lines). JSON handling for parsing and serializing JSON requires about 2,000 lines. A database driver to speak PostgreSQL wire protocol needs roughly 10,000 lines. Date/time handling for time zones, leap seconds, and calendar math requires about 8,000 lines.
The total for just these basics is roughly 75,000 lines, a task that would take a single developer many months.
Instead:
# 5 lines replaces ~75,000 lines of custom codefrom flask import Flask, jsonifyimport psycopg2from datetime import datetimeimport jsonLibraries are essential. The question isn't whether to use them — it's how to use them safely.
The Security Dimension
Every Library Is an Attack Surface
When you add a library to your project, you're trusting the author to have written secure code and not have malicious intent. You're trusting the maintainers to review contributions carefully. You're trusting the registry to ensure the package you downloaded is the real one (not a typosquat or hijacked version). You're trusting the build system to ensure the published binary matches the source code.
Real-World Scale
Here's what a "simple" Python Flask application actually contains:
$ pip install flaskSuccessfully installed: flask-3.0.0 werkzeug-3.0.1 jinja2-3.1.2 markupsafe-2.1.3 itsdangerous-2.1.2 click-8.1.7 blinker-1.7.0 You asked for 1 library.You got 7.Those extra 6 libraries are transitive dependencies — libraries that Flask depends on. You didn't ask for them, but they're now running in your application. Each one is a potential vulnerability.
See: What are Transitive Dependencies?
How Libraries Are Organized by Language
Python
In Python, you declare dependencies in pyproject.toml or requirements.txt. The pip package manager then resolves the versions, downloads them from PyPI (pypi.org), and installs them into site-packages/.
# pyproject.toml[project]dependencies = [ "flask>=3.0", # Web framework "psycopg2>=2.9", # PostgreSQL driver "pydantic>=2.0", # Data validation]JavaScript / Node.js
In JavaScript/Node.js, you declare dependencies in package.json. npm (or yarn/pnpm) resolves the versions, downloads them from npmjs.com, and installs them into node_modules/.
{ "dependencies": { "express": "^4.18.0", "pg": "^8.11.0", "lodash": "^4.17.0" }}Java
In Java, you declare dependencies in pom.xml (Maven) or build.gradle (Gradle). Maven or Gradle resolves the versions, downloads them from Maven Central, and installs them into .m2/ or the gradle cache.
<!-- pom.xml --><dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>3.2.0</version> </dependency></dependencies>Go
In Go, you declare dependencies in go.mod with exact versions. The go mod download command fetches sources from pkg.go.dev (proxy.golang.org) and can optionally be vendored into vendor/.
// go.modmodule myapp require ( github.com/gin-gonic/gin v1.9.1 github.com/lib/pq v1.10.9)Rust
In Rust, you declare dependencies in Cargo.toml. Cargo resolves the versions, downloads them from crates.io, and compiles them from source.
# Cargo.toml[dependencies]actix-web = "4.4"serde = { version = "1.0", features = ["derive"] }tokio = { version = "1", features = ["full"] }Lock Files: Pinning Exact Versions
Every ecosystem has a lock file that records the exact version of every dependency (including transitive ones) that was resolved:
Language | Manifest | Lock File |
|---|---|---|
Python |
|
|
Node.js |
|
|
Java |
|
|
Go |
|
|
Rust |
|
|
Why lock files matter: Without a lock file, running pip install flask>=3.0 today might install Flask 3.0.0, but tomorrow it could install 3.1.0 — a different version with potentially different vulnerabilities or behavior.
Always commit lock files to version control. This ensures every developer and every CI build uses the exact same dependency tree.
Libraries in Container Images
When you build a container image, libraries become part of the image in two layers.
Application-Level Libraries
These are the libraries you declared in your manifest file:
FROM python:3.12-slimCOPY requirements.txt .RUN pip install -r requirements.txt # ← installs Flask, psycopg2, etc.COPY . /appSystem-Level Libraries
The base image itself contains operating system libraries including glibc (C standard library), libssl3 (OpenSSL), libpq5 (PostgreSQL client library), zlib (compression), libexpat (XML parsing), and many other system packages (100+ in total).
Both layers carry vulnerabilities. A vulnerability scanner might report:
CVE-2024-0001 HIGH libssl3 3.0.11 (system library in base image)CVE-2024-0002 MEDIUM jinja2 3.1.2 (transitive dep of Flask)CVE-2024-0003 LOW werkzeug 3.0.1 (transitive dep of Flask)This is why CleanStart builds images from verified source — both the application libraries AND the system libraries are tracked, scanned, and attested.
The Vocabulary You Need
Term | Meaning | Example |
|---|---|---|
Direct dependency | A library you explicitly declared |
|
Transitive dependency | A library pulled in by one of your dependencies |
|
Dependency tree | The full graph of all direct + transitive dependencies | Your app → Flask → Jinja2 → MarkupSafe |
Dependency resolution | The process of finding compatible versions of all libraries |
|
Version constraint | Rules for which versions are acceptable |
|
Pinned version | An exact version locked to prevent changes |
|
Vendoring | Copying dependency source code into your project |
|
Phantom dependency | A transitive dep you use directly but don't declare | Importing |
Diamond dependency | When two deps require the same library at different versions | Library A needs |
What to Read Next
What are Transitive Dependencies? — The explosion problem and why it matters for security. What is a Package Manager? — Tools that install and manage libraries. What is an SBOM? — The complete inventory of every library in your image. Transitive Dependency Removal — How CleanStart eliminates unnecessary dependencies. Dependency Intelligence — CleanStart's 281M+ dependency tracking system.
