github.com/nezhahq/nezha

CVE-2026-49397

CVE-2026-49397 is a medium-severity incorrect authorization vulnerability in github.com/nezhahq/nezha (go), affecting versions >= 2.0.0, < 2.0.14. It is fixed in 2.0.14.

Key facts
CVSS score
5.3
Medium
Attack vector
Network
Issuing authority
GitHub Advisory Database
Affected package
github.com/nezhahq/nezha
Fixed in
2.0.14
Disclosed
2026

Summary

Private services (EnableShowInService: false) are enumerable via per-server endpoints, leaking name and timing data CWE: CWE-285 (Improper Authorization) via CWE-200 (Exposure of Sensitive Information to an Unauthorized Actor) and CWE-863 (Incorrect Authorization, inconsistent gating across data-reader paths) CVSS v3.1: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N → 5.3 (Medium) Summary The EnableShowInService flag on a Service is meant to gate that service's visibility from the public dashboard. The main service-listing endpoint (GET /api/v1/service → showService) correctly filters services with EnableShowInService: false via ServiceSentinel.CopyStats() (service/singleton/servicesentinel.go:421-438). However, two adjacent reader endpoints retrieve service objects through code paths that do not honor the same flag: GET /api/v1/server/:id/service (listServerServices) iterates ServiceSentinel.GetSortedList() (which returns every service regardless of visibility) and emits service ID, name, and timing data for any service monitoring the queried server. GET /api/v1/service/:id/history (getServiceHistory) calls ServiceSentinel.Get(serviceID) directly and emits the service name (and aggregated per-server stats for servers the viewer can see). Both endpoints are mounted on the optionalAuth group, so an unauthenticated visitor can enumerate hidden services as long as they can guess a public server ID (linear scan over a small numeric ID space) or a service ID (likewise). The service owner's intent, "hide this from the public" via EnableShowInService: false, is silently bypassed. Affected nezha master at HEAD 636f4a99e6c3d8d75f17fdf7ad55d4ee0f73f1c0 (the audit checkout) All recent 2.x releases that share this code path (post the EnableShowInService filter introduction at CopyStats) Vulnerability details [A], single-source-of-truth filter exists at the listing site service/singleton/servicesentinel.go:421-438: CopyStats() is the only reader that respects EnableShowInService. Get() and GetSortedList() immediately below it return the raw services with no such filter: [B], listServerServices iterates GetSortedList() and emits hidden services cmd/dashboard/controller/service.go:258-340 (GET /api/v1/server/:id/service): The DB-fallback path at queryServerServicesFromDB (service.go:340-) has the same structure: iterates services (the same GetSortedList() output) and emits ServiceName for any service monitoring serverID. [C], getServiceHistory returns the service name for any ID cmd/dashboard/controller/service.go:126-180 (GET /api/v1/service/:id/history): The per-server data inside the response IS correctly filtered via userCanViewServer. The service NAME is not. The mismatch [A] (CopyStats) gates by EnableShowInService because that's the listing endpoint's contract. [A'] (Get) / GetSortedList() return the raw data because they're "internal" accessors. But [B] and [C] are public-reachable endpoints that use those raw accessors and emit identifying information about services the owner marked as private. The visibility flag exists; it just isn't enforced at every reader of the same data. A correct guard would either: Move the EnableShowInService filter into Get() / GetSortedList() themselves, gated by "caller is admin or service owner" Re-check EnableShowInService at every endpoint that emits service identity (name/id/timing) Proof of concept Setup (any nezha 2.x deployment): User A (member) creates a Service "Internal-CRM-Health" with EnableShowInService: false, monitoring server S which is public (HideForGuest: false). The service does not appear in GET /api/v1/service (the main listing correctly hides it). Enumeration as an unauthenticated guest: Confirmation via the second endpoint: A scripted enumeration over public server IDs (a low-cardinality numeric space, typical nezha deployments have <1000 servers) trivially recovers the full set of hidden services that monitor any public server, along with their names and timing patterns. Impact Direct Service names in nezha deployments are frequently descriptive of the underlying business asset they monitor: "Production CRM Monitor", "Internal Wiki Health", "Backup-Vault Connectivity", "Stripe Webhook Latency". The leak therefore: Discloses the existence and purpose of internal services that the owner explicitly hid from the public dashboard. Exposes timing/latency data for the monitored relationship between a private service and any public server it touches, sufficient for a competitor or attacker to infer business activity patterns, outage windows, and probable backend topology. Confirms presence/absence of a service ID via the second endpoint, an oracle that lets an unauthenticated visitor enumerate the service-id namespace and learn the deployment's service count and naming convention even when no public servers exist as enumeration vectors. Indirect / second-order Affects multi-tenant public dashboards: nezha is frequently deployed as a public status page with a private "internal" tier in the same dashboard. The bypass collapses the privacy boundary between these tiers. Composability with prior advisories: the recent fixes for GHSA-rxf6-wjh4-jfj6 (cross-user trigger-task firing), GHSA-hvv7-hfrh-7gxj (WS server-stream cross-tenant leak), and GHSA-4g6j-g789-rghm (forged monitor results) all address the cross-tenant visibility model. This finding is a sibling that closes one more reader gap in the same model. Suggested fix Either of: Centralize the filter in ServiceSentinel, change Get(id) and GetSortedList() to accept the gin.Context (or a viewer context) and apply the EnableShowInService filter plus an admin-or-owner override. This guarantees every reader inherits the gate: go func (ss ServiceSentinel) GetForViewer(c gin.Context, id uint64) (model.Service, bool) { s, ok := ss.Get(id) if !ok { return nil, false } if !s.EnableShowInService && !callerIsAdminOrOwns(c, s) { return nil, false } return s, true } Recheck at every endpoint that emits service identity, add the EnableShowInService + ownership check at the top of listServerServices, getServiceHistory, and anywhere else GetSortedList()/Get() results flow to a response. More surgical but easier to miss next time. Option (1) is symmetric with how userCanViewServer centralizes the server-visibility decision; the same pattern at the service layer would close this class once.

Impact

What is incorrect authorization?

The application does not correctly enforce access controls, allowing a principal to access resources or operations beyond their granted permissions. Typical impact: unauthorized data access or execution of privileged operations.

Severity and exposure

CVE-2026-49397 has a CVSS score of 5.3 (Medium). The vector is network-reachable, no privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment.

A fixed version is available (2.0.14). Upgrading removes the vulnerable code path.

Affected versions

go

  • github.com/nezhahq/nezha (>= 2.0.0, < 2.0.14)

Security releases

  • github.com/nezhahq/nezha → 2.0.14 (go)
Kodem intelligence

Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.

Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter instead of chasing every advisory.

Kodem's runtime-powered SCA identifies whether CVE-2026-49397 is reachable in your applications. Explore open-source security for your team.

See if CVE-2026-49397 is reachable in your applications. Get a demo

Already deployed Kodem? See CVE-2026-49397 in your environment

Remediation advice

Upgrade github.com/nezhahq/nezha to 2.0.14 or later to resolve this vulnerability.

Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.

Frequently asked questions about CVE-2026-49397

What is CVE-2026-49397?

CVE-2026-49397 is a medium-severity incorrect authorization vulnerability in github.com/nezhahq/nezha (go), affecting versions >= 2.0.0, < 2.0.14. It is fixed in 2.0.14. The application does not correctly enforce access controls, allowing a principal to access resources or operations beyond their granted permissions.

How severe is CVE-2026-49397?

CVE-2026-49397 has a CVSS score of 5.3 (Medium). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.

Which versions of github.com/nezhahq/nezha are affected by CVE-2026-49397?

github.com/nezhahq/nezha (go) versions >= 2.0.0, < 2.0.14 is affected.

Is there a fix for CVE-2026-49397?

Yes. CVE-2026-49397 is fixed in 2.0.14. Upgrade to this version or later.

Is CVE-2026-49397 exploitable, and should I be worried?

Whether CVE-2026-49397 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo

What actually determines whether CVE-2026-49397 is exploitable, and how bad it is?

Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.

How do I fix CVE-2026-49397?

Upgrade github.com/nezhahq/nezha to 2.0.14 or later.

Stop the waste.
Protect your environment with Kodem.