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
This commit is contained in:
FuelBoard Contributor
2026-08-11 23:14:28 +01:00
parent 05f3ef0a70
commit 2ed18d81a3
57 changed files with 608 additions and 25 deletions
+94
View File
@@ -0,0 +1,94 @@
# 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:52``baseURL = 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 (`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 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
- [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 <app-binary> | grep -iE "192\.168|100\.120|client_secret|api_key"
```