Repository navigation
feat(platform): 0.8.0 — errores 500 opacos, auth opt-in con serviceId, logger endurecido, niveles, client-errors, higiene - #21
Merged
Conversation
…l cliente
toErrorResponse responde {error:"Error interno", code, requestId} cuando el error no es
operacional y el status es >= 500; el mensaje real solo va al log (redactado). Los 4xx y los
AppError operacionales conservan su mensaje.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015nTgTv7S3gQwaoNFw5fZeG
…gacy opt-in y modo estricto de módulos
readIdentity delega en readMcSession de mc-sso 1.3.0 y acepta {serviceId, allowedTenants,
normalizeTenant, acceptLegacy}; configureIdentity fija el default por app. Identity expone
serviceId. Sin serviceId configurado emite un WARN por proceso. No se deriva nada de SERVICE_ID
ni cambia el comportamiento por defecto. Se agregan isModuleAllowedStrict, requireModule con
{strict:true} y la opción strictModules de withApiHandler (null/[] = ningún módulo);
isModuleAllowed conserva su semántica.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015nTgTv7S3gQwaoNFw5fZeG
… reservadas, timeout y flush con reintento) y x-request-id validado
- redactSecrets sobre msg y extras (recursivo, profundidad 4, tolera ciclos).
- tenant/userId/requestId del contexto no se pisan desde los extras.
- fetch al ingest con AbortSignal.timeout(2000).
- flush con promesa en vuelo compartida; el lote fallido se reencola con tope de 1000 y los
envíos automáticos respetan un enfriamiento de 5 s tras un fallo.
- x-request-id entrante validado con /^[A-Za-z0-9._-]{1,128}$/ o se regenera.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015nTgTv7S3gQwaoNFw5fZeG
…xx retornado withApiHandler registra warn (sin stack) para AppError con status < 500 lanzados; los 5xx siguen en error. Reduce el ruido de level=ERROR por simples 401/403/404. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015nTgTv7S3gQwaoNFw5fZeG
… limit por IP confiable y validación de service/path
- el cuerpo se lee como stream y se corta al pasar maxBytes (413) sin content-length.
- clave del rate limit: x-vercel-forwarded-for / x-real-ip antes que x-forwarded-for.
- Map del rate limit con tope de 5000 entradas y poda de las más viejas.
- service (/^[A-Za-z0-9._-]{1,80}$/) y path (pathname codificado que empieza con /) se validan o 400.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015nTgTv7S3gQwaoNFw5fZeG
…a línea de log y version 0.8.0 - engines.node >=22 (el CI ya usa Node 22). - cada registro de log lleva platformVersion (constante src/version.ts + test que la ata a package.json) para medir adopción en platform_logs; los extras no pueden pisarla. - sin peerDependencies de next/react: el código no los importa (verificado con grep). - @vercel/oidc se queda en ^3.8.9: la 4.0.5 mantiene la API pero sigue arrastrando @vercel/cli-config -> zod 4.1.11 exacto, así que no cierra los 3 high de npm audit. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015nTgTv7S3gQwaoNFw5fZeG
…ge-lock con la versión 0.8.0 Incluye 'Cambios visibles', 'Cómo adoptar el endurecimiento de auth', la decisión pendiente sobre allowedModules vacío y lo que no se hizo (@vercel/oidc 4.x, peerDependencies, devDependencies). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015nTgTv7S3gQwaoNFw5fZeG
…ntity (platform 0.8.0) Identity ahora expone serviceId; se agrega la comprobación de que readIdentity con el serviceId de otra app rechaza la cookie. Solo toca el test; no cambia mc-sso ni su dist. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015nTgTv7S3gQwaoNFw5fZeG
…ico) vive en globalThis
Turbopack puede instanciar el módulo auth varias veces por proceso; con variables de módulo,
configureIdentity() en una copia no lo veía otra y el WARN salía una vez por copia. Se guarda en
globalThis[Symbol.for("@ai4u/platform/identity")].
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015nTgTv7S3gQwaoNFw5fZeG
… vez de rechazar el reporte
Un path con | [ ] ^ { } se registra tal cual; el resto de caracteres no seguros pasa a "_" y un path
sin "/" inicial se reemplaza por "/invalid". Un service fuera de /^[A-Za-z0-9._-]{1,80}$/ se
registra como "unknown-client". Se evita la regresión de 0.7.0 donde esos reportes daban 400.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015nTgTv7S3gQwaoNFw5fZeG
…o operacionales 5xx Además de ocultar el mensaje, se omite details cuando el error es no operacional y 5xx. Tests de ExternalServiceError con details y de AppError 503 no operacional. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015nTgTv7S3gQwaoNFw5fZeG
Un AppError con isOperational:false (bug inesperado) sigue en error con stack aunque su status sea 4xx, como documenta errors/index.ts. Los 4xx operacionales lanzados siguen en warn. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015nTgTv7S3gQwaoNFw5fZeG
…anitize que nunca lanza, métricas de tokens y circuit breaker
- Buffer/typed arrays se resumen ("[Buffer n bytes]"); objetos y arrays se truncan a 100 elementos.
- sanitize y el spread de extras van en try/catch (fallback {_sanitizeFailed:true}): un Proxy o getter
hostil no rompe log.info.
- max_tokens/inputTokens/tokens_used y bypass dejan de enmascararse; access_token, apiToken, token,
password, userPass siguen enmascarados.
- tras 3 fallos consecutivos del ingest, flushLogs() también espera 5 s de enfriamiento.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015nTgTv7S3gQwaoNFw5fZeG
…declarativo, duplicados del reencolado Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015nTgTv7S3gQwaoNFw5fZeG
Se enmascara toda clave con "pass" salvo una lista explícita de palabras seguras (por segmento) y toda clave con "token" salvo un patrón explícito de conteo (maxTokens, inputTokens, tokens_used…). Corrige huecos de la versión anterior (userpass, dbpass, PASS, secretTokens, my_tokens). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015nTgTv7S3gQwaoNFw5fZeG
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Alcance
@ai4u/platform0.8.0 (seguridad + robustez, Ola 2 del plan). Afecta a todos los consumidores de platform y a todos los tenants; no hardcodea ningún tenant. Solo código del kernel: no se publica, no se crea tag, no se toca ningún otro repo ni secretos. Un PR, un commit por grupo (más docs y un ajuste de test de mc-sso).Hallazgo -> commit -> test
tests/errors-500-opaco.test.tstests/auth-scope.test.tstests/logger-hardening.test.tstests/http-log-levels.test.tstests/client-errors-hardening.test.tstests/platform-version.test.tstests/auth-multicopy.test.tstests/client-errors-hardening.test.tsdetailsen 5xx no operacionaltests/errors-500-opaco.test.tstests/http-log-levels.test.tstests/logger-hardening2.test.tspackages/mc-sso/tests/handler.test.ts(solo el test de interop)Qué cambia de contrato visible (detalle en CHANGELOG 0.8.0)
{error:"Error interno", code, requestId}en vez deerr.message. 4xx y AppError operacionales conservan mensaje. Consumidores que muestrenerrorde un 500 al usuario deben revisarse (auditoría previa pendiente del plan).warn(anteserror): los 401/403 lanzados dejan de verse en ERROR. AppError con isOperational:false sigue en error.x-request-identrante inválido se regenera;service/pathraros de client-errors se neutralizan (no se pierde el reporte); el rate limit de client-errors usax-vercel-forwarded-for/x-real-ipprimero.IdentityexponeserviceId.engines.node >=22. Cada línea de log llevaplatformVersion.readIdentity(req, {serviceId, allowedTenants, acceptLegacy}),configureIdentity). Sin opciones el comportamiento es el de 0.7.0; no se deriva nada deSERVICE_ID. Un WARN por proceso cuando se lee una sesión sinserviceIdconfigurado.Qué NO se hizo
@vercel/oidcsigue en^3.8.9: la 4.0.5 mantiene la API (tsc y tests pasan) pero arrastra@vercel/cli-configconzodfijado a 4.1.11, así quenpm audit --omit=devsigue en 3 high. No cierra el hallazgo.peerDependenciesde next/react: el código no los importa (grep).npm install -D vitest@5da ERESOLVE en el monorepo.isModuleAllowedyrequireModulepor defecto siguen con null/[] = todo (decisión pendiente de Mariano, documentada). Solo se agregó el modo estricto opt-in.scripts/release.sh platform 0.8.0tras merge).Evidencia
Rojo (antes del fix) -> verde, por grupo (salida real):
Tests 2 failed | 4 passed (6)(cuerpo del 500 conteníaconnect ECONNREFUSED 10.0.0.5:5432 ...; AppError 502 no operacional devolvía su mensaje) -> verde.Tests 9 failed | 1 passed (10)(configureIdentity is not a function, cookie de otra app/tenant aceptada, sin WARN,isModuleAllowedStrictinexistente) -> verde.Tests 8 failed | 1 passed (9)(Bearer/token/correo en claro,TypeError: Converting circular structure, tenant pisado, fetch sin señal, ingest colgado = timeout de 10 s del test, flush concurrente no esperaba, lote perdido, sin retención) -> verde.2 failed | 1 passed(5000 caracteres reflejados) -> verde.Tests 5 failed | 3 passed (8)(4xx lanzado no llegaba awarn) -> verde.Tests 10 failed | 2 passed (12)(51 de 51 chunks leídos antes de cortar; XFF falsificable; Map sin tope; service/path libres) -> verde.../src/versioninexistente -> verde.Revisión independiente (segunda ronda): rojo -> verde por grupo: A1 2 fallidos de 2; A2/M2 8 fallidos de 18; M1/B5 2 fallidos de 9; M5 1 fallido de 9; M3/M4/B1/M6 5 fallidos de 6 (más 1 ajuste: el spread de extras también va en try/catch).
Final, igual que el CI, en el worktree:
npm ci && npm run type-check && npm test && npm run build-> type-check sin errores TS; tests: platform 214 (eran 161; +53 nuevos), config 53, contracts 57, mc-sso 51, total 395 verdes, 0 fallos;git diff --quiet -- 'packages/*/dist'limpio (dist regenerado connpm run buildy commiteado en cada grupo).Verificación NO realizada: ningún consumidor real (no se probó contra una app desplegada); la compatibilidad de las 30 apps con el cuerpo "Error interno" no está auditada aquí.
🤖 Generated with Claude Code
https://claude.ai/code/session_015nTgTv7S3gQwaoNFw5fZeG