← all branches

feat/atlas-scan

needs attentionviewing older commit
ea51fa0 · incrementalpre-PRreviewed 2026-08-03 19:02 UTC1H · 4M · 4L · 5I
The branch
Purpose
Gate the private atlas (Power Finder, gas pipelines, aquifers, congestion layers) behind Batu platform login — competitive intelligence data that must not be publicly accessible.
Goal
Serve atlas-private/ directory exclusively to authenticated platform users via a two-layer defense (middleware + route handler), without disrupting the marketing site's open atlas page.
Sub-goals
  • Authenticated file-serving route handler at /atlas/[[...path]] with path traversal and extension allowlist protection
  • Proxy middleware extension: /atlas special-case prevents locale prefix injection on a non-page route
  • next.config.ts: outputFileTracingIncludes to bundle atlas-private/ for serverless; locale-stripping redirect for post-login return
  • Marketing page: replace iframe embed with auth-required CTA linking to platform
The changes (whole branch)
What
New route handler, proxy middleware update, next.config changes, marketing page CTA, test coverage, plus static data files in atlas-private/ and Python build scripts in scripts/atlas/
Why
The atlas private layers (Power Finder siting data, gas pipeline routing, aquifer boundaries, short-circuit data) are commercially sensitive and should not be publicly accessible even on a preview Vercel URL
Areas
apps/platform/src/app/atlas+730apps/platform/src/lib/supabase+1200apps/platform/next.config.ts+120apps/web/src/app/(marketing)/atlas+4530apps/platform/atlas-private+69000scripts/atlas+60029
Blast
~90 code files + hundreds of data/script files. Core platform auth changes are 5 files. Data files are additive (new atlas-private/ directory).
contains-private-data-files two-layer-auth-gate no-open-pr
github-ci· No open PRcoderabbit· No .coderabbit.yaml

Findings · 14

correctness2

high

DataDownload contentUrl resolves to 404 on the marketing domain

apps/web/src/app/(marketing)/atlas/page.tsx:85

contentUrl uses SITE=batuenergy.com but the files are served from app.batuenergy.com behind auth. Every download URL is a broken link. Fix: use ATLAS_APP (already on line 12) or remove the distribution block.

medium

isAccessibleForFree: true contradicts the auth gate

apps/web/src/app/(marketing)/atlas/page.tsx:73

The atlas requires a Batu platform login; the page's own UI says so. isAccessibleForFree:true misrepresents the resource to search engines. Set to false or omit.

security2

low

SVG in TYPES allowlist enables inline script execution (persistent XSS vector)

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

image/svg+xml served at a same-origin URL allows embedded <script> execution. No SVGs exist today but the allowlist is open. Remove .svg from TYPES or force Content-Disposition: attachment.

low

Missing X-Content-Type-Options and X-Frame-Options on all responses

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

Route handlers don't inherit Next.js default security headers. Add X-Content-Type-Options: nosniff and X-Frame-Options: DENY to prevent MIME sniffing and clickjacking.

conventions2

medium

Handler comment misattributes proxy behavior

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

Says 'el proxy solo presence-checkea cookies' but the proxy also calls getClaims() for full JWT validation. Both layers are proper JWT gates. Update comment to reflect reality.

info

permanent:false redirect lacks comment explaining why permanent:true would be wrong

apps/platform/next.config.ts:27

This is a routing seam (i18n router adds locale, handler strips it), not a stable URL alias change. A 308 cached by the browser would bypass future proxy auth logic. Add a comment explaining this.

tests5

medium

Non-existent allowed-extension file not tested (404 on fs error)

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

The fs.readFile error path returns 404, but no test exercises it. Add: call(['data','nonexistent.json']) → expect 404.

medium

.geojson and .csv MIME types not asserted

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

Core private data layers use .geojson and .csv MIME types, which differ from the tested .json. A TYPES map typo would go undetected.

info

Tests read real atlas-private files — content-sensitive assertion

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

toContain('Power Finder') will break if index.html is rebuilt. Mock fs.promises.readFile or use a fixture file.

info

Proxy gate not tested for /atlas/data/... subpath

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

The realistic unauthenticated attack vector is a direct GET /atlas/data/pf.json. Add a proxy test for this path.

info

Nested subdirectory path not exercised

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

data/demand/today.json exercises the multi-level join+normalize+startsWith guard. Add one test for a nested subpath.

improvement3

low

/atlas in PROTECTED_ROUTE_PATTERNS is dead code

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

The special-case block at line 182 intercepts all /atlas* requests before isProtectedRoute() is ever evaluated. Remove the /atlas entry from the array; keep the explanation in the special-case comment.

low

Handler login redirect is locale-free — extra proxy hop

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

Handler redirects to /login (no locale); proxy then adds /es/login. Use /${DEFAULT_LOCALE}/login to match proxy convention and avoid the extra round-trip.

info

Unnecessary Uint8Array copy for file buffer

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

Buffer extends Uint8Array. new Uint8Array(buf) copies the buffer. Pass buf directly to NextResponse to avoid the copy for large GeoJSON/CSV files.

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:40
  11. 9f8b61aneeds attentionincremental0H · 2M · 3L2026-08-03 19:15
  12. ea51fa0needs attentionincremental1H · 4M · 4L2026-08-03 19:02current
  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