HookStackGitHub
Back to catalogue
WorkflowPreToolUse· WebFetchPreToolUseBefore tool execution · can block⚡ blocking

Domain allowlist for WebFetch

Controls the agent's network access by only allowing WebFetch to a list of approved domains. Any other URL is blocked.

Use cases

  • Network control
  • Data compliance
  • Restricted environments

Providers & tags

Claude Code
#workflow#network#webfetch#allowlist

settings.json fragment

{
  "hooks": {
    "PreToolUse": [
      {
        "hooks": [
          {
            "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/webfetch-allowlist.mjs",
            "type": "command"
          }
        ],
        "matcher": "WebFetch"
      }
    ]
  }
}

Script · .claude/hooks/webfetch-allowlist.mjs

#!/usr/bin/env node
// Vérifie que l'URL est dans la liste des domaines autorisés (PreToolUse WebFetch)
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';
import { fileURLToPath } from 'url';

const DEFAULT_DOMAINS = [
  'github.com', 'raw.githubusercontent.com', 'npmjs.com',
  'docs.anthropic.com', 'developer.mozilla.org', 'nodejs.org',
  'www.typescriptlang.org', 'nextjs.org', 'tailwindcss.com',
  'supabase.com', 'vercel.com',
];

export function run(
  input,
  {
    exists = existsSync,
    readFile = readFileSync,
    projectDir = process.env.CLAUDE_PROJECT_DIR ?? process.cwd(),
  } = {},
) {
  const url = input.tool_input?.url ?? '';
  if (!url) return null;

  const allowlistFile = join(projectDir, '.claude', 'webfetch-allowlist.json');
  const allowed = exists(allowlistFile)
    ? JSON.parse(readFile(allowlistFile, 'utf8'))
    : DEFAULT_DOMAINS;

  try {
    const hostname = new URL(url).hostname;
    const ok = allowed.some((d) => hostname === d || hostname.endsWith('.' + d));
    if (!ok) {
      return {
        decision: 'block',
        reason: `Domaine non autorisé : ${hostname}. Ajoutez-le à .claude/webfetch-allowlist.json si nécessaire.`,
      };
    }
  } catch {
    // URL invalide — laisser passer
  }
  return null;
}

/* v8 ignore next 5 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
  const input = JSON.parse(readFileSync(0, 'utf8'));
  const result = run(input);
  if (result) process.stdout.write(JSON.stringify(result));
}