Summary
Severity: high (abuse / quota bypass; no data confidentiality impact)
The ApiKeyPresent request guard at src/main.rs:80-89 checks only that the X-Api-Key header is present — the value is never validated against any backend:
async fn from_request(req: &'r rocket::Request<'_>) -> ... {
let present = req.headers().get_one(\"X-Api-Key\").is_some();
rocket::request::Outcome::Success(ApiKeyPresent(present))
}
The comment claims "value not validated here — PKG handles that", but the value is never forwarded to PKG either (no reference to X-Api-Key outside main.rs:86). The PKG only authenticates the recipient at decryption time — it has no role at upload time.
is_api_key flips the per-upload and rolling-window quota limits 20×:
| Limit |
Without header |
With header |
PER_UPLOAD_LIMIT |
5 GB |
100 GB |
ROLLING_LIMIT (per sender, sliding window) |
5 GB |
100 GB |
(src/store.rs:11-14, applied at src/main.rs:315 and src/main.rs:464.)
Impact
Any anonymous client can defeat the per-upload and rolling-window abuse controls by sending an arbitrary header — e.g. X-Api-Key: x. The 5 GB caps that limit storage abuse on Procolix become 100 GB caps for anyone who reads the source.
This is not an authentication bypass (recipients still need PKG-issued keys to decrypt), but it does defeat the storage / bandwidth abuse controls.
Suggested fix
- Validate the
X-Api-Key value against a configured allowlist (config-driven, hashed at rest) before treating the request as a higher-quota tenant. The validated key should also identify the tenant for per-key quota accounting (today the rolling window is per sender email — a known, weaker tracker).
- If the upstream design really does treat presence as authorization (it should not), at minimum rename the type to
ApiKeyHeaderPresent and document explicitly that quota tiers are unauthenticated, so reviewers do not assume otherwise.
Where
src/main.rs:79-89 — guard
src/main.rs:125 — is_api_key: api_key.0 set on FileState
src/main.rs:315, src/main.rs:464, src/main.rs:521-525 — limit selection
src/store.rs:11-14 — limit constants
Reproduce
curl -H 'X-Api-Key: anything' -X POST .../fileupload/init -d '{...}'
The created FileState.is_api_key will be true and the per-upload check at src/main.rs:316 will admit chunks up to 100 GB.
Summary
Severity: high (abuse / quota bypass; no data confidentiality impact)
The
ApiKeyPresentrequest guard atsrc/main.rs:80-89checks only that theX-Api-Keyheader is present — the value is never validated against any backend:The comment claims "value not validated here — PKG handles that", but the value is never forwarded to PKG either (no reference to
X-Api-Keyoutsidemain.rs:86). The PKG only authenticates the recipient at decryption time — it has no role at upload time.is_api_keyflips the per-upload and rolling-window quota limits 20×:PER_UPLOAD_LIMITROLLING_LIMIT(per sender, sliding window)(
src/store.rs:11-14, applied atsrc/main.rs:315andsrc/main.rs:464.)Impact
Any anonymous client can defeat the per-upload and rolling-window abuse controls by sending an arbitrary header — e.g.
X-Api-Key: x. The 5 GB caps that limit storage abuse on Procolix become 100 GB caps for anyone who reads the source.This is not an authentication bypass (recipients still need PKG-issued keys to decrypt), but it does defeat the storage / bandwidth abuse controls.
Suggested fix
X-Api-Keyvalue against a configured allowlist (config-driven, hashed at rest) before treating the request as a higher-quota tenant. The validated key should also identify the tenant for per-key quota accounting (today the rolling window is per sender email — a known, weaker tracker).ApiKeyHeaderPresentand document explicitly that quota tiers are unauthenticated, so reviewers do not assume otherwise.Where
src/main.rs:79-89— guardsrc/main.rs:125—is_api_key: api_key.0set onFileStatesrc/main.rs:315,src/main.rs:464,src/main.rs:521-525— limit selectionsrc/store.rs:11-14— limit constantsReproduce
The created
FileState.is_api_keywill betrueand the per-upload check atsrc/main.rs:316will admit chunks up to 100 GB.