diff --git a/.gitea/workflows/gitleaks.yml b/.gitea/workflows/gitleaks.yml new file mode 100644 index 0000000..2b3cf1d --- /dev/null +++ b/.gitea/workflows/gitleaks.yml @@ -0,0 +1,27 @@ +# Escaneo de secretos en cada push y pull request. Este repo es PUBLICO. +# Falla si gitleaks encuentra una clave en los commits nuevos. +name: gitleaks + +on: + push: + pull_request: + +jobs: + gitleaks: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Instalar gitleaks + run: | + set -e + VERSION=8.21.2 + curl -sSL "https://github.com/gitleaks/gitleaks/releases/download/v${VERSION}/gitleaks_${VERSION}_linux_x64.tar.gz" -o /tmp/gitleaks.tgz + tar -xzf /tmp/gitleaks.tgz -C /tmp gitleaks + sudo install -m 755 /tmp/gitleaks /usr/local/bin/gitleaks + gitleaks version + + - name: Escanear el historial + run: gitleaks git --no-banner --redact --verbose . diff --git a/.githooks/pre-push b/.githooks/pre-push new file mode 100755 index 0000000..be98f3d --- /dev/null +++ b/.githooks/pre-push @@ -0,0 +1,49 @@ +#!/bin/sh +# Hook pre-push: bloquea el push si lo que se va a subir contiene secretos. +# +# Activar una vez por clon: git config core.hooksPath .githooks +# +# Este repo es PUBLICO: un secreto que llega al remoto queda expuesto aunque se +# borre en el commit siguiente (sigue en el historial). En 2026-03 se filtro asi +# una clave de API pegada en .claude/settings.json. +# +# Si gitleaks esta instalado (https://github.com/gitleaks/gitleaks) se usa el; +# si no, se aplica un escaneo minimo con grep sobre los formatos mas comunes. +set -u + +remote="$1" +z40=0000000000000000000000000000000000000000 +fallo=0 + +while read -r local_ref local_sha remote_ref remote_sha; do + [ "$local_sha" = "$z40" ] && continue # borrado de rama: nada que escanear + if [ "$remote_sha" = "$z40" ]; then + rango="$local_sha" # rama nueva: todo lo alcanzable + else + rango="$remote_sha..$local_sha" + fi + + if command -v gitleaks >/dev/null 2>&1; then + if ! gitleaks git --no-banner --redact --log-opts="$rango" . ; then + fallo=1 + fi + else + # Escaneo minimo: lineas AÑADIDAS en el rango con formatos de clave conocidos. + hits=$(git log -p --no-color "$rango" -- . ':!*.min.*' ':!*.lock' \ + | grep -E '^\+' \ + | grep -E 'AIza[0-9A-Za-z_-]{35}|sk_(live|test)_[0-9A-Za-z]{20,}|ghp_[0-9A-Za-z]{30,}|xox[bp]-[0-9A-Za-z-]{10,}|AKIA[0-9A-Z]{16}|eyJ[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{10,}|-----BEGIN [A-Z ]*PRIVATE KEY|(ANTHROPIC_AUTH_TOKEN|ANTHROPIC_API_KEY|OPENAI_API_KEY|API_KEY|SECRET|PASSWORD|TOKEN)["'"'"']?[[:space:]]*[:=][[:space:]]*["'"'"'][^"'"'"']{8,}' \ + | grep -vE '\$\{?[A-Z_]+\}?|\*\*\*|EJEMPLO|EXAMPLE|placeholder' ) + if [ -n "$hits" ]; then + echo "pre-push: posibles secretos en lo que vas a subir ($rango):" >&2 + printf '%s\n' "$hits" | cut -c1-120 | sed 's/^/ /' >&2 + echo "pre-push: instala gitleaks para un escaneo completo. Si es un falso positivo, --no-verify." >&2 + fallo=1 + fi + fi +done + +if [ "$fallo" -ne 0 ]; then + echo "pre-push: PUSH BLOQUEADO. Quita el secreto del commit (git commit --amend o rebase) y rota la clave si ya la has pegado en algun sitio." >&2 + exit 1 +fi +exit 0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..35821ad --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# Configuracion LOCAL de Claude Code: aqui van las claves (env con ANTHROPIC_*, +# tokens de proveedores...). NUNCA en .claude/settings.json, que es la plantilla +# versionada y este repo es PUBLICO. En 2026-03 se filtro una clave por ahi. +.claude/settings.local.json + +# Secretos y entorno +.env +.env.* +*.pem +*.key + +# Ruido de sistema +.DS_Store +Thumbs.db diff --git a/CLAUDE.md b/CLAUDE.md index c5d5a9a..8007f15 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -129,6 +129,7 @@ Do NOT modify web-base files — they are shared across all projects. 11. Twig concatenation uses `~` operator: `'value=' ~ variable` 12. `enlace` (link) fields already include slashes — **NEVER modify an existing enlace** unless explicitly asked 13. **NEVER modify the `controlador` field** of existing records — it defines whether a page is Builder or Standard +14. **This repository is PUBLIC. NEVER commit secrets** (API keys, `ANTHROPIC_*` tokens, passwords). Local Claude Code config with credentials goes in `.claude/settings.local.json` (git-ignored), never in `.claude/settings.json`. Enable the secret-scanning hook once per clone: `git config core.hooksPath .githooks`. A leaked key stays in git history even after deleting it: rotate it immediately. ## MCP Tools