HookStack
Back to catalogue
SecurityPermissionRequest

Auto-approve read-only permissions

Stop clicking approve on harmless reads

Automatically approves permission requests for read-only operations (Read, Glob, Grep, safe Bash commands) without manual intervention.

What does the Auto-approve read-only permissions hook do?

Auto-approve read-only permissions is a Claude Code PermissionRequest hook. It fires automatically at that lifecycle event — outside the model, so it can't be skipped or forgotten. Stop clicking approve on harmless reads.

As a PermissionRequest hook it runs before the action completes, so it can block or adjust what Claude is about to do. Because it is a deterministic Node.js script, it executes on every matching event without relying on the model to remember — the guarantee that makes agentic workflows safe to automate.

Use cases

  • Reduce interruptions for non-destructive operations
  • Automated security policy based on command safety

Tags

#permission#security#auto-allow#readonly#policy

settings.json fragment

{
  "hooks": {
    "PermissionRequest": [
      {
        "hooks": [
          {
            "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/permission-auto-allow.mjs",
            "type": "command"
          }
        ],
        "matcher": ""
      }
    ]
  }
}

Script · .claude/hooks/permission-auto-allow.mjs

#!/usr/bin/env node
// @hookstack permission-request-auto-allow-readonly
// Auto-autorise les outils lecture seule et les commandes Bash sûres (PermissionRequest)
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";

const SAFE_BASH = [
	/^ls/,
	/^pwd/,
	/^echo/,
	/^cat(?!.*>)/,
	/^head/,
	/^tail/,
	/^wc/,
	/^which/,
	/^whereis/,
	/^file/,
	/^stat/,
	/^git\s+(status|log|diff|show|branch|tag)/,
	/^npm\s+(list|ls|outdated|view)/,
];

const READ_ONLY_TOOLS = ["Read", "Glob", "Grep"];

export function run(input) {
	const toolName = input.tool_name || "";
	const toolInput = input.tool_input || {};

	let allow = READ_ONLY_TOOLS.includes(toolName);
	if (!allow && toolName === "Bash") {
		const cmd = (toolInput.command || "").trim();
		allow = SAFE_BASH.some((p) => p.test(cmd));
	}

	return allow
		? {
				hookSpecificOutput: {
					hookEventName: "PermissionRequest",
					decision: { behavior: "allow" },
				},
			}
		: 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));
}

Learn more

Related hooks