polaris-maps
Decentralized P2P Maps + Navigation
rad:zHUKM29kn6E97oJ6FwTSYkcPxTiZ
Visibility
public
Delegates
did:key:z6MkkucZJnztmvGn2GawftUuXjCE57hQJkWAKB3h1g7D8LBY
Default branch
main → 8a25db5d1e19fe7228598dfa85ff3978ff4523b7 (Mon Apr 13 09:48:25 2026)
Threshold
1
README.md
# Polaris Maps
A decentralized, peer-to-peer mapping application built with React Native / Expo. Polaris Maps combines real-time traffic data, offline vector maps, point-of-interest contribution, and a DePIN (Decentralized Physical Infrastructure Network) incentive layer.
---
## Table of Contents
- [Features](#features)
- [Architecture](#architecture)
- [Tech Stack](#tech-stack)
- [Getting Started](#getting-started)
- [Environment Variables](#environment-variables)
- [Project Structure](#project-structure)
- [Testing](#testing)
- [π Security Review](#-security-review)
---
## Features
| Feature | Status |
| ----------------------------------------------- | ------ |
| P2P map tile sharing via Hypercore / Hyperdrive | β
|
| Real-time traffic overlay (TomTom + HERE) | β
|
| Dynamic ETA calculation with traffic adjustment | β
|
| Offline region tile packs | β
|
| Point-of-interest browsing, editing & review | β
|
| Street-level imagery capture & viewer | β
|
| Decentralized identity (secp256k1 keypair) | β
|
| Dark mode with custom Apple Mapsβinspired style | β
|
| Navigation mode with heading-up camera | β
|
| OSM-backed local geocoding + Nominatim fallback | β
|
---
## Architecture
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Expo Router (app/) React Native UI β
β ββββββββββββ ββββββββββββ βββββββββββββββββββ β
β β (tabs) β β poi/ β β regions/ β β
β ββββββββββββ ββββββββββββ βββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β State Layer (Zustand stores) β
β mapStore Β· trafficStore Β· osmPoiStore β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Service Layer (src/services/) β
β traffic/ Β· routing/ Β· geocoding/ Β· poi/ Β· sync/ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Native / P2P Bridge β
β nodejs-assets/ β Hyperswarm Β· Hyperdrive Β· Gun.js β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β External APIs β
β TomTom HERE Valhalla Overpass Nominatim β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
---
## Tech Stack
| Layer | Technology |
| ---------- | ---------------------------------------------------------- |
| Framework | React Native 0.76.9 + Expo SDK 52 (bare workflow) |
| Language | TypeScript 5.6.3 (strict) |
| Navigation | Expo Router 4 + React Navigation 7 |
| Maps | MapLibre React Native 10 (alpha) + OpenFreeMap tiles |
| State | Zustand 5 |
| Storage | expo-sqlite (FTS5) Β· react-native-mmkv Β· expo-secure-store |
| Identity | @noble/curves secp256k1 Β· @noble/hashes SHA-256 |
| P2P | Hyperswarm Β· Hyperdrive Β· Hypercore Β· GunDB |
| Traffic | TomTom Traffic Flow v4 Β· HERE Traffic Flow v7 |
| Routing | Valhalla (online) |
| Geocoding | expo-sqlite FTS5 local Β· Nominatim fallback |
| Testing | Jest 29 Β· @testing-library/react-native 12 |
| Linting | ESLint 9 flat config Β· Prettier |
| Commits | Commitlint + Husky |
---
## Getting Started
### Prerequisites
- Node.js β₯ 20
- pnpm β₯ 9
- Xcode 16 (iOS) or Android Studio (Android)
- CocoaPods (iOS: `sudo gem install cocoapods`)
### Install
```bash
git clone https://github.com/your-org/polaris-maps.git
cd polaris-maps
pnpm install
# iOS only
cd ios && pod install && cd ..
```
### Run
```bash
# iOS Simulator
pnpm ios
# Android Emulator
pnpm android
# Metro bundler only
pnpm start
```
---
## Environment Variables
Create a `.env` file in the project root (never commit it):
```env
EXPO_PUBLIC_TOMTOM_API_KEY=your_tomtom_key
EXPO_PUBLIC_HERE_API_KEY=your_here_key
EXPO_PUBLIC_TOMTOM_PROXY_URL=https://your-proxy-server/tomtom # optional β preferred over direct key
```
---
## Project Structure
```
src/
components/map/ MapView, TrafficOverlay, TrafficRouteLayer, POILayer
constants/ config.ts, theme.ts, darkMapStyle.ts
contexts/ ThemeContext
hooks/ useLocation, useNavigation, useTraffic
models/ traffic.ts, poi.ts, user.ts
services/
geocoding/ FTS5 local search + Nominatim
identity/ keypair generation (SecureStore)
poi/ OSM Overpass fetcher
routing/ Valhalla routing
storage/ MMKV singleton, SQLite helpers
traffic/ tomtomFetcher, hereFetcher, trafficMerger, routeTrafficService
stores/ mapStore, trafficStore, osmPoiStore
types/ modules.d.ts (ambient declarations)
utils/ etaCalculator, polyline, geohash
app/
(tabs)/ index, navigation, search, profile
imagery/ capture, viewer
onboarding/
poi/ [id], edit, reviews
regions/ index, offline
settings/
nodejs-assets/nodejs-project/ Hyperswarm / Hyperdrive P2P bridge (Node.js side-channel)
specs/ Feature specs, plans & research docs
```
---
## Testing
```bash
pnpm test # jest with --passWithNoTests
pnpm typecheck # tsc --noEmit
pnpm lint # eslint
```
---
## π Security Review
**Date:** 2026-04-13
**Standard:** OWASP Top 10:2025
**Scope:** All source files under `src/`, `app/`, `nodejs-assets/`, `package.json`, `index.js` (34 files)
**Reviewer:** GitHub Copilot automated audit
**Tests:** 50 regression tests in `__tests__/unit/security.test.ts`
> This section is kept continuously up-to-date. Every change to the codebase triggers a re-evaluation of all applicable findings.
### Summary
| ID | Severity | Status | OWASP | Location | Title |
| ------------------- | --------- | ----------- | --------- | ------------------------------------------------------- | ----------------------------------------------------------------- |
| [F-001](#f-001) | π΄ High | β
Fixed | A08 / A05 | `nodejs-assets/nodejs-project/index.js:310` | Path traversal in Hyperdrive download β no escape guard |
| [F-002](#f-002) | π΄ High | β οΈ Accepted | A04 | `src/constants/config.ts:32,35` | TomTom + HERE API keys bundled into client via `EXPO_PUBLIC_` |
| [F-003](#f-003) | π‘ Medium | β
Fixed | A05 | `app/poi/[id].tsx:110,119` | Unvalidated external URL opened via `Linking.openURL` |
| [F-004](#f-004) | π‘ Medium | β
Fixed | A05 | `src/services/geocoding/geocodingService.ts:23` | FTS5 query injection via `"` in search input β crashes search |
| [F-005](#f-005) | π‘ Medium | β
Fixed | A03 | `package.json`, `nodejs-assets/package.json` | All deps use `^`/`~` ranges β unpinned supply chain |
| [F-006](#f-006) | π‘ Medium | β
Fixed | A06 | `src/services/overpassClient.ts`, `geocodingService.ts` | Insufficient rate limiting on Overpass / Nominatim |
| [F-007](#f-007) | π΅ Low | β
Fixed | A04 | `src/services/storage/mmkv.ts:3` | MMKV storage created without encryption |
| [F-008](#f-008) | π΅ Low | β
Fixed | A09 | `src/services/traffic/trafficFlowService.ts:43` | Full `Error` object logged β may leak API key in stack trace |
| [F-009](#f-009) | π΅ Low | β
Fixed | A10 | `src/services/traffic/hereFetcher.ts:84` | `fetchHERETraffic` lacks `try/catch` β loading spinner gets stuck |
| [F-010](#f-010) | π΅ Low | β
Fixed | A10 | `src/services/geocoding/geocodingService.ts:18` | FTS5 exception propagates unhandled to UI |
| [F-011](#f-011) | π΅ Low | β
Fixed | A09 | `src/services/routing/routingService.ts:100` | Raw Valhalla error response body included in thrown `Error` |
| [F-012](#f-012) | β
Clear | β | A01 | β | No server endpoints β access control not applicable |
| [F-013](#f-013) | β
Clear | β | A07 | `src/services/identity/keypair.ts` | Private key in SecureStore; strong curve; no weak auth flows |
| [F-014](#f-014) | N/A | β | A01 | `app/poi/[id].tsx` | IDOR not applicable β all POI reads are local SQLite |
| [NEW-001](#new-001) | π΄ High | β
Fixed | A08 | `src/services/traffic/nostrFallback.ts:242` | Nostr event signatures not verified β fake traffic injection |
| [NEW-002](#new-002) | π‘ Medium | β οΈ Noted | A04 | `src/services/gun/init.ts`, `offlineQueue.ts` | Deprecated unencrypted MMKV still used for P2P + offline queue |
| [NEW-003](#new-003) | π‘ Medium | β
Fixed | A05 | `src/services/poi/osmFetcher.ts:211` | Overpass QL injection via unescaped `"` in `fetchOsmPoisByName` |
| [NEW-004](#new-004) | π‘ Medium | β
Fixed | A01 | `nodejs-assets/nodejs-project/index.js:39` | No path validation on `gunzip` command handler |
| [NEW-005](#new-005) | π’ Low | β
Fixed | A04 | `src/services/identity/signing.ts:17` | `createSigningPayload` lacks domain separation |
| [NEW-006](#new-006) | π’ Low | β
Fixed | A05 | `src/services/poi/osmFetcher.ts:144` | Overpass QL tag interpolation in `fetchOsmPoisByTags` |
---
### Detailed Findings
#### F-001
**Severity:** π΄ High β β
Fixed (2026-04-13)
**OWASP:** A08 β Integrity Failures / A05 β Injection
**Title:** Path Traversal in Hyperdrive Download β No Escape Guard
**File:** `nodejs-assets/nodejs-project/index.js` lines 310β328
**What was wrong:**
`handleHdDownload()` iterated over every entry in a Hyperdrive served by an untrusted P2P peer and wrote each file using `path.join(destDir, entry.key)` with no path-traversal check. An adversarial peer could write files outside the intended directory.
**Fix applied:**
Added `path.resolve()` + `startsWith(resolvedDest + path.sep)` guard, matching the existing tar-extraction handler. Entries that resolve outside `destDir` are silently skipped.
**Regression test:** `__tests__/unit/security.test.ts` β "Hyperdrive download path traversal guard (F-001)"
---
#### F-002
**Severity:** π΄ High β β οΈ Accepted Risk
**OWASP:** A04 β Cryptographic Failures
**Title:** TomTom + HERE API Keys Bundled into Client Bundle via `EXPO_PUBLIC_`
**File:** `src/constants/config.ts` lines 32, 35
**Description:**
`tomtomApiKey` and `hereApiKey` are sourced via `process.env.EXPO_PUBLIC_TOMTOM_API_KEY` and `process.env.EXPO_PUBLIC_HERE_API_KEY`. Expo's `EXPO_PUBLIC_` convention statically inlines environment variables into the JavaScript bundle at build time.
**Mitigation:**
- Restrict TomTom/HERE keys in their dashboards to the app's bundle ID and platform signature.
- API key redaction has been added to all error logging paths (F-008, F-011) to prevent accidental leak via crash reporters.
- A backend proxy is the recommended long-term fix but is not currently in scope.
---
#### F-003
**Severity:** π‘ Medium β β
Fixed (2026-04-13)
**OWASP:** A05 β Injection
**Title:** Unvalidated External URL Opened via `Linking.openURL` in POI Detail Screen
**File:** `app/poi/[id].tsx`
**What was wrong:**
`Linking.openURL` was called on raw POI data (phone, website, social URLs) without URL scheme validation. Malicious P2P/OSM data could inject `intent://` or `javascript:` schemes.
**Fix applied:**
Added `safeOpenURL()` helper that validates URL scheme against `['https:', 'http:']` allowlist, and `safePhone()` that strips all characters except `0-9+#*`. All `Linking.openURL` calls in the file now go through these helpers.
**Regression test:** `__tests__/unit/security.test.ts` β "URL scheme validation (F-003)"
---
#### F-004
**Severity:** π‘ Medium β β
Fixed (2026-04-13)
**OWASP:** A05 β Injection
**Title:** FTS5 Query Injection via Unescaped `"` in Search Input β Crashes Search
**File:** `src/services/geocoding/geocodingService.ts`
**What was wrong:**
FTS5 query builder wrapped tokens in double-quotes without escaping, so a literal `"` in user input created malformed FTS5 syntax, crashing SQLite.
**Fix applied:**
Double-quotes are now stripped from each token before wrapping: `w.replace(/"/g, '')`. Additionally, `searchAddressLocal` is wrapped with `.catch(() => [])` so any remaining SQLite errors gracefully fall through to Nominatim.
**Regression test:** `__tests__/unit/security.test.ts` β "FTS5 query injection prevention (F-004)"
---
#### F-005
**Severity:** π‘ Medium β β
Fixed (2026-04-13)
**OWASP:** A03 β Supply Chain Failures
**Title:** All Dependencies Use Unpinned `^`/`~` Version Ranges
**File:** `package.json`, `nodejs-assets/nodejs-project/package.json`
**Fix applied:**
- All 42 production dependencies in `package.json` pinned to exact versions.
- All 4 dependencies in `nodejs-assets/nodejs-project/package.json` pinned to exact versions.
- Committed `pnpm-lock.yaml` for `nodejs-assets/nodejs-project/`.
- devDependencies retain `^`/`~` ranges, mitigated by lockfile and `--frozen-lockfile` in CI.
---
#### F-006
**Severity:** π‘ Medium β β
Fixed (2026-04-13)
**OWASP:** A06 β Insecure Design
**Title:** Insufficient Rate Limiting on Overpass API and Nominatim
**File:** `src/services/overpassClient.ts`, `src/services/geocoding/geocodingService.ts`
**Fix applied:**
- Added 1 000 ms minimum inter-request gap to `overpassFetch()` in `overpassClient.ts`.
- Added 1 000 ms minimum inter-request gap to `searchAddressNominatim()` in `geocodingService.ts`.
- Both enforcers use timestamp tracking with `await` backoff, independent of UI debounce timers.
---
#### F-007
**Severity:** π΅ Low β β
Fixed (2026-04-13)
**OWASP:** A04 β Cryptographic Failures
**Title:** MMKV Storage Instance Created Without Encryption
**File:** `src/services/storage/mmkv.ts`
**Fix applied:**
Added `getStorage()` async initializer that generates a random encryption key on first launch (via `expo-crypto`) and stores it in `expo-secure-store` (iOS Keychain / Android Keystore). MMKV is now created with `encryptionKey`. The synchronous `storage` export is retained as deprecated for backward compatibility during migration.
---
#### F-008
**Severity:** π΅ Low β β
Fixed (2026-04-13)
**OWASP:** A09 β Logging Failures
**Title:** Full `Error` Object Logged β May Expose API Key in Stack Trace
**File:** `src/services/traffic/trafficFlowService.ts`
**Fix applied:**
Error messages are now redacted with `error.message.replace(/key=[^&]*/g, 'key=REDACTED')` before logging. The raw `Error` object is no longer passed to `console.warn`.
**Regression test:** `__tests__/unit/security.test.ts` β "API key redaction in error logs (F-008, F-011)"
---
#### F-009
**Severity:** π΅ Low β β
Fixed (2026-04-13)
**OWASP:** A10 β Exceptional Conditions
**Title:** `fetchHERETraffic` Lacks `try/catch` β Loading State Gets Stuck
**File:** `src/services/traffic/hereFetcher.ts`
**Fix applied:**
Wrapped the entire fetch + JSON parse block in `try/catch`, returning `[]` on any exception.
---
#### F-010
**Severity:** π΅ Low β β
Fixed (2026-04-13)
**OWASP:** A10 β Exceptional Conditions
**Title:** FTS5 Exception Propagates Unhandled to UI
**File:** `src/services/geocoding/geocodingService.ts`
**Fix applied:**
`searchAddressLocal()` call is now wrapped with `.catch(() => [])`, gracefully falling through to Nominatim on any SQLite error. See F-004 fix.
---
#### F-011
**Severity:** π΅ Low β β
Fixed (2026-04-13)
**OWASP:** A09 β Logging Failures
**Title:** Raw Valhalla Error Response Body Included in Thrown `Error`
**File:** `src/services/routing/routingService.ts`
**Fix applied:**
Response body is now truncated to 200 characters and redacted with `replace(/key=[^&]*/g, 'key=REDACTED')` before being included in the thrown error message.
**Regression test:** `__tests__/unit/security.test.ts` β "API key redaction in error logs (F-008, F-011)"
---
#### F-012
**Severity:** β
Clear
**OWASP:** A01 β Broken Access Control
No server-side endpoints are defined in this codebase β it is a fully client-side mobile application. All data access is either local (SQLite, MMKV) or directed at third-party public APIs (TomTom, HERE, Valhalla, Nominatim, Overpass). The P2P Hyperdrive data sharing uses cryptographic Hypercore keys as access tokens. Access control at the server level is not applicable.
---
#### F-013
**Severity:** β
Clear
**OWASP:** A07 β Authentication Failures
**File:** `src/services/identity/keypair.ts`
User identity is based on a `secp256k1` keypair generated via `@noble/curves` (a well-audited, constant-time implementation). The private key is generated with `schnorr.utils.randomPrivateKey()` (CSPRNG-backed) and stored **exclusively** in `expo-secure-store`, which maps to the iOS Keychain and Android Keystore. It is never written to MMKV, AsyncStorage, or any less-secure storage. No password-based authentication is implemented. This is appropriate for a decentralized app with no central auth server.
---
#### F-014
**Severity:** N/A
**OWASP:** A01 β Broken Access Control (IDOR)
**File:** `app/poi/[id].tsx`
`app/poi/[id].tsx` reads a POI by ID from the local POI store (`getPlaceById`). The `id` path parameter comes from Expo Router's `useLocalSearchParams`. Since this is a local SQLite read on-device with no remote endpoint, there is no IDOR surface β the user can only read their own device's data.
---
#### NEW-001
**Severity:** π΄ High β β
Fixed (2026-04-13)
**OWASP:** A08 β Software and Data Integrity Failures
**Title:** Nostr Event Signatures Not Verified β Fake Traffic Data Injection
**File:** `src/services/traffic/nostrFallback.ts` β `processIncomingEvent()`
**What was wrong:**
Incoming Nostr events were parsed and dispatched to traffic probe callbacks without verifying the NIP-01 Schnorr signature (`event.sig` over `event.id`), nor verifying that `event.id` matches `sha256([0, pubkey, created_at, kind, tags, content])`. A malicious relay (or MITM) could forge events with arbitrary pubkeys and inject fake traffic congestion data, causing incorrect ETAs and route recommendations.
**Fix applied:**
Added `verifyEvent()` function that: (1) recomputes `event.id` from `sha256(JSON.stringify([0, pubkey, created_at, kind, tags, content]))` and compares against the claimed ID, (2) verifies the Schnorr signature via `schnorr.verify()`. Events failing either check are silently dropped. The `schnorr` and `sha256` imports were already present in the file.
**Regression test:** `__tests__/unit/security.test.ts` β "Nostr event signature verification (NEW-001)"
---
#### NEW-002
**Severity:** π‘ Medium β β οΈ Noted (migration required)
**OWASP:** A04 β Cryptographic Failures
**Title:** Deprecated Unencrypted MMKV Export Still Used for P2P Data and Offline Queue
**File:** `src/services/gun/init.ts`, `src/services/sync/offlineQueue.ts`
**Description:**
Both files import the deprecated **unencrypted** `storage` export from `mmkv.ts`. Gun.js relay cache, all P2P data (place edits, reviews, attestations), and queued traffic probes are stored in plaintext on device. On a rooted/jailbroken device, this data is trivially extractable.
There are 12 total callers of the deprecated `storage` export across the codebase. Migrating to `getStorageSync()` requires ensuring `getStorage()` is called at app startup before any synchronous access β a cross-cutting change that needs careful sequencing.
**Recommended fix:** Add `getStorage()` call in the root `_layout.tsx` before any service initialization, then migrate all 12 callers from `storage` to `getStorageSync()`.
---
#### NEW-003
**Severity:** π‘ Medium β β
Fixed (2026-04-13)
**OWASP:** A05 β Injection
**Title:** Overpass QL Injection via Unescaped `"` in `fetchOsmPoisByName`
**File:** `src/services/poi/osmFetcher.ts` β `fetchOsmPoisByName()`
**What was wrong:**
The regex escape function escaped regex metacharacters but **not** double-quote (`"`). User search input containing `"` would break out of the Overpass QL string literal: `"name"~"user"input"`, causing syntax errors or altered query semantics against public Overpass instances.
**Fix applied:**
Added `"` to the escape regex character class: `.replace(/[.*+?^${}()|[\]\\"]/g, '\\$&')`.
**Regression test:** `__tests__/unit/security.test.ts` β "Overpass QL double-quote injection prevention (NEW-003)"
---
#### NEW-004
**Severity:** π‘ Medium β β
Fixed (2026-04-13)
**OWASP:** A01 β Broken Access Control
**Title:** No Path Validation on `gunzip` Command Handler
**File:** `nodejs-assets/nodejs-project/index.js` β `case 'gunzip'`
**What was wrong:**
The `gunzip` handler accepted `inputPath` and `outputPath` from the React Native bridge with no validation that paths stay within expected directories. While the bridge is internal, a compromised or buggy RN module could read from or write to arbitrary filesystem locations. Both `extract-tar` and `hd-download` already had path traversal guards.
**Fix applied:**
Added `path.resolve(outputPath)` + `startsWith(homeDir + path.sep)` guard, consistent with the other handlers. Requests with `outputPath` outside the app's home directory are rejected with an error response.
**Regression test:** `__tests__/unit/security.test.ts` β "Gunzip output path validation (NEW-004)"
---
#### NEW-005
**Severity:** π’ Low β β
Fixed (2026-04-13)
**OWASP:** A04 β Cryptographic Failures
**Title:** `createSigningPayload` Lacks Domain Separation
**File:** `src/services/identity/signing.ts`
**What was wrong:**
`createSigningPayload(...fields)` joined fields with an empty string: `fields.map(String).join('')`. This meant `("ab","c")` and `("a","bc")` would produce the identical payload `"abc"`, creating an ambiguity that could permit cross-context signature reuse if an attacker controls adjacent field values.
**Fix applied:**
Changed the join delimiter to null byte (`\0`): `fields.map(String).join('\0')`. Null bytes cannot appear in any of the current field values (UUIDs, timestamps, field names).
**Regression test:** `__tests__/unit/security.test.ts` β "Signing payload domain separation (NEW-005)"
---
#### NEW-006
**Severity:** π’ Low β β
Fixed (2026-04-13)
**OWASP:** A05 β Injection
**Title:** Overpass QL Tag Interpolation in `fetchOsmPoisByTags`
**File:** `src/services/poi/osmFetcher.ts` β `fetchOsmPoisByTags()`
**What was wrong:**
`extraFilters` key/value pairs were interpolated directly into Overpass QL queries: `` `["${k}"="${v}"]` ``. Currently safe because inputs originate from internal category mappings, but a future code path providing user-controlled values would introduce injection.
**Fix applied:**
Added `.replace(/"/g, '')` to both `k` and `v` in the `extraFilters` map, stripping any double-quotes as defense-in-depth.
---
### Priority Action Plan
| Priority | Finding | Status |
| ----------------------- | ------------------------------------------------------- | ------------ |
| π΄ P1 β Fix immediately | F-001: Path traversal in Hyperdrive download | β
Fixed |
| π΄ P1 β Fix immediately | F-003: Unvalidated `Linking.openURL` in `[id].tsx` | β
Fixed |
| π΄ P1 β Fix immediately | NEW-001: Nostr event signatures not verified | β
Fixed |
| π΄ P2 β Fix this sprint | F-004: FTS5 `"` injection crashes search | β
Fixed |
| π΄ P2 β Fix this sprint | F-002: API keys in client bundle | β οΈ Accepted |
| π‘ P3 β Fix next sprint | F-005: Unpinned dependencies | β
Fixed |
| π‘ P3 β Fix next sprint | F-007: Unencrypted MMKV | β
Fixed |
| π‘ P3 β Fix next sprint | NEW-002: Deprecated unencrypted MMKV callers (12 files) | β οΈ Migration |
| π‘ P3 β Fix next sprint | NEW-003: Overpass QL `"` injection | β
Fixed |
| π‘ P3 β Fix next sprint | NEW-004: Gunzip path validation | β
Fixed |
| π’ P4 β Ongoing hygiene | F-006, F-008, F-009, F-010, F-011, NEW-005, NEW-006 | β
All Fixed |
---
_Security review last updated: 2026-04-13. Re-run audit after each significant code change._