V2: Dragonfly Manager OAuth provider client_secret disclosure via unauthenticated GET /api/v1/oauth (CVE-2026-49254)
### Summary The Dragonfly Manager exposes `GET /api/v1/oauth` and `GET /api/v1/oauth/:id` to unauthenticated clients. The response body deserializes the entire `manager/models.Oauth` struct, which includes the `client_secret` field. Any network-reachable attacker can read the OAuth client secrets configured for `github` or `google` providers, defeating the confidentiality guarantee of those secrets and enabling subsequent abuse against the connected identity providers. ### Affected versions `github.com/dragonflyoss/dragonfly` `<= v2.4.3` (and current `main` at commit `46a8f1e`). The vulnerable wiring is present back to the introduction of OAuth GET handlers and was not addressed by GHSA-j8hf-cp34-g4j7 / CVE-2026-24124, whose remediation only added `jwt + rbac` middleware to the `/jobs` group. ### Privilege required Unauthenticated. The only precondition is that an administrator has registered at least one OAuth provider via `POST /api/v1/oauth` (a one-time setup for tenants that enable GitHub / Google sign-in). ### Vulnerable code [`manager/router/router.go:134-140`](https://github.com/dragonflyoss/dragonfly/blob/e1491bf6134fe307b09e82e11fa94b0587dcd323/manager/router/router.go#L134-L140) (v2.4.3) — the `/oauth` group registration: ```go // Oauth. oa := apiv1.Group("/oauth") oa.POST("", jwt.MiddlewareFunc(), rbac, h.CreateOauth) oa.DELETE(":id", jwt.MiddlewareFunc(), rbac, h.DestroyOauth) oa.PATCH(":id", jwt.MiddlewareFunc(), rbac, h.UpdateOauth) oa.GET(":id", h.GetOauth) oa.GET("", h.GetOauths) ``` Note the asymmetry inside the same `oa` route group: `POST`, `PATCH`, and `DELETE` explicitly attach `jwt.MiddlewareFunc(), rbac` as per-route middleware, but the two `GET` handlers omit both. Compare with the sibling group three lines below at [`manager/router/router.go:143-148`](https://github.com/dragonflyoss/dragonfly/blob/e1491bf6134fe307b09e82e11fa94b0587dcd323/manager/router/router.go#L143-L148), the `/clusters` group: ```go c := apiv1.Group("/clusters", jwt.MiddlewareFunc(), rbac) c.POST("", h.CreateCluster) c.DELETE(":id", h.DestroyCluster) c.PATCH(":id", h.UpdateCluster) c.GET(":id", h.GetCluster) c.GET("", h.GetClusters) ``` Here the middleware pair is attached once at the group level, so every verb on `/clusters` is guarded. The OAuth GETs are an unguarded sibling of the same primitive that GHSA-j8hf-cp34-g4j7 (Jan 2026) patched on the `/jobs` group. This is `sibling-method-dispatch-target` of the AP-012 sub-shape lens: same module, same router file, same anchor primitive ("group lacking JWT + RBAC"), parallel GET methods missed. The handler at [`manager/handlers/oauth.go:127-141`](https://github.com/dragonflyoss/dragonfly/blob/e1491bf6134fe307b09e82e11fa94b0587dcd323/manager/handlers/oauth.go#L127-L141) returns the model directly: ```go func (h *Handlers) GetOauth(ctx *gin.Context) { var params types.OauthParams if err := ctx.ShouldBindUri(¶ms); err != nil { ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()}) return } oauth, err := h.service.GetOauth(ctx.Request.Context(), params.ID) if err != nil { ctx.Error(err) // nolint: errcheck return } ctx.JSON(http.StatusOK, oauth) } ``` [`manager/handlers/oauth.go:155-171`](https://github.com/dragonflyoss/dragonfly/blob/e1491bf6134fe307b09e82e11fa94b0587dcd323/manager/handlers/oauth.go#L155-L171) has the parallel list handler: ```go func (h *Handlers) GetOauths(ctx *gin.Context) { var query types.GetOauthsQuery if err := ctx.ShouldBindQuery(&query); err != nil { ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()}) return } h.setPaginationDefault(&query.Page, &query.PerPage) oauth, count, err := h.service.GetOauths(ctx.Request.Context(), query) if err != nil { ctx.Error(err) // nolint: errcheck return } h.setPaginationLinkHeader(ctx, query.Page, query.PerPage, int(count)) ctx.JSON(http.StatusOK, oauth) } ``` [`manager/models/oauth.go:19-26`](https://github.com/dragonflyoss/dragonfly/blob/e1491bf6134fe307b09e82e11fa94b0587dcd323/manager/models/oauth.go#L19-L26) declares `ClientSecret` with no `json:"-"` tag, so it is serialized into every response: ```go type Oauth struct { BaseModel Name string `gorm:"column:name;type:varchar(256);index:uk_oauth2_name,unique;not null;comment:oauth2 name" json:"name"` BIO string `gorm:"column:bio;type:varchar(1024);comment:biography" json:"bio"` ClientID string `gorm:"column:client_id;type:varchar(256);index:uk_oauth2_client_id,unique;not null;comment:client id for oauth2" json:"client_id"` ClientSecret string `gorm:"column:client_secret;type:varchar(1024);not null;comment:client secret for oauth2" json:"client_secret"` RedirectURL string `gorm:"column:redirect_url;type:varchar(1024);comment:authorization callback url" json:"redirect_url"` } ``` ### How an unauthenticated request reaches the OAuth client_secret 1. `gin.Engine` routes `GET /api/v1/oauth/:id` to the `oa` group registered at [`manager/route
AI Analysis
Technical Summary
The Dragonfly Manager's API exposes GET endpoints for OAuth configurations that do not require authentication. These endpoints return the entire OAuth model, including the client_secret field, due to missing JWT and RBAC middleware on the GET methods. This allows unauthenticated attackers to retrieve OAuth client secrets for providers such as GitHub and Google. The issue is present in versions <= 2.4.3 and persists in the main branch at commit 46a8f1e. Other HTTP methods on the same route group are properly protected, but the GET methods were overlooked. The client_secret field is serialized in JSON responses because it lacks a json:"-" tag in the model definition.
Potential Impact
An unauthenticated attacker can retrieve OAuth client secrets configured in the Dragonfly Manager, compromising the confidentiality of these secrets. This exposure can enable attackers to abuse the connected identity providers (GitHub, Google) by impersonating the application or performing unauthorized OAuth flows. The confidentiality breach affects all tenants who have registered OAuth providers. No privilege is required to exploit this vulnerability other than the existence of at least one configured OAuth provider.
Mitigation Recommendations
Patch status is not yet confirmed — check the vendor advisory for current remediation guidance. Until a fix is available, restrict network access to the Dragonfly Manager API to trusted users only. Avoid exposing the /api/v1/oauth GET endpoints publicly. Monitor vendor communications for updates or patches addressing this issue. Do not rely on the existing JWT and RBAC protections as they do not cover the GET methods for OAuth endpoints.
V2: Dragonfly Manager OAuth provider client_secret disclosure via unauthenticated GET /api/v1/oauth (CVE-2026-49254)
Description
### Summary The Dragonfly Manager exposes `GET /api/v1/oauth` and `GET /api/v1/oauth/:id` to unauthenticated clients. The response body deserializes the entire `manager/models.Oauth` struct, which includes the `client_secret` field. Any network-reachable attacker can read the OAuth client secrets configured for `github` or `google` providers, defeating the confidentiality guarantee of those secrets and enabling subsequent abuse against the connected identity providers. ### Affected versions `github.com/dragonflyoss/dragonfly` `<= v2.4.3` (and current `main` at commit `46a8f1e`). The vulnerable wiring is present back to the introduction of OAuth GET handlers and was not addressed by GHSA-j8hf-cp34-g4j7 / CVE-2026-24124, whose remediation only added `jwt + rbac` middleware to the `/jobs` group. ### Privilege required Unauthenticated. The only precondition is that an administrator has registered at least one OAuth provider via `POST /api/v1/oauth` (a one-time setup for tenants that enable GitHub / Google sign-in). ### Vulnerable code [`manager/router/router.go:134-140`](https://github.com/dragonflyoss/dragonfly/blob/e1491bf6134fe307b09e82e11fa94b0587dcd323/manager/router/router.go#L134-L140) (v2.4.3) — the `/oauth` group registration: ```go // Oauth. oa := apiv1.Group("/oauth") oa.POST("", jwt.MiddlewareFunc(), rbac, h.CreateOauth) oa.DELETE(":id", jwt.MiddlewareFunc(), rbac, h.DestroyOauth) oa.PATCH(":id", jwt.MiddlewareFunc(), rbac, h.UpdateOauth) oa.GET(":id", h.GetOauth) oa.GET("", h.GetOauths) ``` Note the asymmetry inside the same `oa` route group: `POST`, `PATCH`, and `DELETE` explicitly attach `jwt.MiddlewareFunc(), rbac` as per-route middleware, but the two `GET` handlers omit both. Compare with the sibling group three lines below at [`manager/router/router.go:143-148`](https://github.com/dragonflyoss/dragonfly/blob/e1491bf6134fe307b09e82e11fa94b0587dcd323/manager/router/router.go#L143-L148), the `/clusters` group: ```go c := apiv1.Group("/clusters", jwt.MiddlewareFunc(), rbac) c.POST("", h.CreateCluster) c.DELETE(":id", h.DestroyCluster) c.PATCH(":id", h.UpdateCluster) c.GET(":id", h.GetCluster) c.GET("", h.GetClusters) ``` Here the middleware pair is attached once at the group level, so every verb on `/clusters` is guarded. The OAuth GETs are an unguarded sibling of the same primitive that GHSA-j8hf-cp34-g4j7 (Jan 2026) patched on the `/jobs` group. This is `sibling-method-dispatch-target` of the AP-012 sub-shape lens: same module, same router file, same anchor primitive ("group lacking JWT + RBAC"), parallel GET methods missed. The handler at [`manager/handlers/oauth.go:127-141`](https://github.com/dragonflyoss/dragonfly/blob/e1491bf6134fe307b09e82e11fa94b0587dcd323/manager/handlers/oauth.go#L127-L141) returns the model directly: ```go func (h *Handlers) GetOauth(ctx *gin.Context) { var params types.OauthParams if err := ctx.ShouldBindUri(¶ms); err != nil { ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()}) return } oauth, err := h.service.GetOauth(ctx.Request.Context(), params.ID) if err != nil { ctx.Error(err) // nolint: errcheck return } ctx.JSON(http.StatusOK, oauth) } ``` [`manager/handlers/oauth.go:155-171`](https://github.com/dragonflyoss/dragonfly/blob/e1491bf6134fe307b09e82e11fa94b0587dcd323/manager/handlers/oauth.go#L155-L171) has the parallel list handler: ```go func (h *Handlers) GetOauths(ctx *gin.Context) { var query types.GetOauthsQuery if err := ctx.ShouldBindQuery(&query); err != nil { ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()}) return } h.setPaginationDefault(&query.Page, &query.PerPage) oauth, count, err := h.service.GetOauths(ctx.Request.Context(), query) if err != nil { ctx.Error(err) // nolint: errcheck return } h.setPaginationLinkHeader(ctx, query.Page, query.PerPage, int(count)) ctx.JSON(http.StatusOK, oauth) } ``` [`manager/models/oauth.go:19-26`](https://github.com/dragonflyoss/dragonfly/blob/e1491bf6134fe307b09e82e11fa94b0587dcd323/manager/models/oauth.go#L19-L26) declares `ClientSecret` with no `json:"-"` tag, so it is serialized into every response: ```go type Oauth struct { BaseModel Name string `gorm:"column:name;type:varchar(256);index:uk_oauth2_name,unique;not null;comment:oauth2 name" json:"name"` BIO string `gorm:"column:bio;type:varchar(1024);comment:biography" json:"bio"` ClientID string `gorm:"column:client_id;type:varchar(256);index:uk_oauth2_client_id,unique;not null;comment:client id for oauth2" json:"client_id"` ClientSecret string `gorm:"column:client_secret;type:varchar(1024);not null;comment:client secret for oauth2" json:"client_secret"` RedirectURL string `gorm:"column:redirect_url;type:varchar(1024);comment:authorization callback url" json:"redirect_url"` } ``` ### How an unauthenticated request reaches the OAuth client_secret 1. `gin.Engine` routes `GET /api/v1/oauth/:id` to the `oa` group registered at [`manager/route
CVSS v4.0
Affected software
Run on your own infrastructure? Check whether these packages are installed with threat-finder — our free open-source scanner.
AI-Powered Analysis
Machine-generated threat intelligence
Technical Analysis
The Dragonfly Manager's API exposes GET endpoints for OAuth configurations that do not require authentication. These endpoints return the entire OAuth model, including the client_secret field, due to missing JWT and RBAC middleware on the GET methods. This allows unauthenticated attackers to retrieve OAuth client secrets for providers such as GitHub and Google. The issue is present in versions <= 2.4.3 and persists in the main branch at commit 46a8f1e. Other HTTP methods on the same route group are properly protected, but the GET methods were overlooked. The client_secret field is serialized in JSON responses because it lacks a json:"-" tag in the model definition.
Potential Impact
An unauthenticated attacker can retrieve OAuth client secrets configured in the Dragonfly Manager, compromising the confidentiality of these secrets. This exposure can enable attackers to abuse the connected identity providers (GitHub, Google) by impersonating the application or performing unauthorized OAuth flows. The confidentiality breach affects all tenants who have registered OAuth providers. No privilege is required to exploit this vulnerability other than the existence of at least one configured OAuth provider.
Mitigation Recommendations
Patch status is not yet confirmed — check the vendor advisory for current remediation guidance. Until a fix is available, restrict network access to the Dragonfly Manager API to trusted users only. Avoid exposing the /api/v1/oauth GET endpoints publicly. Monitor vendor communications for updates or patches addressing this issue. Do not rely on the existing JWT and RBAC protections as they do not cover the GET methods for OAuth endpoints.
Technical Details
- Gcve Source
- db.gcve.eu
- Osv Id
- GHSA-4q9j-6299-gxmr
- Osv Schema Version
- 1.4.0
- Aliases
- ["CVE-2026-49254"]
- Ecosystems
- ["Go"]
- Database Specific Severity
- LOW
- Cvss Version
- 4.0
Threat ID: 6a46ecba27e9c7971943cd96
Added to database: 07/02/2026, 22:56:58 UTC
Last enriched: 07/02/2026, 23:13:36 UTC
Last updated: 07/31/2026, 12:27:30 UTC
Views: 63
Community Reviews
0 reviewsCrowdsource mitigation strategies, share intel context, and vote on the most helpful responses. Sign in to add your voice and help keep defenders ahead.
Want to contribute mitigation steps or threat intel context? Sign in or create an account to join the community discussion.
Actions
Updates to AI analysis require Pro Console access. Upgrade inside Console → Billing.
Need more coverage?
Upgrade to Pro Console for AI refresh and higher limits.
For incident response and remediation, OffSeq services can help resolve threats faster.
Latest Threats
Check if your credentials are on the dark web
Instant breach scanning across billions of leaked records. Free tier available.