Files
mercadodevida/work/artifacts/TPV-DEV-IN-PROD/security.json

99 lines
5.9 KiB
JSON

{
"feature_id": "TPV-DEV-IN-PROD",
"verdict": "APPROVED",
"security": "security",
"reviewed_at": "2026-08-27T21:14:30Z",
"scope": [
"project/scripts/monolith.sh",
"docs/HOWTO-monolith.md",
"docs/pos/POS_OPERATIONS.md"
],
"summary": "Smoke test post-arranque en monolith.sh no introduce vulnerabilidades: zero secretos, zero nuevas dependencias, zero eval/source de inputs externos, todo lo que llega al shell es control interno del propio script. La función se ejecuta en el host del operador (no remotamente) y bypassa Authelia por diseño (conexión directa 127.0.0.1), lo que es coherente con el resto de monolith.sh. Verdict: APPROVED.",
"checks": [
{
"id": "no-secrets",
"description": "Cero secretos nuevos en el diff",
"command": "git diff project/scripts/monolith.sh docs/ | grep -iE 'password|secret|token|key|api[_-]?key'",
"result": "PASS",
"evidence": "Sin matches. El script sigue usando las mismas vars de entorno (NEXT_PUBLIC_API_URL, etc.) que ya estaban."
},
{
"id": "no-new-deps",
"description": "Cero dependencias nuevas",
"command": "git diff project/scripts/monolith.sh | grep -E '^\\+' | grep -E 'apt|yarn add|npm i|brew install|require\\(|import .* from'",
"result": "PASS",
"evidence": "Solo bash builtins (printf, grep, head, local, case, return), curl (ya usado en wait_http y check_services), y funciones internas (service_port, service_url, log_file)."
},
{
"id": "no-eval",
"description": "Sin eval/source de inputs externos",
"command": "git diff project/scripts/monolith.sh | grep -E '^\\+' | grep -E '\\beval\\b|\\bsource\\b'",
"result": "PASS",
"evidence": "Sin matches. No se introduce eval ni source de datos externos."
},
{
"id": "input-validation",
"description": "Validación de inputs adecuada (no user input)",
"command": "code review",
"result": "PASS",
"evidence": "service viene de ${NEXT_SERVICES[@]} (array literal). port/url/log derivados de funciones internas determinísticas. body viene de curl (sanitizado por bash). hmr_code viene de '%{http_code}' (curl no interpreta). No hay exposición de variables de usuario al shell."
},
{
"id": "bounded-curl",
"description": "Curls acotados con --max-time",
"command": "grep -E 'curl --max-time' project/scripts/monolith.sh | head -5",
"result": "PASS",
"evidence": "Smoke usa `curl --max-time 5` en todos los llamadas (root + /_next/hmr). Mismo patrón que wait_http (--max-time 3) y check_services (--max-time 5). Consistente con el resto del script."
},
{
"id": "grep-no-regex-injection",
"description": "Sin regex injection: usa grep -F (fixed string)",
"command": "grep -n 'grep ' project/scripts/monolith.sh | head -5",
"result": "PASS",
"evidence": "Smoke usa `grep -qF \"$marker\"` con -F (fixed-string mode). Los markers son literales del array HMR_MARKERS, no derivados de input. Imposible regex injection."
},
{
"id": "no-path-traversal",
"description": "Sin path traversal: log_file determinístico",
"command": "code review de log_file()",
"result": "PASS",
"evidence": "log_file() retorna \"$RUNTIME_DIR/$service.log\" donde RUNTIME_DIR=$MDV_RUNTIME_DIR/$MODE (control interno) y service viene de arrays literales (SERVICES, NEXT_SERVICES). Ningún componente viene de input de usuario."
},
{
"id": "side-effects-bounded",
"description": "Efectos secundarios acotados en fallo",
"command": "code review de la sección if ! smoke_check_no_dev_markers; then stop_all; exit 1; fi",
"result": "PASS",
"evidence": "stop_all solo mata PIDs en .runtime/$MODE/.pid (registrados por el propio spawn_service). No broad-kill. exit 1 aborta el script. El estado final del host queda limpio (sin procesos prod, sin PIDs huérfanos)."
},
{
"id": "no-secret-exposure",
"description": "Logs de error no filtran secretos",
"command": "git diff project/scripts/monolith.sh | grep -E '^\\+' | grep -E 'echo|printf' | grep -iE 'password|secret|token|key|env'",
"result": "PASS",
"evidence": "Mensajes de error solo mencionan service name, URL local 127.0.0.1, marker substring y HTTP status code. Sin variables de entorno, cookies, headers ni credenciales en stdout/stderr."
},
{
"id": "authelia-bypass-by-design",
"description": "Bypass de Authelia en smoke es deliberado y coherente",
"command": "code review",
"result": "PASS",
"evidence": "monolith.sh ya opera en host-local (wait_http, check_services, status_all usan 127.0.0.1). El smoke test sigue la misma convención: verifica el proceso local, no el edge público. Esto es coherente y no introduce regresión de seguridad."
}
],
"findings": [],
"notes": [
"El smoke test es una verificación operacional de defensa en profundidad. No añade ni quita controles de seguridad del edge (Authelia, Traefik, cookies Secure). El hardening real sigue siendo responsabilidad de la config fuera del repo.",
"El fix REDUCE el riesgo operacional de exponer dev internals al público (que es un fallo de seguridad aunque la app no se rompa: HMR WS abierto + source maps sin minificar)."
],
"verdict_rationale": "Sin findings críticos, altos ni medios. El diff no introduce secretos, no añade deps, no usa eval/source de inputs externos, no tiene regex ni path injection, y los efectos secundarios están acotados al propio PID space del script. El bypass de Authelia es deliberado y coherente con el resto del script. APPROVED.",
"evidence_paths": [
"work/artifacts/TPV-DEV-IN-PROD/architect.md",
"work/artifacts/TPV-DEV-IN-PROD/implementer.md",
"work/artifacts/TPV-DEV-IN-PROD/reviewer.json",
"project/scripts/monolith.sh (líneas 368-435)",
"docs/HOWTO-monolith.md",
"docs/pos/POS_OPERATIONS.md"
],
"agent": "security"
}