58 lines
2.7 KiB
JSON
58 lines
2.7 KiB
JSON
{
|
|
"feature_id": "F-136",
|
|
"agent": "security",
|
|
"stage": "security_gate",
|
|
"verdict": "APPROVED",
|
|
"reviewed_at": "2026-08-21T19:10:00Z",
|
|
"summary": "Cambio puramente de presentación de datos existentes. Helper puro sin estado + migración idempotente que solo transforma casing de strings. Sin nuevas superficies de ataque, sin bypass de auth, sin exposición de datos sensibles. La transformación es destructiva del formato visual (ALL CAPS → Title Case) pero preserva toda la información semántica.",
|
|
"checks": [
|
|
{
|
|
"item": "Sin nuevos endpoints ni rutas",
|
|
"ok": true,
|
|
"evidence": "git diff solo toca src/shared/text.ts, src/shared/tests/text.test.ts, migrations/042_brand_title_case.js. No se modifica apps/admin/* ni project/src/modules/brands/api/*."
|
|
},
|
|
{
|
|
"item": "Helper toTitleCase es función pura sin acceso a DB/red/IO",
|
|
"ok": true,
|
|
"evidence": "src/shared/text.ts no tiene imports externos; solo usa métodos nativos de String y RegExp"
|
|
},
|
|
{
|
|
"item": "Sin inyecciones SQL — la migración usa queries parametrizadas",
|
|
"ok": true,
|
|
"evidence": "pgm.db.query(sql, [params]) con $1/$2/$3 placeholders, no concatenación"
|
|
},
|
|
{
|
|
"item": "Sin bypass de auth/role checks",
|
|
"ok": true,
|
|
"evidence": "La migración corre con permisos de DB de admin pero no se invoca desde código de usuario. El admin form sigue requiriendo role=admin para POST/PATCH /api/brands (verificado en brands.routes.ts:103,142)."
|
|
},
|
|
{
|
|
"item": "Sin exposición de datos sensibles",
|
|
"ok": true,
|
|
"evidence": "Solo se reformatea el campo `name` (público en GET /brands) y se copia a `seo_title` (también público). Sin tocar slugs, IDs, ni columnas internas."
|
|
},
|
|
{
|
|
"item": "Idempotencia previene ataques de re-aplicación",
|
|
"ok": true,
|
|
"evidence": "Re-correr la migración es no-op por la check `name !== UPPER(name)` en toTitleCase y la guard `if (!nameChanged && !seoChanged) continue` antes del UPDATE"
|
|
},
|
|
{
|
|
"item": "Sin secretos hardcodeados ni nuevas env vars",
|
|
"ok": true,
|
|
"evidence": "Diff solo añade código de transformación; sin constantes sensibles"
|
|
},
|
|
{
|
|
"item": "Migración actualiza solo `name`, `seo_title`, `updated_at`",
|
|
"ok": true,
|
|
"evidence": "UPDATE explícito solo sobre esas 3 columnas; `id` se usa solo en WHERE"
|
|
},
|
|
{
|
|
"item": "No toca slugs que son identificadores URL públicos",
|
|
"ok": true,
|
|
"evidence": "Las columnas `id` y `slug` no aparecen en el SET de la migración"
|
|
}
|
|
],
|
|
"issues": [],
|
|
"notes": "Cambio seguro. La transformación solo afecta formato visual de strings públicos. Riesgo de seguridad nulo."
|
|
}
|