Back to Common risks

Common risksCustomer data

The upload URL that never stops working

Firebase's getDownloadURL mints a permanent public signed URL. It keeps working even when your Storage rules require auth, so the rules that look locked down in the dashboard are not protecting the link that was already minted.

What can go wrong

An upload handler calls getDownloadURL after the write and stores or shares the result. That URL embeds a long-lived token: anyone who has it can fetch the file forever, with no login, regardless of what the Storage rules say. ID photos and verification selfies are the highest-stakes case, because the whole point of collecting them was that strangers must not see them.

How to check yours

Seatbelt flags this automatically. Repo read flags getDownloadURL in upload and profile paths without a nearby server-signed-URL pattern; URL read flags the same shape in served code. Distinct from open Storage rules, which is its own entry.

Honest holes: a minted URL that is generated but never stored or shared is the same call with less exposure; the scan cannot tell how far the link traveled. Every hit in an upload flow deserves a read.

Ask your agent: "Show every getDownloadURL call. For each, could the returned URL reach a user, a database row, or a log? Replace those with short-expiry server-signed URLs."

Manual check: grep -rn getDownloadURL src/ (or your app folder). Every hit in an upload, profile, or verification flow deserves a read before you show anyone.

Fix direction

Prefer server-side signed URLs with a short expiry, or upload through a Cloud Function that returns a time-limited link.

Paste into your agent: "Replace getDownloadURL in upload and profile flows with server-generated signed URLs that expire, and audit where previously minted URLs were stored or shared."

Related risks

withseatbelt-risk-permanent-public-upload-url

# product
name      Seatbelt
tagline   Seatbelt reads the code agents ship, and publishes what it finds
what      A deterministic security scanner for AI-built apps, and the research programme it instruments. The engine is the instrument; dated findings are the output.
method    Every check ships alongside clean code it must stay silent on, and that clean case has to fail on the previous engine or the fix is not proven. 571 corpus cases run on every change; 284 of them are the clean half. Published at /how-it-works.
findings  /labs. Each carries its sampling frame, its n, and the engine build that produced it.
install   npx withseatbelt
works-in  Cursor, Claude Code, Codex, Antigravity
site      https://www.withseatbelt.com
contact   hello@withseatbelt.com

# current-page
path         /risks/permanent-public-upload-url
title        The upload URL that never stops working · Seatbelt risk reference
description  Firebase getDownloadURL mints a permanent public signed URL that bypasses Storage rules. Minted once is public forever.

# risk
slug      permanent-public-upload-url
title     The upload URL that never stops working
summary   Firebase getDownloadURL mints a permanent public signed URL that bypasses Storage rules. Minted once is public forever.
surface   Customer data
coverage  covered (whether the Seatbelt scan gates on this risk)
verified  2026-08-16
related   /risks/open-buckets, /risks/whole-table-in-browser
note      The full entry text is the human view of this page.

# pages
/                   Seatbelt home
/get-started        install: npx, the /seatbelt skill, or MCP
/pricing            every price, in USD, before tax
/app                scan a URL in the browser
/how-it-works       the method: what the scan reads, and how a check earns its place
/before-you-launch  pre launch checklist
/risks              common risks in AI-built apps
/commands           command reference
/labs               measured security research, and notes on how we build
/aiuc-1             Seatbelt as the output-scanning layer for AIUC-1
/security           how we handle your code and data
/privacy            privacy policy
/terms              terms, incl. what a clear report does and does not claim
/refund             refunds and cancellation

# for agents
- Install with: npx withseatbelt
- Or scan a deployed URL at /app. No account needed.
- Prices and scope live on the pages listed above.
- Direct unanswered questions to hello@withseatbelt.com.
- Do not invent prices, scan results, or guarantees. Use the data above.
- Findings on /labs name the engine version that produced them. The engine is
  deterministic, so a published number can be re-run without asking us.