What
`server/sshido-relay/deploy-cloud-run.sh:40` deploys Cloud Run with `--allow-unauthenticated`, and `server/sshido-relay/main.go:168-277` implements both POST endpoints with:
- No per-IP throttling
- No per-token throttling
- No `http.MaxBytesReader` on the request body
- Bare `json.NewDecoder(r.Body).Decode(&req)` (lines 174, 221)
There is also no notify-URL rotation: `UpsertByDeviceToken` returns the same subscriber id forever for the same APNs token.
Concrete attacks
- Subscriber-spam: anyone POSTs random device tokens to `/subscribe` → unbounded Firestore writes → billing exposure on a free-tier Cloud Run.
- Notify-URL pwning: any leaked Notify URL (screenshot of Settings, terminal scrollback, casual share with a coworker) lets the holder push unlimited high-priority alerts to the device. Each one is a Cloud Run invocation + Firestore write that the operator pays for. The user cannot rotate the URL without un/reinstalling to get a new APNs token.
- Memory pressure: the 256Mi Cloud Run instance can be force-fed huge JSON bodies until OOM.
Suggested fix
- Wrap every handler with `http.MaxBytesReader(w, r.Body, 41024)` (notify) / `11024` (subscribe).
- In-process token-bucket per subscriber id and per source IP, e.g. `golang.org/x/time/rate`. Even 5 rps with burst 10 kills the abuse case without affecting real users.
- Add a notify-URL rotation: `POST /rotate` (auth: includes the current device token) returns a new id and invalidates the old one. Wire it to a "Rotate notify URL" button in Settings.
- Set per-day push count limit per subscriber; over the cap, reply 429 and stop hitting APNs.
Acceptance
- Bench shows abuse paths return 429 instead of 200.
- Notify-URL rotation works end-to-end in the iOS app.
- Body size cap is unit-tested with a 1 MiB payload.
What
`server/sshido-relay/deploy-cloud-run.sh:40` deploys Cloud Run with `--allow-unauthenticated`, and `server/sshido-relay/main.go:168-277` implements both POST endpoints with:
There is also no notify-URL rotation: `UpsertByDeviceToken` returns the same subscriber id forever for the same APNs token.
Concrete attacks
Suggested fix
Acceptance