Skip to content

security: stop gym-picker images leaking a Referer to third-party hosts (#242) - #382

Merged
hokiepokedad2 merged 2 commits into
mainfrom
fix/242-gym-image-referrerpolicy
Aug 5, 2026
Merged

security: stop gym-picker images leaking a Referer to third-party hosts (#242)#382
hokiepokedad2 merged 2 commits into
mainfrom
fix/242-gym-image-referrerpolicy

Conversation

@hokiepokedad2

Copy link
Copy Markdown
Contributor

Closes #242 — partially, and deliberately so. See "What this does not do" below.

What changed

Four <img> tags in gym-picker.component.html now set referrerpolicy="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.url from 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.
  • The team-icon fallback, 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}/photo proxy 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 ScannerService and 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.url in 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] on img as SecurityContext.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"> in index.html would 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 — passes
  • npm run prettier-check — passes
  • npx ng build --configuration production — succeeds (the bundle-budget and QuickPick template warnings are pre-existing on main)
  • npx jest --testPathPatterns "gym-picker|scanner" — 8 passed

No test added. gym-picker has 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.

…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
@github-actions github-actions Bot added the fix label Aug 5, 2026
@hokiepokedad2
hokiepokedad2 merged commit 5b0e9da into main Aug 5, 2026
8 checks passed
@hokiepokedad2
hokiepokedad2 deleted the fix/242-gym-image-referrerpolicy branch August 5, 2026 14:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

security: proxy gym photo URLs instead of exposing scanner-internal URLs to browsers

1 participant