Files
FuelBoard Contributor 2ed18d81a3 Backlog, tests, logo compression, security audit, App Store icon
- BACKLOG.md: P0-P3 roadmap incl. govuk API switch (blocked on credentials)
- APPSTORE.md: review-gate analysis (2 blockers: LAN data source, Always-location notes)
- SECURITY.md: audit - no secrets, no ATS holes, one MEDIUM (relay IP in binary, planned fix)
- FuelBoardTests/: SPM package, 28 tests on real Shared/ sources (sanitizer, price guard, RAG, sort, distance, brands)
- Logos: proper 2x/3x pairs, palette-quantized, 75% smaller (315KB -> 79KB)
- App icon: 1024px fuelpump on gradient (was missing entirely -> instant rejection)
- decodeStations(from:) exposed for testability; relay fetch reuses it
2026-08-11 23:14:28 +01:00

4.0 KiB
Raw Permalink 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. One architecture note (relay IP baked into the app) is scheduled for removal by the govuk-API switch (BACKLOG P0). Findings below are ordered by severity.

Findings

1. LAN IP baked into app binary — MEDIUM (planned fix)

  • Shared/FuelPriceProvider.swift:52baseURL = URL(string: "http://192.168.1.131:8788")!
  • Confirmed present in the built app binary (strings).
  • Why it works: ATS exempts raw numeric IP addresses (documented Apple behaviour), so no NSAllowsArbitraryLoads exception exists in the plist — the plist is clean.
  • Risk: the relay is a private LAN host; the app is unusable off the user's network, and the URL is effectively a hardcoded endpoint.
  • Fix: the govuk API switch (BACKLOG P0) replaces this with the HTTPS gov.uk endpoint via the relay. For any future public build, the app should talk to an HTTPS domain, never a raw IP.

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: only the relay IP, maps.apple.com, and group.com.apt.fuelboard strings found. No credentials.

3. Price data hardening — PASS (three layers)

  • Relay rejects out-of-band rows at the edge (_fuel_price 50500 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 (FuelBoardTestsPriceGuardTests).

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 for LAN dev, MUST CHANGE for public

  • No NSAllowsArbitraryLoads anywhere (confirmed by plist + pbxproj grep).
  • Cleartext HTTP only to the LAN relay IP (ATS-exempt). Once the relay moves behind an HTTPS domain (govuk switch), no ATS changes are needed.

6. Privacy / permissions — review for App Store

  • NSLocationWhenInUseUsageDescription + NSLocationAlwaysAndWhenInUseUsageDescription both present with clear copy; UIBackgroundModes: location justified by the alert feature (geofenced cheapest-station notifications).
  • 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"