# FuelBoard Security Audit Audit date: 2026-08-11 · Scope: app, widget, relay, build pipeline, git history. ## Summary No critical issues. No secrets in source, git history, or binaries. The prior LAN relay/private-IP app path has been removed from the app binary; production data access is public HTTPS GitHub mirror plus bundled fallback. Findings below are ordered by severity. ## Findings ### 1. LAN IP baked into app binary — RESOLVED - The app no longer contains `RelayFuelProvider`, `fuelboard.relayFallback`, `NSLocalNetworkUsageDescription`, or a private `192.168.*` live-data endpoint. - Live data uses the public HTTPS GitHub mirror; the app falls back to bundled data/cache when the mirror is unavailable. - A built-binary `strings` scan should remain part of every release gate to ensure no private LAN endpoint is reintroduced. ### 2. No secrets in source / git / binary — PASS - App + widget sources: no API keys, tokens, or passwords (only keychain API usage in `FuelStore.swift` and OAuth *documentation* comments in `FuelPriceProvider.swift`). - Relay: `CLIENT_ID`/`CLIENT_SECRET` read from environment only (`app/main.py:31-32`); `.env` is gitignored; `__pycache__`/`*.pyc` untracked; `.env.example` ships empty placeholders. - Binary scan should show no private LAN data endpoint; expected network strings include public GitHub raw URLs, `maps.apple.com`, and `group.com.apt.fuelboard`. No credentials. ### 3. Price data hardening — PASS (three layers) - Relay rejects out-of-band rows at the edge (`_fuel_price` 50–500 pence/litre). - App drops out-of-band grades at decode (`FuelPriceProvider.decodeStations`, previously `c1edd4d`). - Station names title-cased at decode + on cache load (`aee890e`). - Covered by unit tests (`FuelBoardTests` — `PriceGuardTests`). ### 4. Keychain / app-group hygiene — PASS - Keychain access groups: `com.apt.fuelboard.shared` listed FIRST (default access group), then `com.apt.fuelboard` — matches the proven WidgetBoard pattern for free-account keychain sharing. - Stations stored in app-group UserDefaults only (~2.9 MB exceeds keychain limits; documented in `FuelStore.swift`). Stored data is public price data — not sensitive. - `kSecAttrAccessibleAfterFirstUnlock` — widget can read after reboot without unlock. Correct for background widget refresh. ### 5. Transport security — PASS - No `NSAllowsArbitraryLoads` anywhere (confirmed by plist + pbxproj grep). - No cleartext LAN relay endpoint ships in the app binary. Live network data is fetched over HTTPS from the public GitHub mirror. ### 6. Privacy / permissions — review for App Store - `NSLocationWhenInUseUsageDescription` + `NSLocationAlwaysAndWhenInUseUsageDescription` both present with clear copy. The app deliberately does not include the `location` value in `UIBackgroundModes`; optional alerts use CoreLocation region monitoring/significant-change behaviour rather than persistent real-time background tracking. - Location is the only sensitive permission requested. No contacts, photos, camera, mic. - No third-party SDKs → no SDK privacy manifests required. ## What was checked - [x] Source secret scan (app, widget, shared, relay, config) - [x] Git history scan (all 43 commits, both repos) - [x] Built binary string scan (app + widget) - [x] Entitlements (app groups, keychain groups, ordering) - [x] Info.plist (ATS, permissions, background modes) - [x] Relay env handling + gitignore + pycache hygiene - [x] Data validation layers (relay band, app decode guard, sanitizer) ## Recurring checks Run before every release: ```bash # secrets in working tree grep -rniE "api[_-]?key|client[_-]?secret|password|token" FuelBoard/ Shared/ FuelBoardWidgets/ --include="*.swift" # secrets in git history (both repos) git grep -ilE "client_secret|api_key|password" $(git rev-list --all) | head # binary leak check (after build) strings | grep -iE "192\.168|100\.120|client_secret|api_key" ```