Saturday, April 4, 2026

Hardening Claude Code: Security Hooks for Package Managers & Web Access

In the previous post I introduced the Dynamic CDI Test Bean Addon — the first os890 CDI extension created entirely in an agentic AI workflow. During that and other longer multi-day sessions for larger projects with Claude, I also needed to harden the AI agent itself. Here's what came out of that: a set of PreToolUse hooks that improve security guardrails at runtime — suggested and implemented by Claude itself.

Disclaimer — Use at Your Own Risk and without warranty These hooks are a best-effort hardening measure, not a security guarantee. They are not a replacement for a proper outbound firewall, network-level controls, or enterprise security policies. Hook behavior may change with future Claude Code updates. URL extraction uses regex and may miss obfuscated network calls. Provided as-is, without warranty and at your own risk. Test thoroughly before relying on them.

The Two Risks

Claude Code can run shell commands, install packages, and fetch web content on your behalf. Two things which needed attention when called without scripts:

1. Supply-chain attacksnpm install / pnpm add / yarn add / bun install can execute arbitrary preinstall/postinstall scripts. A single malicious dependency can steal credentials or install backdoors.

2. Unrestricted web access — Claude Code can fetch any URL via WebFetch, WebSearch, or curl/wget. Prompt injection in fetched content could trick it into exfiltrating data.

There is also a third, subtler motivation: keeping research focused. In longer agentic sessions, an unrestricted agent will readily pull in information from wherever it lands — forums, random blogs, outdated docs. Limiting web access to a curated allowlist of trusted sources means Claude's research stays grounded in high-quality references rather than drifting toward whatever the search engine surfaces.

CLAUDE.md instructions help, but they can be ignored. Hooks can't — they intercept tool calls before execution.

The Setup

Two Node.js scripts in ~/.claude/, wired via settings.json:

1. package-install-check.js — blocks any npm/pnpm/yarn/bun install command that doesn't include --ignore-scripts:

// Receives tool call JSON on stdin, outputs decision to stdout
const input = JSON.parse(data);
const cmd   = input.tool_input.command || '';

if (
  /\b(npm|pnpm|yarn|bun)\s+(install|i|ci|add)\b/.test(cmd) &&
  !/--ignore-scripts/.test(cmd)
) {
  // deny — block before execution
} else {
  // allow
}

2. domain-check.js — checks URLs against ~/.claude/allowed-domains.txt. Handles WebFetch (URL check), WebSearch (allowed_domains parameter check), and Bash (curl/wget URL extraction).

3. settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "node ~/.claude/package-install-check.js"
          }
        ]
      },
      {
        "matcher": "WebFetch",
        "hooks": [
          {
            "type": "command",
            "command": "node ~/.claude/domain-check.js"
          }
        ]
      },
      {
        "matcher": "WebSearch",
        "hooks": [
          {
            "type": "command",
            "command": "node ~/.claude/domain-check.js"
          }
        ]
      }
    ]
  }
}

Lesson Learned: Hook Response Format

One thing that cost us time: the hook response format. Claude initially used {"decision":"allow"} which caused a PreToolUse:Bash hook error on every command. The correct format is:

// Allow
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow"
  }
}

// Block
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Explanation"
  }
}

Also: use file-based scripts with async stdin reading (process.stdin.on('data'/'end')), not inline node -e one-liners. The inline approach has stdin timing issues that cause spurious errors.

Full Setup & Scripts

The complete setup — including one-command install/uninstall scripts, the full domain-check.js, a starter domain whitelist, verification tests, and known limitations — will follow soon.

Dynamic CDI Test Bean Addon — The First os890 CDI Extension Created Entirely by AI

Welcome to os890.ai — a new blog dedicated to the open-source side of agentic AI workflows. If you've followed os890.blogspot.com for Java and CDI content, this is its AI-focused companion: a space to document what happens when agentic AI meets open-source tools, libraries, and real-world development workflows.

The first project featured here sets the tone perfectly.

The Dynamic CDI Test Bean Addon is a CDI portable extension that automatically mocks unsatisfied injection points using Mockito — enabling clean CDI SE tests in multi-module projects where not all beans are on the classpath. A useful open-source tool in its own right. But what makes it the right starting point for this blog is how it was built:

The entire addon was created from scratch using an agentic AI workflow — extension logic, 64 tests against both Weld and OpenWebBeans, build configuration with Enforcer, Checkstyle, and Apache RAT, DeltaSpike integration, and all accompanying documentation including this post.

No hand-written code. No manual scaffolding. Just an agentic workflow driving open-source tooling from zero to a fully tested, properly licensed, publishable library.

This project was deliberately chosen for that reason: a CDI extension is a small but precise piece of software. There is no room for vague approximations — contracts must be correct, edge cases in dependency resolution matter, and the test suite runs against two different CDI implementations. It is the kind of project where implementation details are not cosmetic. That makes it a much more honest proving ground for agentic AI than building a typical app where the rough edges of generated code rarely surface.

That intersection — agentic AI and open-source software, tested against problems that actually require precision — is exactly what this blog is about. How far can these workflows go? What does it take to produce production-quality open-source artifacts with AI? Where do the rough edges still show? These are the questions os890.ai will keep exploring.

Try It or Build On It

The project site covers the full feature set, from zero-config auto-mocking to composable @TestBean meta-annotations. It also ships with a SKILL.md file — a structured description designed to make the addon easy to pick up and use inside your own agentic AI workflows.

What's Next

This project also served as a real-world validation run for jawelte — more on that soon.

Hardening Claude Code: Security Hooks for Package Managers & Web Access

In the previous post I introduced the Dynamic CDI Test Bean Addon — the first os890 CDI extension created entirely in an agentic AI wor...