惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
T
Threat Research - Cisco Blogs
GbyAI
GbyAI
Y
Y Combinator Blog
美团技术团队
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 【当耐特】
S
SegmentFault 最新的问题
IT之家
IT之家
Recent Announcements
Recent Announcements
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
阮一峰的网络日志
阮一峰的网络日志
T
The Blog of Author Tim Ferriss
Martin Fowler
Martin Fowler
Microsoft Azure Blog
Microsoft Azure Blog
V
Visual Studio Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
U
Unit 42
WordPress大学
WordPress大学
博客园 - Franky
L
LangChain Blog
人人都是产品经理
人人都是产品经理
小众软件
小众软件
博客园 - 叶小钗
罗磊的独立博客
酷 壳 – CoolShell
酷 壳 – CoolShell
大猫的无限游戏
大猫的无限游戏
云风的 BLOG
云风的 BLOG
Vercel News
Vercel News
雷峰网
雷峰网
腾讯CDC
Google DeepMind News
Google DeepMind News
博客园 - 三生石上(FineUI控件)
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Help Net Security
Help Net Security
C
Check Point Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
N
News and Events Feed by Topic
V2EX - 技术
V2EX - 技术
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Schneier on Security
Schneier on Security
博客园 - 聂微东
A
Arctic Wolf
H
Heimdal Security Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Recent Commits to openclaw:main
Recent Commits to openclaw:main
T
The Exploit Database - CXSecurity.com
C
Cyber Attacks, Cyber Crime and Cyber Security
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Google DeepMind News
Google DeepMind News

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Supply chain npm vs PyPI: I compared both simulations and the most dangerous vector isn't what everyone thinks
Juan Torchia · 2026-05-08 · via DEV Community

Supply chain npm vs PyPI: I compared both simulations and the most dangerous vector isn't what everyone thinks

I'd just finished the PyPI post, closed the terminal feeling good about myself, and then sat there staring at two result files open in parallel splits: npm-simulation-results.json on the left, pypi-simulation-results.json on the right. The numbers looked different. Too different to ignore.

I hadn't planned to do this cross-analysis. It was one of those moments where the screen talks to you if you actually pay attention. Three hours later I had a thesis that made me uncomfortable enough to write it down.

My thesis: npm gets all the scrutiny, all the articles, all the Dependabot alerts. PyPI lives in an operational blind spot for most backend teams — and that blind spot is exactly the vector attackers are exploiting most consistently in 2025.


Supply chain attack npm vs PyPI: the numbers nobody compares side by side

I documented the npm simulation in my previous post on Node.js dependencies in production. The PyPI one with PyTorch Lightning came later, in an ML context. Now I'm putting them together.

Metric npm (Node.js) PyPI (Python/ML)
Direct packages in my stack 47 23
Total transitive packages 1,247 891
Surface not audited by scanner 34% 61%
Time to manual detection (simulated) 4h 20min 11h 45min
Packages without hash verification enabled 12% 78%
Maintainers with 2FA active (estimated avg) ~60% ~31%

That 78% of PyPI packages without hash verification isn't a number I pulled from some report — I measured it against my own production requirements.txt using a script I wrote that compares Requires-Dist against the hashes registered in the lock file. If you don't have a lock file for Python... we've got a problem that precedes the entire vector discussion.

The number that hit me hardest was detection time. Eleven hours and forty-five minutes for a simulated attack on the ML stack, versus four hours twenty for Node. That difference isn't random.


Why PyPI takes longer to detect: the ecosystem structure problem

There are three concrete technical reasons. Not opinions — actual ecosystem architecture differences.

1. The installation model is less deterministic

npm with a properly configured package-lock.json pins the dependency resolution chain in a reproducible way. Python with pip install -r requirements.txt and no explicit lock file (pip freeze does not count as a real lock) resolves at runtime. That means two separate installs can pull different versions without anyone noticing in a PR diff.

# npm: this pins the entire tree
npm ci --audit

# Python: this is NOT a real lock file
pip install -r requirements.txt

# This gets closer, but has its own limitations
pip install --require-hashes -r requirements-locked.txt

Enter fullscreen mode Exit fullscreen mode

# The script I used to audit hashes in my PyPI stack
import subprocess
import json
import sys

def check_installed_hashes():
    """
    Compares installed packages against the hashes
    registered in the PyPI index.
    Returns packages without integrity verification.
    """
    result = subprocess.run(
        ["pip", "list", "--format=json"],
        capture_output=True, text=True
    )
    packages = json.loads(result.stdout)
    no_hash = []

    for pkg in packages:
        name = pkg["name"]
        version = pkg["version"]
        # Query PyPI API to check if sha256 hash exists
        import urllib.request
        url = f"https://pypi.org/pypi/{name}/{version}/json"
        try:
            with urllib.request.urlopen(url, timeout=5) as r:
                data = json.loads(r.read())
                urls = data.get("urls", [])
                has_hash = any(
                    u.get("digests", {}).get("sha256")
                    for u in urls
                )
                if not has_hash:
                    no_hash.append(f"{name}=={version}")
        except Exception:
            # If no response, mark as unverifiable
            no_hash.append(f"{name}=={version} [unverifiable]")

    return no_hash

if __name__ == "__main__":
    problems = check_installed_hashes()
    print(f"\nPackages without verified hash: {len(problems)}")
    for p in problems:
        print(f"  - {p}")
    sys.exit(1 if problems else 0)

Enter fullscreen mode Exit fullscreen mode

2. The ML package lifecycle is longer and less watched

In a typical Node.js production stack, Dependabot or Renovate sends PRs every week. The noise is high, yes, but so is the review frequency. An ML package like torch, transformers, or lightning can stay pinned to a specific version for months because "if you touch ML versions the trained model breaks." That intentional freeze creates a massive window for typosquatting that nobody's going to question.

In my simulation, I introduced a package called torch-utils (fictional, inspired by the real vector from the PyTorch Lightning incident). I left it in the environment for 11 days without any automatic scanner flagging it. The equivalent npm package was detected in 18 hours by Snyk.

3. Security culture in ML doesn't come from DevSecOps

This is the uncomfortable thing to say, but it's real: most of the data scientists and ML engineers writing production requirements.txt files come from a culture where the goal is getting the model to converge, not keeping the supply chain secure. That's not their fault — it's a training gap the ecosystem still hasn't closed. Compare that to the Node ecosystem, where there are years of collective trauma post-left-pad, post-event-stream, post-ua-parser-js.


The mistakes I made in both simulations (and what I changed)

Mistake 1 — I simulated in isolation, not integrated

In the npm simulation I assumed the attacker injects into an isolated project. In reality, the most effective attacks I documented in 2024-2025 compromised packages that are transitive dependencies of development tools, not the app itself. The malicious package gets in through your linter's devDependency, not your ORM.

When I redid the simulation with that vector, detection time jumped from 4h 20min to 8h 10min for npm. Nearly double.

Mistake 2 — I underestimated the CI/CD vector in Python

PyPI has a specific problem with GitHub Actions workflows that use pip install directly without a lockfile on the runner. I had this in three of my own workflows before this analysis. That means if a package is compromised between two workflow executions, the second build can include the malware with no visible diff in the repository code.

# ❌ What I had — massive attack surface
- name: Install dependencies
  run: pip install -r requirements.txt

# ✅ What I changed to after the analysis
- name: Install dependencies with verification
  run: |
    pip install --require-hashes \
      --no-deps \
      -r requirements-hashed.txt
    # requirements-hashed.txt generated with pip-compile --generate-hashes

Enter fullscreen mode Exit fullscreen mode

Mistake 3 — I didn't measure post-compromise persistence

A successful supply chain attack doesn't end when the malicious package installs. What matters is how long it can exfiltrate data before being removed. I didn't measure this well in my simulations. When I added it as a metric, the Python ecosystem showed longer persistence windows because ML deployments have slower update cycles than Node.js running on Railway.

This connects to what I learned about guardrails for autonomous agents: systems with lower change frequency have more persistence surface for any attack vector, not just supply chain.


The gotchas no standard checklist mentions

Gotcha 1: pip install from direct git refs

# requirements.txt with this is an auditing nightmare
git+https://github.com/someone/repo@main#egg=my-package

Enter fullscreen mode Exit fullscreen mode

No version. No hash. The @main can point to whatever commit the repo owner pushes next. I saw this in three different ML projects over the past year. npm has the equivalent with github:user/repo but the practice is far less common in production there.

Gotcha 2: The namespace packages problem in PyPI

PyPI doesn't have namespaces with verified ownership the way npm has @scope/package. Anyone can publish numpy-utils, pandas-extras, or torch-helpers. A similar name requires no relationship to the original package whatsoever. This is structurally different from npm where scoped packages give a much clearer ownership signal.

Gotcha 3: Packages with compiled C extensions

Both in npm (packages with native bindings) and PyPI (packages with .so extensions), compiled code is not analyzable by standard static scanners. But in PyPI this is far more common: numpy, scipy, torch — they all ship compiled code. That means a source code audit doesn't cover you. You need dynamic behavioral analysis, which almost no team has in their standard pipeline.

The same applies to how I think about reproducible environments in my Docker stack on Railway: images with compiled dependencies are harder to verify at runtime.


Unified audit checklist: npm + PyPI in the same pipeline

This is the artifact that was left hanging after the two previous posts. Unified, prioritized, with what I actually use.

## SUPPLY CHAIN CHECKLIST — npm + PyPI unified

### CRITICAL (blocks deploy if it fails)
- [ ] npm: package-lock.json committed and not in .gitignore
- [ ] npm: npm ci instead of npm install in CI/CD
- [ ] PyPI: requirements-hashed.txt generated with pip-compile --generate-hashes
- [ ] PyPI: pip install --require-hashes in all CI workflows
- [ ] Both: no packages installed from git refs without a fixed hash
- [ ] Both: vulnerability scanner running on every PR (Snyk / pip-audit)

### HIGH (fix within the week if it fails)
- [ ] npm: npm audit --audit-level=high in pre-commit hook
- [ ] PyPI: pip-audit --require-hashes running weekly
- [ ] Both: review of maintainers with push access on critical packages
- [ ] Both: new version alerts on the 10 most critical packages in each stack
- [ ] Docker images: COPY requirements before RUN install for cache invalidation

### MEDIUM (next sprint)
- [ ] npm: Dependabot or Renovate with automatic PRs and weekly limit
- [ ] PyPI: manual review of packages with compiled C extensions
- [ ] Both: SBOM (Software Bill of Materials) generated per build and archived
- [ ] Both: documented freeze policy for pinned ML packages
- [ ] CI/CD: environment variables without registry credential access from workers

Enter fullscreen mode Exit fullscreen mode


FAQ: supply chain attack npm vs PyPI

Is running npm audit or pip audit in the pipeline enough?

No, and I proved this in both simulations. Standard scanners detect known vulnerabilities in known versions. A brand new malicious package or fresh typosquatting won't show up in any advisory database for days or weeks. The scanner is necessary but not sufficient — you need hash verification, behavioral analysis, and alerts on new packages appearing in your transitive dependencies.

Why isn't pinning exact versions in Python enough?

Pinning torch==2.1.0 doesn't protect you if the .whl file on the PyPI index gets silently replaced. This happened in real incidents. The hash in the lockfile verifies that what you're downloading is exactly the same binary you verified before. Without a hash, the exact version is an illusion of control.

Does npm have a real advantage over PyPI in supply chain security?

Structural advantage, yes. npm has namespaced packages with verified ownership, a more developed provenance index, and a community with more years of collective supply chain trauma (from left-pad in 2016 to event-stream in 2018). That doesn't mean npm is safe — it means the ecosystem developed more defensive layers over time. PyPI is building its own, but years behind.

How do I detect typosquatting before it reaches production?

The technique that worked best for me: a pre-install script that compares each new package against a list of popular packages using Levenshtein distance. If numpy shows up as numppy or nunpy, it flags it. Not foolproof, but in my simulations it caught 70% of typosquatting cases before the static scanner even ran.

Are compiled ML packages auditable?

Partially. You can verify the binary hash against the hash published on PyPI. What you can't easily do is audit the source code that generated that binary. For that you need reproducible builds verified by third parties, which only the largest projects (numpy, scipy) have implemented. For everything else, the best practice is using official distributions from conda-forge or pip with hash verification, and never installing from alternative sources.

Is it worth having a separate audit pipeline for ML?

Yes, and that's the most important operational conclusion from this analysis. ML packages have different update cadences, compiled binaries, and teams with a different security culture than traditional backend. Treating them with the same pipeline as Express.js or FastAPI gives you false confidence. You need specific policies: freeze decisions documented with reasoning, mandatory hash verification, and manual review before updating ML dependencies in production.


The vector that worries me most going into 2026

After both simulations and crossing the numbers, my position is this: the Python/ML ecosystem is the most dangerous supply chain for most backend teams in 2025-2026 — not because it's technically more vulnerable than npm in absolute terms, but because the gap between attack sophistication and the average team's defensive maturity is wider.

npm has years of security culture baked in. Node teams know they need to watch Dependabot, run npm audit, distrust packages with a single maintainer. That accumulated knowledge matters.

ML-ops teams are in 2016 terms when it comes to supply chain. They pin versions to avoid breaking the model, they don't have real lockfiles, they install from direct git refs, and they have compiled binaries that no static scanner can read. That combination is the most exploitable vector right now.

What I'd do differently if I were starting from scratch: I'd treat the requirements.txt of an ML stack with the same paranoia I use for root access to production. Not because I'm being dramatic, but because my own simulation numbers say detection time is nearly triple that of Node. And three times longer means three times more exfiltration.

If this helps you revisit your stack's audit checklist, good. If you already have lockfiles with hashes in both ecosystems, even better. If not... the next PyPI supply chain incident is already in motion, and it probably won't show up in any advisory until it's too late.


If you came here from the npm post, the PyTorch Lightning one, or the guardrails for autonomous agents post — all three arcs connect: attack surface, entry vector, and post-compromise persistence are the same problem seen from three different angles.


This article was originally published on juanchi.dev