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

推荐订阅源

云风的 BLOG
云风的 BLOG
The GitHub Blog
The GitHub Blog
Y
Y Combinator Blog
博客园 - 三生石上(FineUI控件)
T
The Blog of Author Tim Ferriss
宝玉的分享
宝玉的分享
Hugging Face - Blog
Hugging Face - Blog
WordPress大学
WordPress大学
V
Visual Studio Blog
小众软件
小众软件
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
MongoDB | Blog
MongoDB | Blog
V
V2EX
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 【当耐特】
Microsoft Azure Blog
Microsoft Azure Blog
The Cloudflare Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Engineering at Meta
Engineering at Meta
L
LangChain Blog
Martin Fowler
Martin Fowler
GbyAI
GbyAI
博客园 - 司徒正美

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
Detecting Supply-Chain Malware Without Running the Code
Pavel Espitia · 2026-06-27 · via DEV Community

Pavel Espitia

After I got targeted by a fake-job-interview repo designed to steal my keys, I built a scanner that checks a repository for supply-chain attacks without cloning, installing, or running any of it. The whole point is to find the malicious code statically, before it ever executes, because by the time you run npm install it is already too late. Here is how static detection of these attacks works and what it looks for.

Why static, and why before install

The dangerous moment in a supply-chain attack is install or build time. A postinstall script, a malicious dependency, a build step that runs arbitrary code. Once you run npm install, that code has already executed with your shell's environment, including any secrets it can reach.

So a scanner that runs the code to analyze it has already lost. The analysis has to be static: read the files, parse them, reason about them, and never execute a line. That constraint shapes everything.

What the scanner looks for

Three categories cover most of what I have seen in real lures.

1. Build-time code execution. The first thing I check is anything that runs during install or build:

// package.json scripts that fire automatically
const dangerousScripts = ["preinstall", "install", "postinstall", "prepare"];

A postinstall that runs an obfuscated script, downloads and executes a remote payload, or shells out to curl | sh is the single biggest red flag. Legitimate packages occasionally use these hooks, but a postinstall that fetches and runs remote code is almost never benign.

2. Dependencies missing from the lockfile. This is the subtle one, and it is how the attack that targeted me worked. The package.json declares a dependency, but it is not in the lockfile, or the lockfile points a known package name at a malicious tarball URL. The intent is that you trust the familiar name in package.json and never check what the lockfile actually resolves it to.

// Flag dependencies in package.json that the lockfile resolves
// to an unexpected registry or a direct tarball URL
function checkResolutions(pkg: PackageJson, lock: Lockfile) {
  for (const [name] of Object.entries(pkg.dependencies ?? {})) {
    const resolved = lock.packages[name]?.resolved;
    if (resolved && !resolved.startsWith("https://registry.npmjs.org/")) {
      flag(`${name} resolves to a non-registry URL: ${resolved}`);
    }
  }
}

A package named like a popular library but resolved from a random URL is a classic typosquat or hijack.

3. Obfuscation. Malicious payloads are usually obfuscated to hide what they do and slip past a casual reader. So I look for the fingerprints of obfuscation: long hex or base64 string literals, dense \x escape sequences, eval of a decoded string, arrays of character codes assembled at runtime.

function looksObfuscated(source: string): boolean {
  const longHexString = /["'][0-9a-f]{120,}["']/i.test(source);
  const evalOfDecoded = /eval\s*\(\s*(atob|Buffer\.from|decode)/.test(source);
  const charCodeArray = /String\.fromCharCode\s*\(\s*\d+(\s*,\s*\d+){20,}/.test(source);
  return longHexString || evalOfDecoded || charCodeArray;
}

None of these is proof of malice on its own. Together, on a file that also has a postinstall hook, they are damning.

The analyzer core is pure and testable

The most important architectural decision was keeping the analysis logic pure: it takes file contents as input and returns findings, with no I/O of its own. Fetching the repo is a separate layer. That separation means I can unit-test the analyzer against known-malicious and known-clean fixtures without any network or filesystem:

test("flags postinstall that pipes curl to sh", () => {
  const findings = analyze({ "package.json": MALICIOUS_FIXTURE });
  expect(findings).toContainEqual(
    expect.objectContaining({ rule: "remote-code-execution-on-install" }),
  );
});

A security tool that I cannot test thoroughly is a security tool I do not trust. Pure functions make the testing trivial.

Where AI fits, carefully

I do use an LLM as one layer, but not as the gate. The deterministic rules above catch the known patterns reliably. The model is for the judgment call on suspicious-but-not-obviously-malicious code: "this script downloads a config file and parses it, is that benign or a staged payload?" The model reasons about intent in a way regex cannot.

But the model never executes anything either, and I never let it be the sole reason to pass or fail a repo. Deterministic rules first, model for nuance, human for the final call. The model is an advisor, not an authority, because a model can be talked out of a finding and a regex cannot.

The real lesson

The attack that targeted me relied on me trusting a friendly repo and running its install step. The entire defense is to break that chain: analyze before you install, statically, and treat install-time code execution, lockfile mismatches, and obfuscation as the three things most likely to hurt you. You do not need to run hostile code to know it is hostile. You need to read it before it gets the chance to run.