Files
fuelboard/SECURITY.md

95 lines
3.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 <app-binary> | grep -iE "192\.168|100\.120|client_secret|api_key"
```