security: stop gym-picker images leaking a Referer to third-party hosts (#242) - #382
Merged
Merged
Conversation
…ts (#242) The gym picker renders two kinds of remote image: the scanner DB's gym.url photo, and the team-icon fallback served from raw.githubusercontent.com. Neither carried a referrer policy, so every image request told the remote host which PoracleWeb instance the user was browsing. All four <img> tags now set referrerpolicy="no-referrer". Browsers already default to strict-origin-when-cross-origin, so the pre-existing leak was the origin rather than the full URL; this closes the remainder. Presentation-only. GymSearchResult.Url still carries the raw scanner URL, and no API, model, or scanner query changed. The photo-proxy endpoint suggested in #242 is deliberately not implemented: fetching a URL supplied by a database PoracleWeb does not own would convert a passive disclosure into an authenticated outbound-request primitive from a host that can reach Poracle, Koji, Golbat and both MySQL servers. Refs #242
This was referenced Aug 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #242 — partially, and deliberately so. See "What this does not do" below.
What changed
Four
<img>tags ingym-picker.component.htmlnow setreferrerpolicy="no-referrer". That's the whole code change; the rest of the diff is a CHANGELOG entry.The component renders two kinds of remote image:
gym.urlfrom the scanner DB — a Niantic CDN URL (lh3.googleusercontent.com) in a stock Golbat/RDM deployment, though an operator can rewrite the column to point at a self-hosted mirror.https://raw.githubusercontent.com/whitewillem/PogoAssets/...(gym-picker.component.ts:81).Neither carried a referrer policy, so every image request told the remote host which PoracleWeb instance the user was browsing. Both are covered now — the fallback path matters as much as the photo path and is hit more often.
Worth being precise about the size of the win: modern browsers already default to
strict-origin-when-cross-origin, so what leaked was the origin, not the full URL. This closes the remainder rather than plugging an open hole.What this does not do
The issue proposed a
GET /api/scanner/gyms/{id}/photoproxy that resolves the gym, fetches the stored URL server-side, and streams the body back. I did not build it, and I'd argue against it.That endpoint lets any authenticated user make the PoracleWeb container issue an outbound HTTP request to a URL it reads from a database it doesn't own. PoracleWeb sits on the internal network with reachability to Poracle, Koji, Golbat and both MySQL hosts. Anyone with write access to
gym.url— scanner operators, a compromised scanner, MAD/RDM webhook paths — turns that into an internal port scanner with responses streamed back to the browser. That trades a passive host-name disclosure for an active internal-request primitive, which is the worse of the two.Doing it safely needs a scheme and host allowlist plus post-DNS private-IP blocking, a response size cap, a redirect limit and a timeout. If the allowlist is being built anyway, apply it at projection in
ScannerServiceand skip the fetch entirely — that's alternative #2 in the issue and the one I'd pick if a deployment ever reports the mirror case.The premise the issue leads with is also narrower than stated:
gym.urlin Golbat/RDM holds the Niantic URL from the GMO payload, so there is no scanner hostname in it unless an operator has deliberately put one there.XSS isn't a factor here either way — Angular sanitizes
[src]onimgasSecurityContext.URL.Follow-up, not in this PR
Remote images are used well beyond the gym picker (uicons via
icon.service.ts, Discord avatars, the gym/raid/lure lists and dialogs). A single<meta name="referrer" content="no-referrer">inindex.htmlwould cover all of them in one line instead of annotating tags one by one. That's a whole-app behavior change and belongs in its own PR.Verification
npm run lint— passesnpm run prettier-check— passesnpx ng build --configuration production— succeeds (the bundle-budget and QuickPick template warnings are pre-existing onmain)npx jest --testPathPatterns "gym-picker|scanner"— 8 passedNo test added.
gym-pickerhas no existing spec, and standing up a TestBed harness to assert one static attribute isn't worth the fixture. Flagging that rather than hiding it.