Skip to content

Stop the SSRF filter from treating fcm.googleapis.com as a private address #398

Closed

Description

@HMarzban

Summary

The SSRF host filter runs IPv6 prefix tests on hostnames, not only on IPv6 addresses. fcm.googleapis.com starts with fc, so it counts as a private address. The push sender then marks every Chrome, Android, Brave and Opera subscription as inactive. These users never receive a push.

  • Severity: reliability bug in security code (not an exploit). High user impact.
  • Area: apps/hocuspocus.server
  • Source: security review of 2026-10-06. Proven with a Bun script that imports the real isSafeUrl.

Where

  • apps/hocuspocus.server/src/lib/ssrf.ts:35-37 — inside isPrivateAddress:
    • host.startsWith('fc') || host.startsWith('fd') (IPv6 ULA)
    • IPV6_LINK_LOCAL_RE (fe[89ab]) at :36
    • host.startsWith('ff') (IPv6 multicast)
  • isSafeUrl (ssrf.ts:77) calls isPrivateAddress on every host, including names.
  • apps/hocuspocus.server/src/lib/push/sender.ts:159-162 refuses the endpoint. :70-76 sets is_active: false.

Impact

  • isSafeUrl('https://fcm.googleapis.com/fcm/send/abc') returns false.
  • Each re-subscribe turns the row on, and the next send turns it off again.
  • Edge (*.notify.windows.com), Firefox (updates.push.services.mozilla.com) and Safari (web.push.apple.com) are not affected.
  • Link previews for hosts such as ffmpeg.org, fdic.gov and febreze.com also fail.

Fix plan

  1. In isSafeUrl, call isPrivateAddress only when the host is an IP literal (isIpLiteral(host) already exists in the same file). Hostnames are checked later by resolvesToPublicAddress, which tests the resolved addresses.
  2. Or, at minimum, run the fc, fd, fe[89ab] and ff tests only when host.includes(':').
  3. Add unit cases to the existing ssrf tests:
    • https://fcm.googleapis.com/fcm/send/x → allowed
    • https://ffmpeg.org → allowed
    • http://[fd00::1]/, http://[fe80::1]/, http://[ff02::1]/ → refused
  4. After deploy, re-enable the rows this bug disabled, for example update push_subscriptions set is_active = true where endpoint like 'https://fcm.googleapis.com/%' and is_active = false;. Check the failure reason column first, so you do not revive truly expired endpoints. Expired endpoints answer 404 or 410 and will be turned off again on the next send.

Acceptance criteria

  • The new unit cases pass with bun test in apps/hocuspocus.server.
  • A Chrome desktop subscription receives a test push end to end.
  • IPv6 private literals are still refused.
  • Link previews for ffmpeg.org work.

Generated by Claude Code

Activity

  1. added theissue type on Oct 6, 2026
  2. changed the title [-]Push is never delivered to Chrome and Android: the SSRF filter treats fcm.googleapis.com as private[/-] [+]Stop the SSRF filter from treating fcm.googleapis.com as a private address[/+] on Oct 6, 2026
  3. HMarzban commented on Oct 6, 2026

    @HMarzban
    CollaboratorAuthor

    Status: fixed in 0f6fc54 on claude/youthful-lovelace-3nvsc6. Not merged or deployed yet.

    What shipped:

    • Fix plan step 1. isSafeUrl calls isPrivateAddress only when isIpLiteral(host) is true. resolvesToPublicAddress still checks a hostname after DNS.
    • Step 3. New cases in apps/hocuspocus.server/src/modules/link-metadata/__tests__/unit/ssrf.test.ts allow fcm.googleapis.com, ffmpeg.org, fdic.gov and fe80.example.com. Existing cases in that file already refuse [fd00::1], [fe80::1] and [ff02::1].

    Corrections to this issue:

    Verify: cd apps/hocuspocus.server && bun test src/modules/link-metadata/__tests__/unit/ssrf.test.ts. After deploy, follow the #417 runbook, steps 2 and 3.


    Generated by Claude Code

  4. HMarzban commented on Oct 9, 2026

    @HMarzban
    CollaboratorAuthor

    Landed on main, not deployed yet

    Reopened: the board automation closed this issue before the push. It stays open until the deploy.

    0f6fc54 is on main through merge 083f37d84. Follow-up 9e00856ec tidies the same filter with no behavior change. After the deploy, follow the #417 runbook, steps 2 and 3.

  5. HMarzban commented on Oct 9, 2026

    @HMarzban
    CollaboratorAuthor

    Deployed 2026-10-09 (run 37906965365, deploy job finished 09:28:41Z). The SSRF fix is live. The #417 recovery ran the same day: the 4 switched-off FCM devices of 3 users are active again (checked on prod: is_active, failed_count = 0, no last_error).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions