← all branches

feat/atlas-scan

needs attentionviewing older commit
f5d3266 · incrementalpre-PRreviewed 2026-08-03 19:40 UTC0H · 3M · 8L · 3I
The branch
Purpose
Build and gate the private Batu Atlas — a siting intelligence suite with sensitive energy-infrastructure layers (Power Finder, gas pipelines, acuiferos, congestion zones, cortocircuito) served only to authenticated platform users.
Goal
Move atlas-private data from web/public (publicly accessible) to platform (auth-gated) and implement a secure, two-layer authenticated file-serving route handler.
Sub-goals
  • SG-1: Migrate atlas-private data bundle from apps/web/public/atlas to apps/platform/atlas-private (outside public/)
  • SG-2: Implement authenticated route handler at /atlas with fail-closed 401 on invalid session
  • SG-3: Fix Next.js limitation: optional catch-all [[...path]] not supported in route handlers — split into /atlas and /atlas/[...path]
  • SG-4: Add <base href=/atlas/> injection for relative fetch resolution
  • SG-5: Gate /atlas in proxy middleware (redirect-to-login) + handler (JWT re-validate)
  • SG-6: Write congestion analysis pipeline scripts (scripts/atlas/congestion/)
  • SG-7: Write pland (PowerFinder land) data pipeline scripts (scripts/atlas/pland/)
The changes (whole branch)
What
Route handler split from single [[...path]] (broken) to two explicit handlers (/atlas root + /atlas/[...path]) sharing serveAtlas() helper. Updated next.config.ts outputFileTracingIncludes to cover both route keys. Updated test file to import from the two new routes.
Why
Next.js route handlers do not support optional catch-all [[...path]] — the build succeeds but the router never matches the route, causing /atlas to fall through to the [locale] tree and bounce in a redirect loop.
Areas
apps/platform+24137apps/web+1921scripts/atlas+60000
Blast
~6300 lines added across 100+ files. Platform: 5 code files (route handlers, serve lib, test, next.config). Scripts: 60+ Python/data pipeline files (analysis-only, no production impact). Web: atlas page updated.
auth-sensitive static-file-server private-data-at-rest pre-PR
CI / GitHub checks· no PR — no check status availableCodeRabbit· no .coderabbit.yaml in repo

Findings · 14

security4

medium

CDN script (maplibre-gl) loaded without Subresource Integrity

apps/platform/atlas-private/index.html

maplibre-gl@4.7.1 from unpkg.com with no integrity= attribute. CDN compromise → script runs in authenticated session, exfiltrates private GeoJSON layers. Fix: add integrity="sha384-..." or vendor the library.

low

Middleware matcher skips proxy for .svg and .png paths under /atlas

apps/platform/src/lib/supabase/proxy.ts

Handler (layer 2) still returns 401 — no data leak. But redirect-to-login UX absent for those extensions.

low

Missing X-Content-Type-Options: nosniff header

apps/platform/src/app/atlas/_lib/serve.ts:77

One-liner fix: add 'X-Content-Type-Options': 'nosniff' to the headers object.

info

SVG in MIME allowlist can execute JS when opened directly in browser tab

apps/platform/src/app/atlas/_lib/serve.ts:30

No SVG files currently in atlas-private/ and content is source-controlled. Theoretical risk if a malicious SVG is committed.

conventions2

low

String.replace('<head>') only patches first occurrence — fragile on casing drift

apps/platform/src/app/atlas/_lib/serve.ts:73

Safe for current artifact. Consider /<head>/i for forward resilience.

low

TYPES allowlist omits .js and .css — future bundled atlas rebuilds would 404

apps/platform/src/app/atlas/_lib/serve.ts:26

Current artifact uses CDN-only JS/CSS. If rebuilt as Vite/webpack bundle, those requests will 404 silently.

tests5

medium

401 fail-closed test only exercises getRoot() — getPath() auth path untested

apps/platform/src/lib/supabase/__tests__/atlas-route-gate.test.ts:75

Add: call(['data','pf.json']) with sessionOk=false should return 401. The two routes are separate imports — a wiring accident removing the session check from [...path]/route.ts would go undetected.

medium

Cache-Control and X-Robots-Tag not verified on data file responses

apps/platform/src/lib/supabase/__tests__/atlas-route-gate.test.ts:101

Primary defence against CDN caching of authenticated content. Set in serveAtlas() unconditionally but only verified on the index.html test case.

low

No test for non-existent file within an allowed extension (catch branch)

apps/platform/src/lib/supabase/__tests__/atlas-route-gate.test.ts:101

Add: call(['data','missing.json']) with sessionOk=true should return 404.

low

Proxy gate tests don't cover /atlas/ subpaths

apps/platform/src/lib/supabase/__tests__/atlas-route-gate.test.ts:34

Both proxy tests use req('/atlas'). A regression in startsWith('/atlas/') branch would go undetected.

low

No positive test for .geojson or .csv extension types

apps/platform/src/lib/supabase/__tests__/atlas-route-gate.test.ts:101

Private atlas key layers (gas_pipelines, acuiferos, parques_industriales) are .geojson. Removing .geojson from TYPES would not be caught.

improvement3

low

path.relative(BASE, abs).startsWith('..') is more idiomatic traversal guard

apps/platform/src/app/atlas/_lib/serve.ts:57

Canonical Node static-file server pattern; makes the invariant self-evident vs BASE + path.sep.

info

Buffer round-trip in HTML injection is unnecessary

apps/platform/src/app/atlas/_lib/serve.ts:66

NextResponse accepts a string body directly — the intermediate Buffer.from is dead work.

info

Unused NextRequest import in [...path]/route.ts

apps/platform/src/app/atlas/[...path]/route.ts:2

_req is never accessed; import can be dropped or replaced with the global Request type.

History · 16 commits

  1. 91aaedfsafeincremental0H · 1M · 1L2026-08-12 17:35
  2. cb8d915needs attentionincremental2H · 5M · 3L2026-08-12 17:29
  3. 4cbbe8aneeds attentionincremental0H · 3M · 8L2026-08-12 14:01
  4. 4aa3c02needs attentionincremental2H · 3M · 3L2026-08-12 02:35
  5. f5630b2needs attentionincremental0H · 4M · 5L2026-08-12 02:17
  6. 28fde5bneeds attentionincremental1H · 2M · 2L2026-08-12 01:53
  7. 0babe51needs attentionincremental1H · 2M · 5L2026-08-12 01:23
  8. 50e8a8cneeds attentionincremental3H · 5M · 7L2026-08-11 23:58
  9. 3af4686needs attentionincremental3H · 5M · 5L2026-08-03 20:07
  10. f5d3266needs attentionincremental0H · 3M · 8L2026-08-03 19:40current
  11. 9f8b61aneeds attentionincremental0H · 2M · 3L2026-08-03 19:15
  12. ea51fa0needs attentionincremental1H · 4M · 4L2026-08-03 19:02
  13. 2b33f2fneeds attentionincremental0H · 1M · 1L2026-07-18 05:09
  14. 2f8cf79needs attentionincremental1H · 3M · 4L2026-07-18 00:59
  15. 1616332safeincremental0H · 0M · 1L2026-07-18 00:22
  16. e997fd8needs attentionfull1H · 4M · 6L2026-07-17 23:53