DEV Community

Cover image for The Blind Spot in Vibe Coding: Your AI Agent Doesn't Check Licenses
Augusto Chirico
Augusto Chirico

Posted on

The Blind Spot in Vibe Coding: Your AI Agent Doesn't Check Licenses

You ask your AI agent to solve a PDF generation problem. Five minutes later, it has installed pandoc (GPL-2.0), pulled a WASM-based converter with ambiguous licensing, and resolved a dependency from an unofficial mirror. The problem is solved. The code works.

But now your project has a GPL dependency, a WASM binary whose license doesn't match its JavaScript wrapper, and a binary downloaded from a source with no integrity guarantees. The agent didn't check any of this. It wasn't built to.

I've run into this more than once while working on commercial products. Packages like these show up in your dependency tree without any friction, and by the time someone notices, the code is already in production. This post is what I wish I'd had before that happened — a practical guide to catching these issues early and automating the check so you don't have to think about it.

⚡ How It Happens

AI coding agents optimize for solving the problem you described. They search npm, crates.io, PyPI, GitHub — whatever produces a working solution fastest. What they don't do:

  • Read the license field in package.json
  • Distinguish between MIT and GPL
  • Know that your project is a commercial product
  • Check if a .wasm binary was compiled from GPL source code
  • Verify that a mirror is official or trusted

There's no malice here. The agent is simply indifferent to licensing. And when you're moving fast with AI-assisted development, that indifference compounds quietly.

🔍 Four Risks Worth Knowing

1. GPL/AGPL Contamination

GPL is a copyleft license. If your project links against a GPL dependency, your project must also be distributed under GPL — or you can't distribute it at all. AGPL extends this to network use: if users interact with your software over a network (i.e., most SaaS products), you must provide source code.

This isn't just about direct dependencies. If package A is MIT but depends on package B which is GPL, the GPL propagates up the tree.

Known examples: pandoc (GPL-2.0), ghostscript (AGPL-3.0), ffmpeg (GPL-2.0+ depending on build flags).

2. No License = No Permission

A common misconception: if a package has no license, it's free to use. Under copyright law, it's the opposite — no license means all rights reserved. You have no legal permission to use, copy, or distribute it.

npm packages with an empty license field, or with "UNLICENSED" in package.json, fall into this category. The agent installs them just like any other package.

3. WASM Binaries — The Black Box

An npm package can have a MIT-licensed JavaScript wrapper around a .wasm binary compiled from C, C++, or Rust source code. The binary inherits the license of its source code, not the wrapper.

svg2pdf-wasm illustrates this well: the npm package may declare one license, but the compiled binary comes from a Rust crate with its own licensing terms. The agent only reads package.json. It never checks the binary's provenance.

4. Unofficial Mirrors and Supply Chain

When an agent resolves dependencies, it may pull from non-standard sources — unofficial mirrors, npm proxies, or GitHub forks of abandoned packages. These aren't necessarily malicious, but they bypass the integrity guarantees of official registries.

Some packages also run postinstall scripts that download additional binaries at install time. The npm package might be MIT, but the binary it fetches could have entirely different terms.

Risk Red flag Impact
GPL in commercial project license: GPL-* or AGPL-* in package.json Must open-source your code or stop distributing
No license Missing or UNLICENSED license field No legal permission to use
WASM license mismatch .wasm files in node_modules Binary license ≠ wrapper license
Unofficial source Non-standard URLs in postinstall scripts No integrity guarantees

🛠️ The Solo Dev Recipe

Three checks before you deploy. No extra dependencies — just your package manager and standard shell tools.

Step 1: List All Licenses

pnpm has a built-in command for this:

pnpm licenses list --prod
Enter fullscreen mode Exit fullscreen mode

npm doesn't have a native equivalent, but every package.json has a license field. You can extract them all:

find node_modules -maxdepth 2 -name "package.json" -exec \
  jq -r '[.name, .version, .license // "NONE"] | @tsv' {} \; 2>/dev/null \
  | sort
Enter fullscreen mode Exit fullscreen mode

This gives you a complete list: package name, version, and license. Look for anything that isn't MIT, ISC, BSD, or Apache.

Step 2: Find Problematic Licenses

Filter for GPL, AGPL, or packages with no license at all:

# GPL/AGPL dependencies
pnpm licenses list --prod 2>/dev/null | grep -iE "GPL|AGPL"

# Packages with no license field
find node_modules -maxdepth 2 -name "package.json" -exec \
  jq -r 'select(.license == null or .license == "" or .license == "UNLICENSED") | .name' {} \; 2>/dev/null
Enter fullscreen mode Exit fullscreen mode

If either of these returns results, stop and investigate before shipping.

Step 3: Find WASM Binaries and Postinstall Scripts

These are supply chain concerns that no license field will tell you about:

# WASM binaries in your dependencies
find node_modules -name "*.wasm" 2>/dev/null

# Packages with postinstall scripts (can download arbitrary binaries)
find node_modules -maxdepth 2 -name "package.json" -exec \
  jq -r 'select(.scripts.postinstall != null) | "\(.name): \(.scripts.postinstall)"' {} \; 2>/dev/null
Enter fullscreen mode Exit fullscreen mode

If you find .wasm files, check the source repository. The npm package license is not authoritative for compiled binaries — the binary inherits the license of the code it was compiled from.

Putting It Together

A single script that covers all three checks:

#!/bin/bash
# license-audit.sh — zero dependencies, just shell + jq
set -euo pipefail

BLOCKED="GPL\|AGPL\|SSPL"
EXIT_CODE=0

echo "── License audit ──"
PROBLEMS=$(pnpm licenses list --prod 2>/dev/null | grep -iE "$BLOCKED" || true)
if [ -n "$PROBLEMS" ]; then
  echo "Copyleft licenses found:"
  echo "$PROBLEMS"
  EXIT_CODE=1
fi

echo "── Missing licenses ──"
MISSING=$(find node_modules -maxdepth 2 -name "package.json" -exec \
  jq -r 'select(.license == null or .license == "" or .license == "UNLICENSED") | .name' {} \; 2>/dev/null)
if [ -n "$MISSING" ]; then
  echo "Packages with no license:"
  echo "$MISSING"
  EXIT_CODE=1
fi

echo "── WASM binaries ──"
WASM=$(find node_modules -name "*.wasm" 2>/dev/null)
if [ -n "$WASM" ]; then
  echo "Found (verify source licenses manually):"
  echo "$WASM"
fi

echo "── Postinstall scripts ──"
POSTINSTALL=$(find node_modules -maxdepth 2 -name "package.json" -exec \
  jq -r 'select(.scripts.postinstall != null) | "\(.name): \(.scripts.postinstall)"' {} \; 2>/dev/null)
if [ -n "$POSTINSTALL" ]; then
  echo "Packages with postinstall:"
  echo "$POSTINSTALL"
fi

exit $EXIT_CODE
Enter fullscreen mode Exit fullscreen mode

Drop this in your repo as scripts/license-audit.sh, run it before deploys or in CI. It uses pnpm, jq, find, and grep — tools that are already on your machine.

🏗️ Scaling to Teams: A Claude Code Hook

The script above works for one developer. In a team with multiple AI agents running in parallel, manual checks don't hold up. You need something automatic.

A Claude Code PostToolUse hook that runs after every package install:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'if echo \"$TOOL_INPUT\" | grep -qE \"(npm install|pnpm add|pnpm install|yarn add)\"; then pnpm licenses list --prod 2>/dev/null | grep -iE \"GPL|AGPL\" && echo \"⚠ Copyleft license detected\" || true; fi'"
          }
        ]
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Every time the agent runs pnpm add, npm install, or yarn add, this hook checks for copyleft licenses. If something problematic slipped in, the agent sees it right away.

This goes in your project's .claude/settings.json or your global ~/.claude/settings.json.

A Prompt-Based Alternative

For a more contextual approach, a prompt-based hook gives the agent enough information to reason about the problem and suggest alternatives:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "prompt",
            "prompt": "If the command just executed was a package install (npm install, pnpm add, yarn add), check for license issues: run `pnpm licenses list --prod 2>/dev/null | grep -iE 'GPL|AGPL'` and `find node_modules -name '*.wasm' 2>/dev/null`. If you find GPL/AGPL dependencies, identify them and suggest MIT/Apache-licensed alternatives. If you find WASM binaries, flag them and note that their license may differ from the npm package license."
          }
        ]
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

With this version, the agent doesn't just detect the problem — it looks for alternatives and explains the licensing concern.

🔒 Going Further: A License Audit Skill

For teams that want structured coverage, a dedicated Claude Code skill can encode your license policy, know your project type, and guide the agent through every install decision.

I'm publishing a reference implementation as a Claude Code skill: license-audit-skill.

What it includes:

  • License policy configuration — allowed/blocked licenses per project type (MIT, Apache, commercial SaaS)
  • Progressive disclosure — lightweight context by default; deep reference files (compatibility matrix, WASM audit guide) load only when needed
  • PostToolUse hook — automatic check after every package install, using only native tools
  • WASM binary audit — finds .wasm files and traces them to their source license
  • Supply chain checks — flags postinstall scripts that download external binaries
  • Alternative suggestions — when a package is blocked, helps the agent find a permissively-licensed replacement

The whole thing runs on pnpm, jq, find, and grep. No license-checker packages that themselves go unmaintained — which would be ironic in an article about dependency risk.

📋 TL;DR

Who What to do Tool
Solo dev Run pnpm licenses list --prod and check for GPL/AGPL Built-in
Team with AI agents Add a PostToolUse hook to .claude/settings.json Automatic on every install
Commercial product license-audit.sh in CI + prompt-based hook Shell script, zero dependencies
Risk level License types Action
🟢 Safe MIT, ISC, BSD-2, BSD-3, Apache-2.0, 0BSD Use freely
🟡 Review LGPL-2.1, LGPL-3.0, MPL-2.0, CC-BY-4.0 Check your linking and distribution model
🔴 Blocked GPL-2.0, GPL-3.0, AGPL-3.0, SSPL, UNLICENSED Don't use in commercial/closed-source without legal review

Your agent writes the code. You own the license. Know what's in your dependencies before someone else asks.

Originally published on augustochirico.dev

Top comments (0)