Files
eden/.claude/hooks/guard_file.py
Matheus (Handix) 44510bd019 Bootstrap EDEN: Fase 0 (arquitetura) e Fase 1 (monorepo + infra)
Fase 0 — descoberta e arquitetura:
- Inventário do projeto, glossário de domínio, arquitetura com bounded
  contexts e topologia de containers, threat model inicial.
- 12 ADRs cobrindo modular monolith, topologia de containers (Postgres
  isolado + eden-core/parceiros/assinante em containers e portas
  distintos), auth/sessões, modelo de permissões, criptografia/segredos,
  contrato first-class, stock ledger, separação billing/finance/fiscal,
  outbox transacional, adapters SaperX e Focus NFe, e identidade
  compartilhada entre as 3 apps.
- 14 subagentes e 7 skills especializados por domínio em .claude/.
- Hooks de segurança (PreToolUse/PostToolUse/Stop) testados via pipe.

Fase 1 — plataforma (em andamento):
- Monorepo pnpm workspaces + Turborepo: apps/{api,worker,core-web,
  reseller-web,subscriber-web} + 9 packages compartilhados.
- apps/api: NestJS mínimo com /health/live e /health/ready (checando
  Postgres real via @eden/database).
- 3 frontends Vite + React + TypeScript + Tailwind, com o favicon
  oficial do EDEN.
- packages/database: migration baseline (node-pg-migrate) criando
  roles/role_permissions/applications/users/user_applications/sessions/
  audit_log — audit log append-only com hash-chain, testado ao vivo
  (UPDATE/DELETE bloqueados pelo trigger).
- compose.yaml implementando a topologia da ADR-0002, validada de ponta
  a ponta: os 6 containers sobem e ficam saudáveis com um único
  `docker compose up`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 08:01:14 -03:00

78 lines
2.2 KiB
Python
Executable File

#!/usr/bin/env python3
"""PreToolUse guard for Read/Write/Edit/Glob/Grep (EDEN, Master Prompt §2.1/§3.4).
Blocks access to paths outside the project root and to ~/.ssh or /etc, and
asks for confirmation before reading a .env/credential-looking file. Fails
open on any internal error.
"""
import json
import os
import re
import sys
PROJECT_ROOT = "/opt/eden"
HOME = os.path.expanduser("~")
SECRET_NAME_RE = re.compile(r"(^|/)(\.env(\..+)?|.*credential.*|.*secret.*)$", re.IGNORECASE)
def extract_path(tool_input: dict) -> str:
for key in ("file_path", "path", "notebook_path"):
if key in tool_input:
return tool_input[key] or ""
# Grep/Glob use "path" for the search root; pattern itself isn't a filesystem path.
return ""
def decide(path: str):
if not path:
return "allow", None
abspath = os.path.abspath(path)
ssh_dir = os.path.join(HOME, ".ssh")
if abspath == ssh_dir or abspath.startswith(ssh_dir + os.sep):
return "deny", "acesso a ~/.ssh"
if abspath == "/etc" or abspath.startswith("/etc" + os.sep):
return "deny", "acesso a /etc"
if abspath == "/root" or abspath.startswith("/root" + os.sep):
return "deny", "acesso a /root"
if abspath.startswith("/opt/") and not (
abspath == PROJECT_ROOT or abspath.startswith(PROJECT_ROOT + os.sep)
):
return "deny", f"caminho fora de {PROJECT_ROOT} (outro projeto no host)"
basename = os.path.basename(abspath)
if SECRET_NAME_RE.match(basename):
return "ask", f"leitura/escrita de arquivo que parece conter segredo ({basename})"
return "allow", None
def main():
try:
payload = json.load(sys.stdin)
tool_input = payload.get("tool_input") or {}
path = extract_path(tool_input)
except Exception:
print(json.dumps({}))
return
decision, reason = decide(path)
if decision == "allow":
print(json.dumps({}))
return
output = {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": decision,
"permissionDecisionReason": f"[eden-guard] {reason} (EDEN_MASTER_PROMPT_CLAUDE.md §2.1/§3.4)",
}
}
print(json.dumps(output))
if __name__ == "__main__":
main()