Skip to content

Security: rhaffle87/simuloran

Security

docs/SECURITY.md

SIMULORAN – Security Architecture & Tradeoffs

1. Content Security Policy (CSP)

The deployment serves a strict HTTP Content Security Policy configured in vercel.json and mirrored in the Vite preview server:

Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' data: https://fonts.gstatic.com https://tiles.openfreemap.org https://*.openfreemap.org; img-src 'self' data: blob: https://tiles.openfreemap.org https://*.openfreemap.org https://tile.openstreetmap.org https://*.tile.openstreetmap.org https://basemaps.cartocdn.com https://*.basemaps.cartocdn.com; connect-src 'self' blob: https://tiles.openfreemap.org https://*.openfreemap.org https://tile.openstreetmap.org https://*.tile.openstreetmap.org https://basemaps.cartocdn.com https://*.basemaps.cartocdn.com; worker-src 'self' blob:; child-src 'self' blob:; frame-ancestors 'none'; form-action 'none'; base-uri 'self';

Documented Policy Directives & Tradeoffs

Directive Scope Rationale & Tradeoff
script-src 'self' Strict All dynamic evaluation (eval(), Function(), WASM dynamic compilation) is strictly forbidden. MapLibre GL JS functions with pure JavaScript rendering.
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com Pinned + Relaxed 'unsafe-inline' is required for MapLibre GL dynamic canvas/DOM styling (markers, popups, scalebars) and React dynamic inline style attributes. Pinned to Google Fonts for external font stylesheets.
font-src 'self' data: https://fonts.gstatic.com https://tiles.openfreemap.org https://*.openfreemap.org Pinned Pinned to Google Fonts CDN (fonts.gstatic.com) for Space Grotesk/JetBrains Mono fonts, OpenFreeMap for PBF vector glyphs, and data: for KaTeX mathematical font subsets.
connect-src / img-src Pinned Pinned to OpenFreeMap (tiles.openfreemap.org, *.openfreemap.org), OpenStreetMap (tile.openstreetmap.org, *.tile.openstreetmap.org), and Carto (basemaps.cartocdn.com, *.basemaps.cartocdn.com). Wildcards are scoped to subdomain prefixes only. api.mapbox.com is omitted.
worker-src / child-src 'self' blob: Required for MapLibre background workers (blob:) and off-thread Vite Web Workers ('self' blob:) computing 2D hyperbolic LOP grids and spatial ASF simulations.
frame-ancestors 'none' Strict Prevents clickjacking by forbidding embedding in foreign iframes.
form-action 'none' Strict Simulator has no form submissions.
base-uri 'self' Strict Prevents base tag hijacking attacks.

2. HTTP Strict Transport Security (HSTS) & Preload Notice

The site serves:

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

Warning

Preload Decision Caution: While preload is safe on standard *.vercel.app preview deployments, if attaching a custom production domain (e.g., simuloran.org), do NOT submit the domain to the HSTS preload list (hstspreload.org) without a deliberate engineering decision. Inclusion in browser preload lists is permanent and irreversible for months, and will break any internal subdomains that lack valid public HTTPS certificates.


3. Window Debug Globals & Testability Exposure Policy

To maintain a clean production namespace while supporting automated sub-pixel regression testing:

  • Gating Mechanism: Inspection hooks window.__maplibreInstance and window.__baselineGeoJson are strictly gated in src/components/map/MapView.jsx behind:
    if (typeof window !== 'undefined' && (import.meta.env.DEV || window.__SIMULORAN_E2E__ || window.__LORAN_E2E__)) {
      window.__maplibreInstance = mapInstance;
    }
  • Production Visitor Experience: In production builds (npm run build), import.meta.env.DEV is false. Normal visitors to https://simuloran.vercel.app (and incoming simuloran.com / simuloran.org) will have window.__maplibreInstance === undefined and window.__baselineGeoJson === undefined.
  • E2E Automation Hook: Automated test suites (Playwright) explicitly inject window.__SIMULORAN_E2E__ = true (or legacy window.__LORAN_E2E__ = true) via page.addInitScript() before page navigation to inspect map coordinate projection accuracy and worker layer readiness.
  • Threat Model & State Exposure: The simulator contains no user authentication, no database, no session credentials, and no PII. All data structures represent synthetic radio navigation physics (TDOA, ECD, ASF, transmitter coords). Tampering with the MapLibre instance or GeoJSON layer via console does not expose private data or present any security privilege escalation.
  • Tamper-Proofing Tradeoff: Setting window.__SIMULORAN_E2E__ = true manually in DevTools prior to reload is technically possible by any visitor. This is an accepted engineering tradeoff: the hook is gated to maintain a clean default window namespace, not as a security barrier, which is acceptable since no privileged or sensitive state exists.

There aren't any published security advisories