maniator / maniator/verticopolis
[Bug]: [P2] The web ingest route forwards platform and distribution_channel unvalidated, so desktop rows can be forged
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 5
- Forks
- 1
- PR merge metrics
- No merged PRs in 30d
Description
Found by the Blind Hunter layer reviewing analytics stage 2 (the desktop ingest route). Not introduced by that stage; it emerges from stage 1 and stage 2 together, and neither the party ruling nor either stage anticipated it.
The hole
Stage 2 stamps platform and distribution_channel server-side on /api/ingest/desktop, after the client property spread, specifically so a crafted body cannot invent a storefront or mislabel its surface. That guarantee holds on that route.
The sibling route undoes it. /api/ingest accepts an absent Origin header (originAllowed returns true when the header is missing, by design, since non-browser clients legitimately omit it), and it forwards the client's properties untouched. So:
curl -X POST https://<host>/api/ingest -d '{"event":"boot","session":"x","properties":{"platform":"desktop","distribution_channel":"steam"}}'
lands a fabricated Steam desktop session in the dataset. The desktop route's entire validation layer is bypassed by aiming one path to the left. Stage 2's own test documents the behavior: it asserts the web route forwards platform: "web", distribution_channel: "steam" unchanged.
Why it emerged
Stage 1 made EVERY web event carry platform and distribution_channel. Stage 2 made them server-authored on the desktop route only. Before stage 1 the web route carried no such dimensions, so there was nothing to forge; before stage 2 nothing claimed they were authoritative. The combination is what opens it.
Why it was not fixed in stage 2
Stage 2's scope constraint required the web route's behavior to be unchanged, and the fix is a real behavior change that needs a decision on shape. Stage 2 corrected its own comments, which had claimed the dimensions were server-authored without qualification.
Fix shape, and the wrinkle
The server cannot simply stamp platform on the web route the way it does on the desktop one: web versus twa is a client-side distinction (the TWA marker rides the launch URL's query string), and the server has no way to verify which one it is looking at. So the honest fix is validation rather than stamping:
- On the web route, accept
platformonly from{web, twa}and rewrite anything else tounknown. - Accept
distribution_channelonly from{web, twa}and rewrite anything else tounknown, since a storefront value can only be legitimate on the desktop route.
That leaves an honest residual worth stating: a forger can still claim twa when they are web, and vice versa. Both are web surfaces, so the blast radius is one low-stakes dimension rather than a fake desktop population.
Priority
P2 rather than P3 because it is a data-integrity issue in the dataset the demo decision will be made from, and because it is cheap to fix. It is not urgent while desktop builds emit nothing (the gate is still closed), so it can land with or before the client stage, but it must land before anyone trusts a desktop count.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by locating the /api/ingest route and the Stage 2 test that currently asserts the web route forwards platform and distribution_channel unchanged. Trace how those properties are accepted, then validate web values against web and twa, rewriting other values to unknown, and run the relevant ingest tests to confirm desktop values cannot be forged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend, security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100