#!/usr/bin/env node
// Hook: pre-write-executor.mjs
// Event: PreToolUse (Write)
// Purpose: Block writes to security-sensitive paths during plan execution.
//
// Protocol:
//   - Read JSON from stdin: { tool_name, tool_input }
//   - tool_input.file_path — the target path for Write tool
//   - BLOCK (exit 2): writes to security infrastructure, shell configs, secrets
//   - Allow (exit 0): everything else

import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';

const HOME = process.env.HOME || process.env.USERPROFILE || '/tmp';

// ---------------------------------------------------------------------------
// BLOCK rules — path patterns that must never be written during execution.
// ---------------------------------------------------------------------------
const BLOCK_RULES = [
  {
    name: 'Git hook injection (.git/hooks/)',
    test: (p) => /\/\.git\/hooks\//.test(p),
    description:
      'Writing to .git/hooks/ could inject malicious git hooks that execute ' +
      'on every commit, push, or checkout. Blocked.',
  },
  {
    name: 'Claude settings self-modification',
    test: (p) => /\/\.claude\/settings[^/]*\.json$/.test(p),
    description:
      'Writing to .claude/settings.json could disable security hooks or ' +
      'change permission modes. Blocked.',
  },
  {
    name: 'Claude hooks self-modification',
    test: (p) => /\/\.claude\/hooks\//.test(p) || /\/\.claude-plugin\//.test(p),
    description:
      'Writing to .claude/hooks/ or .claude-plugin/ could modify security ' +
      'hook configuration. Blocked.',
  },
  {
    name: 'Shell configuration files',
    test: (p) => {
      const sensitive = [
        `${HOME}/.zshrc`,
        `${HOME}/.bashrc`,
        `${HOME}/.bash_profile`,
        `${HOME}/.profile`,
        `${HOME}/.zshenv`,
        `${HOME}/.zprofile`,
      ];
      const resolved = resolve(p);
      return sensitive.some((s) => resolved === s || resolved.startsWith(s + '.'));
    },
    description:
      'Writing to shell config files (~/.zshrc, ~/.bashrc, etc.) could inject ' +
      'persistent commands. Blocked.',
  },
  {
    name: 'SSH directory',
    test: (p) => {
      const resolved = resolve(p);
      return resolved.startsWith(`${HOME}/.ssh/`) || resolved === `${HOME}/.ssh`;
    },
    description: 'Writing to ~/.ssh/ could compromise SSH keys or config. Blocked.',
  },
  {
    name: 'AWS credentials',
    test: (p) => {
      const resolved = resolve(p);
      return resolved.startsWith(`${HOME}/.aws/`) || resolved === `${HOME}/.aws`;
    },
    description: 'Writing to ~/.aws/ could compromise cloud credentials. Blocked.',
  },
  {
    name: 'GnuPG directory',
    test: (p) => {
      const resolved = resolve(p);
      return resolved.startsWith(`${HOME}/.gnupg/`) || resolved === `${HOME}/.gnupg`;
    },
    description: 'Writing to ~/.gnupg/ could compromise GPG keys. Blocked.',
  },
  {
    name: 'Environment files (.env)',
    test: (p) => /\/\.env(?:\.[a-zA-Z0-9]+)?$/.test(p),
    description:
      'Writing to .env files could expose or modify secrets. Blocked. ' +
      'Use .env.template instead.',
  },
];

// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------
let input;
try {
  const raw = readFileSync(0, 'utf-8');
  input = JSON.parse(raw);
} catch {
  // Cannot parse stdin — fail open.
  process.exit(0);
}

const filePath = input?.tool_input?.file_path;

if (!filePath || typeof filePath !== 'string') {
  process.exit(0);
}

const resolved = resolve(filePath);

for (const rule of BLOCK_RULES) {
  if (rule.test(resolved)) {
    process.stderr.write(
      `[ultraplan] BLOCKED: ${rule.name}\n` +
      `  Path: ${resolved}\n` +
      `  ${rule.description}\n`
    );
    process.exit(2);
  }
}

// Allow
process.exit(0);
