Skip to content

fix(web): resourceId codificado en currentPath de pre-registro + test anti-drift - #344

Merged
vgpastor merged 5 commits into
GlobalEmergency:mainfrom
nopestack:fix/342-appbar-currentpath-robustness
Jul 6, 2026
Merged

fix(web): resourceId codificado en currentPath de pre-registro + test anti-drift#344
vgpastor merged 5 commits into
GlobalEmergency:mainfrom
nopestack:fix/342-appbar-currentpath-robustness

Conversation

@nopestack

Copy link
Copy Markdown
Contributor

Closes #342

Contexto

Seguimiento de la review no bloqueante de #336 (fix #278), que añadió AppBar.currentPath para que el next del login apunte a la página exacta.

Cambios

  1. resourceId codificado en el currentPath de pre-registro. En apps/web/src/app/e/[slug]/pre-registro/page.tsx el currentPath interpolaba resourceId crudo en un query string: `/e/${slug}/pre-registro?resourceId=${resourceId}`. Un id con &, # o espacio podía dejar el redirect(next) de vuelta con una forma inesperada al reconstruir la query. Fix de una línea: encodeURIComponent(resourceId).

  2. Test anti-drift para los ~24 literales de ruta. Quedan ~24 páginas que pasan su currentPath como literal duplicando el routing file-based de src/app, sin nada que garantice que el string coincide con la carpeta real: un rename o un typo pasaría build/lint/tests y solo se notaría en runtime como un next de login incorrecto.

    Se evaluaron dos opciones:

    • Fuente única vía header de proxy (exponer el pathname en middleware para que los Server Components lo lean con headers(), sin ampliar el matcher de auth de proxy.ts). Se descartó para este follow-up: obligaría a un matcher de proxy corriendo en todas las rutas solo para fijar un header (coste en cada request), y si en cambio se reutilizara el matcher de auth ya existente, se reintroduciría exactamente el acoplamiento que fix(web): AppBar usa el pathname exacto para el next del login (#278) #336 ya había descartado (rutas no protegidas entrando al gate de auth). Es una opción válida pero de mayor alcance que el de este issue.
    • Test que verifica el invariante (la opción liviana que sugiere el propio issue): se añade apps/web/src/lib/app-bar-current-path-drift.test.ts, que recorre cada page.tsx bajo src/app, deriva la ruta real desde la carpeta del fichero ([slug]${slug}, etc.) y comprueba que cada currentPath declarado coincide. Verificado que detecta el drift: forzar un literal desincronizado (ej. registrarregistrarrr) hace fallar el test.

    Se optó por la segunda: mismo beneficio (atrapar el drift antes de runtime) con una fracción del riesgo/alcance para un follow-up pequeño.

    Nota: los tests de apps/web (pnpm --filter web test, incl. los preexistentes en src/lib/src/domain) no están cableados en el CI actual (.github/workflows/ci.yml solo corre pnpm --filter web lint/build) ni en el gate documentado en AGENTS.md. Es una brecha preexistente, no introducida por este PR — se deja fuera de alcance, pero se señala aquí por si se quiere abrir un issue aparte.

Test plan

  • pnpm --filter web test — 94/94 tests OK (92 preexistentes + 2 nuevos).
  • Mutación manual: se rompió a propósito un literal de currentPath (registrarregistrarrr) y el nuevo test lo detectó; se restauró después.
  • pnpm install --frozen-lockfile
  • pnpm --filter @reliefhub/api-client build && pnpm --filter web build — OK
  • pnpm --filter web lint — 0 errores (1 warning preexistente sin relación, en opengraph-image.tsx)
  • pnpm --filter api build — OK (sin cambios en apps/api)

nopestack added 3 commits July 6, 2026 09:54
… anti-drift de rutas

Sigue GlobalEmergency#336/GlobalEmergency#278: el currentPath de la página de pre-registro interpolaba el
resourceId sin codificar en el query string, así que un id con &, # o espacio
podía romper el next del login en el round-trip de vuelta.

Además, para el drift de los ~24 literales de ruta pasados a
AppBar.currentPath (sin nada que garantice que coinciden con la carpeta real
bajo src/app), se añade un test que recorre cada page.tsx y verifica que su
currentPath coincide con la ruta derivada del fichero. Se prefiere esta
opción ligera al refactor de exponer el pathname vía header de proxy: ese
approach exigiría un matcher de proxy que corra en TODAS las rutas solo para
fijar el header (impacto en cada request) y, si se reutilizara el matcher de
auth existente, volvería a acoplar rutas no protegidas al gate de auth — el
motivo exacto por el que GlobalEmergency#336 ya lo había descartado.
@nopestack
nopestack requested a review from vgpastor as a code owner July 6, 2026 11:13
@vercel

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

@nopestack is attempting to deploy a commit to the GlobalEmergency Team on Vercel.

A member of the Team first needs to authorize it.

nopestack added 2 commits July 6, 2026 13:39
…ceptionFilter

OAuthExceptionFilter (GlobalEmergency#340) captura cualquier fallo del callback OAuth,
incluido el UnauthorizedException por CSRF state inválido, y responde con
un 302 a /login?error=oauth_failed en vez del 401 crudo anterior. Los 6
tests que esperaban 401 quedaron rotos porque el comportamiento nuevo es
intencional (GlobalEmergency#340) y el test no se actualizó.

Se actualizan las aserciones para esperar el 302 con el Location correcto
y se verifica directamente lo que sí importa para CSRF: la cookie
rh_oauth_state se invalida (Expires en el pasado) y nunca se emite un
accessToken/sesión.

Closes GlobalEmergency#346
@nopestack

Copy link
Copy Markdown
Contributor Author

Nota: incorporé un merge de fix/346-oauth-csrf-status-code (PR #347) a esta rama. No es parte del alcance de esta PR — es la corrección de una regresión preexistente en main (ver #346) que hacía fallar el check Test e2e en cualquier PR basada en el main sincronizado, incluida esta. Lo mergeé para que el check pase ahora sin esperar a que #347 se revise por separado; #347 sigue siendo la fuente canónica del fix.

@vgpastor
vgpastor merged commit 0784654 into GlobalEmergency:main Jul 6, 2026
11 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants