DEV Community

Cover image for How to Audit Open Source Dependencies in Python Scripts
Jay Grider
Jay Grider

Posted on • Originally published at chkdsklabs.com

How to Audit Open Source Dependencies in Python Scripts

Auditing open source dependencies in Python scripts often feels like checking a single line of code while the rest of the supply chain burns down. Most developers rely on pip check or basic linting tools, assuming that if their direct imports are clean, the application is secure. That assumption breaks the moment you realize that updating requests might accidentally pull in a vulnerable version of urllib3, or that a popular data library like pandas brings along its own unpatched transitive dependencies. The risk landscape isn't just about what you install; it's about the invisible layers of code your packages depend on, where CVEs often hide for months before a patch is available.

Direct dependencies are those explicitly listed in requirements.txt or pyproject.toml. While fixing them seems straightforward, they frequently introduce hidden risks through their own supply chains. Transitive dependencies—libraries imported by your direct libraries—frequently contain unpatched CVEs that standard scanners miss without deep traversal. The distinction matters because fixing a direct dependency rarely resolves the root cause if a vulnerable transitive layer is pulling in malicious or outdated code.

You need to move beyond simple version checks and start generating Software Bills of Materials (SBOM). Using tools like pip-compile or poetry export allows you to flatten dependency trees and identify every package version involved in your runtime environment. However, a flat list isn't enough for security correlation. You must convert these lists into structured SBOM formats like SPDX or CycloneDX. This enables automated correlation with vulnerability databases like NVD or GitHub Advisories, turning a static inventory into an actionable risk map.

Regularly regenerating SBOMs during CI/CD pipelines is critical for catching drift between development environments and production deployments before they become security incidents. Without this step, the "gold standard" of your build environment can diverge significantly from what actually runs in staging or production, leaving gaps where vulnerabilities go unnoticed until a breach occurs.

Interpreting SBOM data for actionable security decisions requires mapping specific package versions against known CVEs to prioritize patches based on severity scores (CVSS) and exploitability. You also need to identify "dependency hell" scenarios where updating one library breaks another, requiring strategic version pinning or containerization strategies. SBOM metadata is equally vital for auditing license compliance, ensuring that open-source components do not inadvertently introduce legal liabilities in commercial Python applications.

Even small teams using minimal frameworks often inherit massive dependency trees from popular libraries like pandas, requests, or tensorflow. Lack of formal SBOM generation leads to "blind spots" where critical vulnerabilities sit unpatched for months due to unclear ownership or version confusion. Adopting lightweight auditing practices early prevents technical debt accumulation that eventually forces expensive, rushed refactors during production outages.

As generative AI models integrate into Python workflows, the definition of "dependencies" expands to include model artifacts and inference libraries with unique supply chain risks. New safety standards and global initiatives emphasize the need for transparent artifact provenance, mirroring traditional software SBOM requirements but adapted for non-code assets. Auditing must now cover not just code vulnerabilities, but also potential data poisoning vectors or unauthorized model modifications within the dependency ecosystem.

The Gap Between Code SBOMs and Model Artifacts

Traditional Python auditing focuses entirely on the pyproject.toml. It validates that the code running your application matches the code declared in your manifest. But when you introduce local LLMs—whether via Hugging Face pipelines or custom quantization workflows—you are importing artifacts like .gguf and .safetensors files alongside your standard libraries. These are not code dependencies; they are binary assets that require their own layer of inspection.

OpenAI's recent push for global action on youth AI safety highlights the industry-wide shift toward treating model behavior and provenance with the same rigor as software code [[1]]. Yet, most Python projects still treat these model files as unstructured blobs. If a malicious actor compromises a source repository or injects a poisoned model artifact into your inference pipeline, standard dependency scanners won't catch it because they aren't designed to inspect the internal structure of binary tensors or metadata within GGUF files.

This is where specialized tools bridge the gap. L-BOM is a small Python CLI that inspects local LLM model artifacts such as .gguf and .safetensors files and emits a lightweight Software Bill of Materials (SBOM) with file identity, format details, model metadata, and parsing warnings [[2]]. By integrating this into your audit workflow, you can treat model artifacts with the same scrutiny as your Python packages.

You might start by scanning individual models to verify their integrity before adding them to your project. Running l-bom scan .\models\Llama-3.1-8B-Instruct-Q4_K_M.gguf generates a JSON output containing file size, SHA256 hashes, quantization details, and context length parameters [[2]]. This metadata allows you to correlate the model artifact against known supply chain risks or verify that it matches the expected baseline from your upstream provider.

For larger projects managing multiple models, recursive scanning becomes essential. You can execute l-bom scan .\models --format table to render a Rich table of all artifacts in a directory, quickly spotting anomalies in file sizes or missing metadata fields [[2]]. If you need to integrate this into documentation for your team or external consumers, the tool supports exporting scans as Hugging Face-ready README content via --format hf-readme, complete with customizable titles and descriptions [[2]].

Implementation Details: From CLI to CI/CD Pipeline

The real challenge isn't just running a scan; it's making the output part of your automated decision loop. When using L-BOM alongside standard Python dependency management, you create a unified view of your entire stack—both the code and the models it consumes.

Consider a typical CI pipeline. First, you run poetry export --format requirements-txt to generate a flat list of direct dependencies. Next, you execute pip-compile to lock versions and ensure reproducibility. But the final step must include artifact inspection. You can script a pre-commit hook or a GitHub Action that iterates through your model directory, runs l-bom scan, and checks the returned JSON for specific criteria.

For example, if your policy requires all loaded models to have a SHA256 hash that matches a known-good baseline stored in your environment variables, you can parse the output of l-bom to validate this programmatically. If the hash doesn't match or if the file size deviates significantly from the expected quantized size (e.g., a Q4 model suddenly being 10% larger), the pipeline fails immediately. This prevents poisoned artifacts from ever reaching your inference endpoints.

MODEL_PATH="./models/my-model.gguf"
EXPECTED_HASH="f6b981dcb86917fa463f78a362320bd5e2dc45445df147287eedb85e5a30d26a"

# Run L-BOM scan and capture JSON output
SCAN_OUTPUT=$(l-bom scan $MODEL_PATH)

# Extract actual hash using jq (requires jq installed)
ACTUAL_HASH=$(echo $SCAN_OUTPUT | jq -r '.sha256')

if [ "$ACTUAL_HASH" != "$EXPECTED_HASH" ]; then
    echo "Model integrity check failed. Hash mismatch detected."
    exit 1
fi

echo "Model integrity verified."
Enter fullscreen mode Exit fullscreen mode

This approach treats model files as first-class citizens in your security posture, mirroring the way you handle code vulnerabilities. It ensures that the "software bill of materials" for your AI application is just as rigorous as the one for your backend services.

By combining standard Python auditing practices with specialized artifact inspection, you cover the full spectrum of risks in modern Python applications. You avoid the trap of thinking that because your code is clean, your system is safe. Instead, you build a defensive layer that accounts for the reality of how dependencies and artifacts actually interact in production environments.

Edge Cases: When SBOMs Fail and What to Do About It

No tool catches everything, and L-BOM has limitations. It excels at static inspection of file metadata and format validation, but it cannot dynamically analyze the behavior of a model during inference or detect logic-level vulnerabilities embedded in the weights themselves. This is a critical distinction when discussing AI safety standards [[3]].

If your application loads models from an untrusted source—say, a user-uploaded file in a web interface—the static SBOM generated by L-BOM provides a baseline but doesn't guarantee safety against adversarial inputs or data poisoning. You still need runtime monitoring and potentially model-specific security checks that go beyond the initial artifact audit.

Furthermore, SBOMs can become stale. If you update your dependency tree frequently or switch between different quantized versions of a model, your SBOM must be regenerated. This is why integrating L-BOM into your CI/CD pipeline is non-negotiable. A manual scan done once a week won't catch the drift that happens daily in fast-moving development environments.

Another edge case arises with license compliance. While L-BOM can extract license information from model metadata if present, it cannot verify legal usage rights across all jurisdictions or complex downstream licenses. You must cross-reference the SBOM output with your organization's legal team and policy documents.

Finally, consider the human factor. A tool like HissCheck can help automate testing aspects of your Python scripts, but auditing dependencies is ultimately a process that requires discipline [[8]]. Developers often skip the extra steps required to scan artifacts because they focus on getting features done. Building these checks into the pipeline removes the need for manual intervention and ensures consistency across all team members.

When you combine L-BOM with standard Python tools, you create a comprehensive audit trail. This isn't just about compliance; it's about operational resilience. If a vulnerability is discovered in a popular library or a model artifact turns out to be compromised, you have the data you need to trace the impact instantly and remediate before it spreads.

Top comments (0)