Files
fuelboard/SECURITY.md
T

3.9 KiB
Raw Blame History

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

  • Source secret scan (app, widget, shared, relay, config)
  • Git history scan (all 43 commits, both repos)
  • Built binary string scan (app + widget)
  • Entitlements (app groups, keychain groups, ordering)
  • Info.plist (ATS, permissions, background modes)
  • Relay env handling + gitignore + pycache hygiene
  • Data validation layers (relay band, app decode guard, sanitizer)

Recurring checks

Run before every release:

# 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 <app-binary> | grep -iE "192\.168|100\.120|client_secret|api_key"