Nezha Monitoring: RoleMember-reachable SSRF with full response-body reflection via POST /api/v1/notification
Date: 2026-05
Affected: go/github.com/nezhahq/nezha
Attack vector: ssrf
Category: vulnerability-disclosure
Severity: High
CVE: CVE-2026-46717
CWE: CWE-863
CWE-918
CVSS: 8.5 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N)

## Summary nezha's dashboard supports two user roles: `RoleAdmin` (Role==0) and `RoleMember` (Role==1). The notification routes `POST /api/v1/notification` and `PATCH /api/v1/notification/:id` are wired through `commonHandler` rather than `adminHandler` — so a `RoleMember` user can call them. These handlers synchronously `Send()` an HTTP request to a user-controlled URL and reflect the *entire* response body (no size limit) back to the caller on any non-2xx response. Net effect: a low-privilege `RoleMember` can read intranet HTTP response bodies via the dashboard's hub. ## Affected versions Commit `50dc8e660326b9f22990898142c58b7a5312b42a` and earlier on `master`. ## Reachability chain ``` cmd/dashboard/controller/controller.go:121-122 auth.GET("/notification", listHandler(listNotification)) auth.POST("/notification", commonHandler(createNotification)) // <-- commonHandler, not adminHandler ``` For comparison, `/user` routes ARE gated by `adminHandler`: ``` auth.GET("/user", adminHandler(listUser)) auth.POST("/user", adminHandler(createUser)) auth.POST("/batch-delete/user", adminHandler(batchDeleteUser)) ``` `adminHandler` (controller.go:220-236) explicitly enforces `user.Role.IsAdmin()`. `commonHandler` (controller.go:214-218) does not. ## The vulnerable handler ```go // cmd/dashboard/controller/notification.go:46-83 func createNotification(c *gin.Context) (uint64, error) { var nf model.NotificationForm if err := c.ShouldBindJSON(&nf); err != nil


Mitigations:

Architectural layers (MAESTRO):