# Unauthenticated route signs with stored cloud credentials

Public route that mints cloud-signed URLs or tokens from stored IAM credentials without caller authorization

_Area: Cloud and container security. Seen in 2 disclosed reports; latest 2026-06-22. Updated 2026-11-01._

## How it works
A server route exists to hand clients a cloud-signed artifact (an S3 pre-signed PUT URL, a token) built from credentials the server stores for a datasource. The route is meant for authenticated, permissioned users, but its middleware chain only applies a soft gate such as recaptcha, not an authorization check. The assumption that breaks: the code assumes "reaching this controller means the caller is allowed." Nothing in the controller re-checks the caller, so anyone who can name the datasource (and, in one case, the workspace) gets a signature minted with the server's IAM identity. The blast radius is the IAM policy behind those credentials, not the one bucket the feature was designed for [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-35c4-rvc8-frhm.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-jj36-r9w3-3pfh.md).

## Where it shows up
- Route registrations where sibling mutating endpoints carry `authorized(...)` but the signed-URL route carries only `recaptcha` or CSRF middleware [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-35c4-rvc8-frhm.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-jj36-r9w3-3pfh.md).
- Controllers that load datasource secrets with `{ enriched: true }` and build an S3/cloud client from them [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-jj36-r9w3-3pfh.md).
- Request body fields for `bucket` and `key` that are passed straight into the signing call instead of being pinned to the configured bucket [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-35c4-rvc8-frhm.md).
- Endpoints that touch stored cloud credentials at all — signing, presigning, token minting.

## How to review
Diff the middleware chain of every route on a router against its siblings; a lone recaptcha/CSRF guard where peers have `authorized(...)` is the signal [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-35c4-rvc8-frhm.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-jj36-r9w3-3pfh.md). Then read the controller: confirm it re-derives the caller's identity and checks datasource/table permission before signing. The invariant: **a signature is only minted for a caller who has been authorized against that specific datasource, and the bucket/key are pinned server-side, not taken from the request.** Check that a negative-auth test exists — the existing test used builder auth headers only, so no anonymous case was ever exercised [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-35c4-rvc8-frhm.md).

## How finders spotted it
- Code review of route registrations noticed the asymmetry: every other mutating endpoint on `static.ts` had `authorized(...)`, the signed-URL route only had recaptcha; tracing the controller confirmed stored credentials were used and the bucket was unpinned [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-35c4-rvc8-frhm.md).
- Same habit on the second route: only `recaptcha` before `controller.getSignedUploadURL`, and the controller signs from enriched datasource secrets using body-controlled bucket/key [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-jj36-r9w3-3pfh.md).

## Variants seen
- Anonymous S3 PUT pre-signed URL minting with attacker-controlled bucket/key, widening from the configured bucket to the full IAM policy [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-35c4-rvc8-frhm.md).
- Same class where the caller only needs workspace and datasource IDs to write arbitrary objects [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-jj36-r9w3-3pfh.md).

## Impact and severity
Anonymous write into any bucket the datasource credentials reach; attackers can plant HTML/JS at known paths or overwrite trusted keys, turning storage write into stored-content attacks [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-35c4-rvc8-frhm.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-jj36-r9w3-3pfh.md). Severity is raised by how far the IAM policy reaches beyond the intended bucket [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-35c4-rvc8-frhm.md).

## Evidence
- E1: https://bugbountylibrary.com/r/blog%3Awiz-io-blog-from-phishing-to-llmjacking.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-35c4-rvc8-frhm.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-jj36-r9w3-3pfh.md
