Environment
Unraid OS Version: 7.2.4
Unraid API: 4.29.2+c39b0b26
Authentication: API key, role VIEWER, granular permissions, READ_ANY only
Transport: local GraphQL endpoint
Are you using a reverse proxy? No — tested against the server directly.
No hostnames, addresses, API-key values, or registration data are included.
Pre-submission Checklist
Issue Description
An API key holding role VIEWER and only READ_ANY permissions can successfully
execute notification mutations. Every other resource correctly rejects mutations
for the same key, so this looks like a missing authorization guard on the notification
mutation resolvers rather than a problem with the permission model itself.
Key scope, as reported by the API when queried with the key under test:
query { me { roles permissions { resource actions } } }
roles: ["VIEWER"]
permissions: 17 resources, every one ["READ_ANY"]
ARRAY, DISK, DOCKER, LOGS, VARS, NETWORK, INFO, NOTIFICATIONS,
SERVICES, SHARE, VMS, OS, ONLINE, REGISTRATION, FLASH, DISPLAY, CONFIG
No CREATE_*, UPDATE_*, or DELETE_* action is granted on any resource. The
NOTIFICATIONS entry is exactly {"resource": "NOTIFICATIONS", "actions": ["READ_ANY"]}.
Steps to Reproduce
-
Create an API key with role VIEWER and granular permissions including
NOTIFICATIONS:READ_ANY. Grant no write action on any resource.
-
Confirm the key's scope contains only READ_ANY entries:
query { me { roles permissions { resource actions } } }
-
Issue a notification mutation with that key:
mutation {
createNotification(input: {
title: "scope-test"
subject: "scope-test"
description: "permission probe"
importance: INFO
}) { id }
}
HTTP 200:
{ "data": { "createNotification": { "id": "..." } } }
The notification is created and appears in the WebGUI.
-
Contrast with a correctly-guarded mutation using the same key:
mutation { array { setState(input: { desiredState: STOP }) { state } } }
{ "errors": [ { "message": "Forbidden resource",
"extensions": { "code": "FORBIDDEN",
"originalError": { "statusCode": 403 } } } ],
"data": null }
Expected Behavior
All mutations are rejected with Forbidden resource, consistent with a key that holds
only READ_ANY.
Actual Behavior
Notification mutations succeed. Mutations on every other resource tested are correctly
refused.
| Mutation |
Expected |
Actual |
createNotification |
Forbidden |
Succeeds |
archiveAll |
Forbidden |
Succeeds |
deleteArchivedNotifications |
Forbidden |
Succeeds |
array { setState } |
Forbidden |
Forbidden (correct) |
docker { start } |
Forbidden |
Forbidden (correct) |
vm { start } |
Forbidden |
Forbidden (correct) |
parityCheck { start } |
Forbidden |
Forbidden (correct) |
apiKey { create } |
Forbidden |
Forbidden (correct) |
updateSettings |
Forbidden |
Forbidden (correct) |
Results were reproduced across two separate runs.
Impact
This breaks the security guarantee of the VIEWER role. A key documented and intended
as read-only can write to the notification subsystem.
The deletion path is the more meaningful half. deleteArchivedNotifications and
archiveAll let a read-only key destroy or hide notification history, including
warnings a user has not yet seen (SMART warnings, parity errors). That reads as an
alert-suppression / audit-integrity issue rather than a cosmetic one.
This is becoming more relevant as third-party integrations standardise on VIEWER
keys for monitoring — Home Assistant bridges, MCP servers, dashboards. Users granting
a VIEWER key to an external tool reasonably expect that tool cannot mutate anything.
Suggested Fix
Apply the authorization guard already used by the array / docker / vm / settings
mutation resolvers to the notification mutation resolvers, requiring an appropriate
write action (NOTIFICATIONS:CREATE_ANY / UPDATE_ANY / DELETE_ANY) rather than
being satisfied by READ_ANY.
Additional Context
Found while configuring a read-only integration against the GraphQL API. Any test
notification created during verification was archived and deleted afterwards; no other
server state was modified.
Possibly related, same authorization subsystem: #2060.
Environment
Unraid OS Version: 7.2.4
Unraid API:
4.29.2+c39b0b26Authentication: API key, role
VIEWER, granular permissions,READ_ANYonlyTransport: local GraphQL endpoint
Are you using a reverse proxy? No — tested against the server directly.
No hostnames, addresses, API-key values, or registration data are included.
Pre-submission Checklist
Issue Description
An API key holding role
VIEWERand onlyREAD_ANYpermissions can successfullyexecute notification mutations. Every other resource correctly rejects mutations
for the same key, so this looks like a missing authorization guard on the notification
mutation resolvers rather than a problem with the permission model itself.
Key scope, as reported by the API when queried with the key under test:
No
CREATE_*,UPDATE_*, orDELETE_*action is granted on any resource. TheNOTIFICATIONSentry is exactly{"resource": "NOTIFICATIONS", "actions": ["READ_ANY"]}.Steps to Reproduce
Create an API key with role
VIEWERand granular permissions includingNOTIFICATIONS:READ_ANY. Grant no write action on any resource.Confirm the key's scope contains only
READ_ANYentries:Issue a notification mutation with that key:
HTTP 200:
{ "data": { "createNotification": { "id": "..." } } }The notification is created and appears in the WebGUI.
Contrast with a correctly-guarded mutation using the same key:
{ "errors": [ { "message": "Forbidden resource", "extensions": { "code": "FORBIDDEN", "originalError": { "statusCode": 403 } } } ], "data": null }Expected Behavior
All mutations are rejected with
Forbidden resource, consistent with a key that holdsonly
READ_ANY.Actual Behavior
Notification mutations succeed. Mutations on every other resource tested are correctly
refused.
createNotificationarchiveAlldeleteArchivedNotificationsarray { setState }docker { start }vm { start }parityCheck { start }apiKey { create }updateSettingsResults were reproduced across two separate runs.
Impact
This breaks the security guarantee of the
VIEWERrole. A key documented and intendedas read-only can write to the notification subsystem.
The deletion path is the more meaningful half.
deleteArchivedNotificationsandarchiveAlllet a read-only key destroy or hide notification history, includingwarnings a user has not yet seen (SMART warnings, parity errors). That reads as an
alert-suppression / audit-integrity issue rather than a cosmetic one.
This is becoming more relevant as third-party integrations standardise on
VIEWERkeys for monitoring — Home Assistant bridges, MCP servers, dashboards. Users granting
a
VIEWERkey to an external tool reasonably expect that tool cannot mutate anything.Suggested Fix
Apply the authorization guard already used by the array / docker / vm / settings
mutation resolvers to the notification mutation resolvers, requiring an appropriate
write action (
NOTIFICATIONS:CREATE_ANY/UPDATE_ANY/DELETE_ANY) rather thanbeing satisfied by
READ_ANY.Additional Context
Found while configuring a read-only integration against the GraphQL API. Any test
notification created during verification was archived and deleted afterwards; no other
server state was modified.
Possibly related, same authorization subsystem: #2060.