# StudioCMS S3 Storage Manager Authorization Bypass via Missing `await` on Async Auth Check

Missing await on async isAuthorized() in StudioCMS S3 storage manager made the Promise truthy, letting any visitor-role user list, upload, rename and delete bucket files.

- Payout: Research
- Severity: high
- Program: StudioCMS
- Found by: offset
- Disclosed: 2026-03-12
- CVE: CVE-2026-32101
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-mm78-fgq8-6pgr
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-mm78-fgq8-6pgr
- Tags: area:web domain:web impact:data-leak impact:integrity kind:research platform:ghsa sev:high tech:nodejs tech:s3 technique:source-review vuln:access-control vuln:auth-bypass

**Target:** S3 storage manager API endpoints (POST/PUT /studiocms_api/integrations/storage/manager)
**Root cause:** isAuthorized() is declared async returning Promise<boolean>, but both handlers call it without await; a Promise object is always truthy so !isAuthorized(type) is always false and the 401 branch never executes.
**Steps:**
1. Log in as a visitor-role user and obtain the session cookie.
2. POST `{"action":"list","prefix":""}` to `/studiocms_api/integrations/storage/manager` with `Cookie: studiocms-session=<visitor-session-token>`; expect 401 but get 200 with full bucket listing.
3. PUT to the same endpoint with `x-storage-key: malicious/payload.html` and binary body; expect 401 but file uploads.
4. POST `{"action":"delete","key":"important/document.pdf"}`; expect 401 but file is deleted.
**How found:** Code review of the storage manager handlers noticed isAuthorized() called without await despite being typed Promise<boolean> in definitions.ts, making the negation always false.
**Exploitation:** Any authenticated visitor-role user sends requests to the storage manager endpoints; the auth check evaluates a truthy Promise, so the 401 is skipped and the requested list/upload/delete/rename operation executes against the S3 bucket.
**Escalation:** Visitor can list all files and generate presigned download URLs, delete arbitrary objects, and upload or rename files to replace legitimate content with malicious payloads.
**Insight:** When an auth helper is typed Promise<boolean>, grep every call site for missing await — a truthy Promise silently disables the check; the type signature in definitions.ts is the tell.

## Proof of concept (copied exactly from the original report)

**curl PoC listing S3 bucket as visitor**

```
curl -X POST 'http://localhost:4321/studiocms_api/integrations/storage/manager' \
  -H 'Cookie: studiocms-session=<visitor-session-token>' \
  -H 'Content-Type: application/json' \
  -d '{"action":"list","prefix":""}'
```

**curl PoC uploading file as visitor**

```
curl -X PUT 'http://localhost:4321/studiocms_api/integrations/storage/manager' \
  -H 'Cookie: studiocms-session=<visitor-session-token>' \
  -H 'Content-Type: application/octet-stream' \
  -H 'x-storage-key: malicious/payload.html' \
  --data-binary '<h1>Uploaded by visitor</h1>'
```

