fix: address consolidated multi-agent audit findings #160

Merged
ellamizuki merged 1 commit from fix/audit-round-consolidated into main 2026-09-09 05:17:53 +00:00
ellamizuki commented 2026-09-09 05:11:29 +00:00 (Migrated from github.com)

Summary

Consolidated fixes from a three-agent parallel audit (frontend/i18n/SEO, server/security, infra/production) cross-reviewed by the orchestrator: 17 confirmed findings fixed, each with a pre-fix reproduction and a post-fix regression test or probe.

Findings and fixes

Frontend / SEO

  • [ALTA] Sitemap and hreflang advertised 404 EN variants for pt-only posts. The two pt-only posts exist in 6 locales but not en; build_absolute_language_alternates always emitted all 7 locales plus x-default -> EN (404). Sitemap now emits only locales with real translations. Probe: /blog/coisas-que-eu-gostaria... -> 404 while the sitemap advertised it with hreflang="en".
  • [MEDIA] Localized homepage canonicals pointed at a 308. /pt canonical was https://yuricunha.com/pt/ but the platform 308-redirects to /pt. getLocalizedPath now returns /pt (trailing slash only kept for the root).
  • [BAIXA] Root RSS feed was not sorted by date (locale feeds were). Same sort applied.
  • [BAIXA] No RSS autodiscovery. Layout metadata now declares alternates.types['application/rss+xml'] per locale.
  • [BAIXA] Duplicate WebSite JSON-LD on the homepage with diverging fields. The layout block was enriched (author/sameAs/inLanguage) and the page-level duplicate removed.

Server / Security

  • [MEDIA] Unauthenticated DB writes + telemetry growth. Public procedures (users.getPublicProfile, users.requestPasswordReset, announcements.getAnnouncements, markAnnouncementViewed, dismissAnnouncement) had no rate limit while every call inserted timing/apiUsage telemetry rows; apiUsage was also missing from the retention cron. Dedicated rate limits added; apiUsage pruned at 90 days.
  • [MEDIA] Public announcements leaked inactive content. Omitting active skipped the filter, exposing drafts with all localized fields. Non-admin callers now default to active: true.
  • [MEDIA] Newsletter could be used for inbox flooding. Shared 50/10s limiter plus a real email per request. Now a dedicated 3/1h limiter (double opt-in already limits impact).
  • [BAIXA] /api/avatar 500s and unbounded render work. ?size=abc -> 500, ?size=99999999 -> unbounded ImageResponse. Size is now parsed and clamped to 16..512, and the error body no longer reflects internal exception text.
  • [BAIXA] views.increment created rows for arbitrary slugs. Slug validated against the content collection (NOT_FOUND otherwise).

Infra / Production

  • [ALTA] Every music cover on /music silently degraded to a placeholder. The image optimizer allowed lastfm.freetls.fastly.net but Last.fm serves covers from lastfm-img.freetls.fastly.net -> 400 on all covers. Host added; probe: same URL via optimizer now 200.
  • [MEDIA] Analytics IP-hash salt drift. ANALYTICS_IP_HASH_SALT was read via raw process.env with a public fallback. Now declared in the env package; in production a missing salt disables IP hashing instead of hashing with a guessable value.
  • [MEDIA] Flag/env mismatch. flags.spotify gated the Last.fm NowPlaying card and forced dead SPOTIFY_* env vars as required. Now gated by flags.lastfm; dead vars removed.
  • [BAIXA] Umami double-slash redirect. NEXT_PUBLIC_UMAMI_URL trailing slash normalized in the env schema (was an extra 308 per pageview).
  • [BAIXA] Wakatime router resilience. AbortSignal timeout and graceful degradation, matching the lastfm/github routers.

Tests

  • Regression tests updated/added for the new contracts: announcements rate limits + active filter default, views slug validation (NOT_FOUND path), newsletter dedicated limiter, cron apiUsage retention, ip-hash env binding, sitemap/SEO alternates.
  • Local next start verification of the RSS/sitemap changes before push.

Validation

pnpm check:ci   # exit 0
pnpm test:unit  # 66 files, 283 tests passed
pnpm build      # 8/8 tasks successful

Pre-fix probes were executed against production for every finding listed above.

## Summary Consolidated fixes from a three-agent parallel audit (frontend/i18n/SEO, server/security, infra/production) cross-reviewed by the orchestrator: 17 confirmed findings fixed, each with a pre-fix reproduction and a post-fix regression test or probe. ## Findings and fixes ### Frontend / SEO - **[ALTA] Sitemap and hreflang advertised 404 EN variants for pt-only posts.** The two pt-only posts exist in 6 locales but not `en`; `build_absolute_language_alternates` always emitted all 7 locales plus `x-default` -> EN (404). Sitemap now emits only locales with real translations. Probe: `/blog/coisas-que-eu-gostaria...` -> 404 while the sitemap advertised it with `hreflang="en"`. - **[MEDIA] Localized homepage canonicals pointed at a 308.** `/pt` canonical was `https://yuricunha.com/pt/` but the platform 308-redirects to `/pt`. `getLocalizedPath` now returns `/pt` (trailing slash only kept for the root). - **[BAIXA] Root RSS feed was not sorted by date** (locale feeds were). Same sort applied. - **[BAIXA] No RSS autodiscovery.** Layout metadata now declares `alternates.types['application/rss+xml']` per locale. - **[BAIXA] Duplicate WebSite JSON-LD on the homepage** with diverging fields. The layout block was enriched (author/sameAs/inLanguage) and the page-level duplicate removed. ### Server / Security - **[MEDIA] Unauthenticated DB writes + telemetry growth.** Public procedures (`users.getPublicProfile`, `users.requestPasswordReset`, `announcements.getAnnouncements`, `markAnnouncementViewed`, `dismissAnnouncement`) had no rate limit while every call inserted timing/apiUsage telemetry rows; `apiUsage` was also missing from the retention cron. Dedicated rate limits added; `apiUsage` pruned at 90 days. - **[MEDIA] Public announcements leaked inactive content.** Omitting `active` skipped the filter, exposing drafts with all localized fields. Non-admin callers now default to `active: true`. - **[MEDIA] Newsletter could be used for inbox flooding.** Shared 50/10s limiter plus a real email per request. Now a dedicated 3/1h limiter (double opt-in already limits impact). - **[BAIXA] `/api/avatar` 500s and unbounded render work.** `?size=abc` -> 500, `?size=99999999` -> unbounded ImageResponse. Size is now parsed and clamped to 16..512, and the error body no longer reflects internal exception text. - **[BAIXA] `views.increment` created rows for arbitrary slugs.** Slug validated against the content collection (NOT_FOUND otherwise). ### Infra / Production - **[ALTA] Every music cover on `/music` silently degraded to a placeholder.** The image optimizer allowed `lastfm.freetls.fastly.net` but Last.fm serves covers from `lastfm-img.freetls.fastly.net` -> 400 on all covers. Host added; probe: same URL via optimizer now 200. - **[MEDIA] Analytics IP-hash salt drift.** `ANALYTICS_IP_HASH_SALT` was read via raw `process.env` with a public fallback. Now declared in the env package; in production a missing salt disables IP hashing instead of hashing with a guessable value. - **[MEDIA] Flag/env mismatch.** `flags.spotify` gated the Last.fm NowPlaying card and forced dead `SPOTIFY_*` env vars as required. Now gated by `flags.lastfm`; dead vars removed. - **[BAIXA] Umami double-slash redirect.** `NEXT_PUBLIC_UMAMI_URL` trailing slash normalized in the env schema (was an extra 308 per pageview). - **[BAIXA] Wakatime router resilience.** AbortSignal timeout and graceful degradation, matching the lastfm/github routers. ## Tests - Regression tests updated/added for the new contracts: announcements rate limits + active filter default, views slug validation (NOT_FOUND path), newsletter dedicated limiter, cron `apiUsage` retention, ip-hash env binding, sitemap/SEO alternates. - Local `next start` verification of the RSS/sitemap changes before push. ## Validation ``` pnpm check:ci # exit 0 pnpm test:unit # 66 files, 283 tests passed pnpm build # 8/8 tasks successful ``` Pre-fix probes were executed against production for every finding listed above.
isyuricunha (Migrated from github.com) reviewed 2026-09-09 05:11:29 +00:00
vercel[bot] commented 2026-09-09 05:11:34 +00:00 (Migrated from github.com)

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
website-docs Ready Ready Preview Sep 9, 2026 5:17am UTC
website-web Ready Ready Preview Sep 9, 2026 5:17am UTC
[vc]: #M5StnAiaTtgZQG5+a/tWcp6qhfHK8HPaJquMCvCMcEU=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJ3ZWJzaXRlLWRvY3MiLCJwcm9qZWN0SWQiOiJwcmpfMmVVMnNUTTBpZFk3MHJUYnNjQ0N0RXQ2TUZPdyIsInJvb3REaXJlY3RvcnkiOiJhcHBzL2RvY3MiLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20veXVyaS1jdW5oYXMtcHJvamVjdHMvd2Vic2l0ZS1kb2NzL0RUWmRrZjVSWDIyNlpmR3Z0cWRYQmppTXNHdnIiLCJwcmV2aWV3VXJsIjoid2Vic2l0ZS1kb2NzLWdpdC1maXgtYXVkaXQtcm91bmQtY28tNTdjMGY0LXl1cmktY3VuaGFzLXByb2plY3RzLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoid2Vic2l0ZS1kb2NzLWdpdC1maXgtYXVkaXQtcm91bmQtY28tNTdjMGY0LXl1cmktY3VuaGFzLXByb2plY3RzLnZlcmNlbC5hcHAifX0seyJuYW1lIjoid2Vic2l0ZS13ZWIiLCJwcm9qZWN0SWQiOiJwcmpfYjZyRmhBQVBISVhSNzNjZW9kRTB5dmxCdzM4MiIsInJvb3REaXJlY3RvcnkiOiJhcHBzL3dlYiIsImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS95dXJpLWN1bmhhcy1wcm9qZWN0cy93ZWJzaXRlLXdlYi9EbVZWSGhYZ2pveExFVW53eVpFUEQyVDJWY2k4IiwicHJldmlld1VybCI6IndlYnNpdGUtd2ViLWdpdC1maXgtYXVkaXQtcm91bmQtY29uLTcxMDdhZC15dXJpLWN1bmhhcy1wcm9qZWN0cy52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6IndlYnNpdGUtd2ViLWdpdC1maXgtYXVkaXQtcm91bmQtY29uLTcxMDdhZC15dXJpLWN1bmhhcy1wcm9qZWN0cy52ZXJjZWwuYXBwIn19XX0= The latest updates on your projects. Learn more about [Vercel for GitHub](https://vercel.link/github-learn-more). | Project | Deployment | Actions | Updated | | :--- | :----- | :------ | :------ | | <a href="https://vercel.com/yuri-cunhas-projects/website-docs"><sup><img src="https://vercel.com/api/www/avatar?projectId=prj_2eU2sTM0idY70rTbscCCtEt6MFOw&teamId=team_g087rqPZJa9s4DWPeZtQhxa9&s=32" width="16" height="16" align="middle" alt="" /></sup></a> [website-docs](https://vercel.com/yuri-cunhas-projects/website-docs) | ![Ready](https://vercel.com/static/status/ready.svg) [Ready](https://vercel.com/yuri-cunhas-projects/website-docs/DTZdkf5RX226ZfGvtqdXBjiMsGvr) | [Preview](https://website-docs-git-fix-audit-round-co-57c0f4-yuri-cunhas-projects.vercel.app) | <relative-time datetime="2026-09-09T05:17:25.586Z">Sep 9, 2026 5:17am UTC</relative-time> | | <a href="https://vercel.com/yuri-cunhas-projects/website-web"><sup><img src="https://vercel.com/api/www/avatar?projectId=prj_b6rFhAAPHIXR73ceodE0yvlBw382&teamId=team_g087rqPZJa9s4DWPeZtQhxa9&s=32" width="16" height="16" align="middle" alt="" /></sup></a> [website-web](https://vercel.com/yuri-cunhas-projects/website-web) | ![Ready](https://vercel.com/static/status/ready.svg) [Ready](https://vercel.com/yuri-cunhas-projects/website-web/DmVVHhXgjoxLEUnwyZEPD2T2Vci8) | [Preview](https://website-web-git-fix-audit-round-con-7107ad-yuri-cunhas-projects.vercel.app) | <relative-time datetime="2026-09-09T05:17:25.586Z">Sep 9, 2026 5:17am UTC</relative-time> |
Sign in to join this conversation.
No description provided.