grafana/pkg/api/plugin_resource.go

130 lines
3.8 KiB
Go
Raw Permalink Normal View History

package api
import (
"errors"
"fmt"
"io"
"net/http"
"net/url"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/middleware/requestmeta"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/httpresponsesender"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/util/proxyutil"
"github.com/grafana/grafana/pkg/web"
)
patch(security): apply May 2026 patches (#124824) * patch(security): Access Control: Fix Editor can remove any annotation GL-Vuln: VUL-2026-0018 GL-Public-After: 2026-05-12 GL-Partner-Rel: 2026-04-27 GL-Partner-Ack: 2026-02-17 * patch(security): fix(live): limit input body to 500k GL-Vuln: VUL-2026-0031 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/63 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-02-25T14:02:00Z * patch(security): fix(plugins): limit input resource sizes GL-Vuln: VUL-2026-0032 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/64 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-02-25T14:02:00Z * patch(security): patch(security): fix broken access control in snapshot delete handler GL-Vuln: VUL-2026-0044 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/76 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-03-31 * patch(security): patch(security): fix auth proxy IPv6 bare whitelist parsing (main) GL-Vuln: VUL-2026-0045 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/77 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-03-30 * patch(security): Live: Avoid concurrent map read and map write GL-Vuln: VUL-2026-0049 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/81 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-03-18 * patch(security): Dashboards: Fix /import permission setting GL-Vuln: VUL-2026-0058 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/90 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-03-24 * patch(security): MSSQL, MySQL, postgres infinite loop exploit GL-Vuln: VUL-2026-0057 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/89 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-03-20 * patch(security): patch(security): ServiceAccounts RBAC cache invalidation on permission change GL-Vuln: VUL-2026-0061 GL-Public-After: 2026-05-12 GL-Partner-Rel: 2026-04-15 GL-Partner-Ack: 2026-04-06 * patch(security): SQL Expressions: Fix Security Bugs GL-Vuln: VUL-2026-0064 GL-Public-After: 2026-05-12 GL-Partner-Rel: 2026-04-27 GL-Partner-Ack: 2026-04-11 * fix: nolint gocyclo --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-13 12:53:11 -04:00
const maxResourceBodySize = 128 * 1024 * 1024 // 128 MiB
// CallResource passes a resource call from a plugin to the backend plugin.
//
// /api/plugins/:pluginId/resources/*
func (hs *HTTPServer) CallResource(c *contextmodel.ReqContext) {
hs.callPluginResource(c, web.Params(c.Req)[":pluginId"])
}
func (hs *HTTPServer) callPluginResource(c *contextmodel.ReqContext, pluginID string) {
pCtx, err := hs.pluginContextProvider.Get(c.Req.Context(), pluginID, c.SignedInUser, c.GetOrgID())
if err != nil {
if errors.Is(err, plugins.ErrPluginNotRegistered) {
c.JsonApiErr(404, "Plugin not found", nil)
return
}
c.JsonApiErr(500, "Failed to get plugin settings", err)
return
}
req, err := hs.pluginResourceRequest(c)
if err != nil {
c.JsonApiErr(http.StatusBadRequest, "Failed for create plugin resource request", err)
return
}
if err = hs.makePluginResourceRequest(c.Resp, req, pCtx); err != nil {
handleCallResourceError(err, c)
return
}
requestmeta.WithStatusSource(c.Req.Context(), c.Resp.Status())
}
func (hs *HTTPServer) callPluginResourceWithDataSource(c *contextmodel.ReqContext, pluginID string, ds *datasources.DataSource) {
pCtx, err := hs.pluginContextProvider.GetWithDataSource(c.Req.Context(), pluginID, c.SignedInUser, ds)
if err != nil {
if errors.Is(err, plugins.ErrPluginNotRegistered) {
c.JsonApiErr(404, "Plugin not found", nil)
return
}
c.JsonApiErr(500, "Failed to get plugin settings", err)
return
}
err = hs.DataSourceRequestValidator.Validate(ds.URL, ds.JsonDataMap(), c.Req)
if err != nil {
c.JsonApiErr(http.StatusForbidden, "Access denied", err)
return
}
req, err := hs.pluginResourceRequest(c)
if err != nil {
c.JsonApiErr(http.StatusBadRequest, "Failed for create plugin resource request", err)
return
}
if err = hs.makePluginResourceRequest(c.Resp, req, pCtx); err != nil {
handleCallResourceError(err, c)
return
}
requestmeta.WithStatusSource(c.Req.Context(), c.Resp.Status())
}
func (hs *HTTPServer) pluginResourceRequest(c *contextmodel.ReqContext) (*http.Request, error) {
clonedReq := c.Req.Clone(c.Req.Context())
rawURL := web.Params(c.Req)["*"]
clonedReq.URL = &url.URL{
Path: rawURL,
RawQuery: clonedReq.URL.RawQuery,
}
return clonedReq, nil
}
func (hs *HTTPServer) makePluginResourceRequest(w http.ResponseWriter, req *http.Request, pCtx backend.PluginContext) error {
proxyutil.PrepareProxyRequest(req)
patch(security): apply May 2026 patches (#124824) * patch(security): Access Control: Fix Editor can remove any annotation GL-Vuln: VUL-2026-0018 GL-Public-After: 2026-05-12 GL-Partner-Rel: 2026-04-27 GL-Partner-Ack: 2026-02-17 * patch(security): fix(live): limit input body to 500k GL-Vuln: VUL-2026-0031 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/63 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-02-25T14:02:00Z * patch(security): fix(plugins): limit input resource sizes GL-Vuln: VUL-2026-0032 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/64 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-02-25T14:02:00Z * patch(security): patch(security): fix broken access control in snapshot delete handler GL-Vuln: VUL-2026-0044 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/76 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-03-31 * patch(security): patch(security): fix auth proxy IPv6 bare whitelist parsing (main) GL-Vuln: VUL-2026-0045 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/77 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-03-30 * patch(security): Live: Avoid concurrent map read and map write GL-Vuln: VUL-2026-0049 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/81 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-03-18 * patch(security): Dashboards: Fix /import permission setting GL-Vuln: VUL-2026-0058 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/90 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-03-24 * patch(security): MSSQL, MySQL, postgres infinite loop exploit GL-Vuln: VUL-2026-0057 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/89 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-03-20 * patch(security): patch(security): ServiceAccounts RBAC cache invalidation on permission change GL-Vuln: VUL-2026-0061 GL-Public-After: 2026-05-12 GL-Partner-Rel: 2026-04-15 GL-Partner-Ack: 2026-04-06 * patch(security): SQL Expressions: Fix Security Bugs GL-Vuln: VUL-2026-0064 GL-Public-After: 2026-05-12 GL-Partner-Rel: 2026-04-27 GL-Partner-Ack: 2026-04-11 * fix: nolint gocyclo --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-13 12:53:11 -04:00
body, err := io.ReadAll(http.MaxBytesReader(w, req.Body, maxResourceBodySize))
if err != nil {
return fmt.Errorf("failed to read request body: %w", err)
}
crReq := &backend.CallResourceRequest{
PluginContext: pCtx,
Path: req.URL.Path,
Method: req.Method,
URL: req.URL.String(),
Headers: req.Header,
Body: body,
}
httpSender := httpresponsesender.New(w)
return hs.pluginClient.CallResource(req.Context(), crReq, httpSender)
}
func handleCallResourceError(err error, reqCtx *contextmodel.ReqContext) {
patch(security): apply May 2026 patches (#124824) * patch(security): Access Control: Fix Editor can remove any annotation GL-Vuln: VUL-2026-0018 GL-Public-After: 2026-05-12 GL-Partner-Rel: 2026-04-27 GL-Partner-Ack: 2026-02-17 * patch(security): fix(live): limit input body to 500k GL-Vuln: VUL-2026-0031 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/63 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-02-25T14:02:00Z * patch(security): fix(plugins): limit input resource sizes GL-Vuln: VUL-2026-0032 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/64 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-02-25T14:02:00Z * patch(security): patch(security): fix broken access control in snapshot delete handler GL-Vuln: VUL-2026-0044 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/76 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-03-31 * patch(security): patch(security): fix auth proxy IPv6 bare whitelist parsing (main) GL-Vuln: VUL-2026-0045 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/77 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-03-30 * patch(security): Live: Avoid concurrent map read and map write GL-Vuln: VUL-2026-0049 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/81 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-03-18 * patch(security): Dashboards: Fix /import permission setting GL-Vuln: VUL-2026-0058 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/90 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-03-24 * patch(security): MSSQL, MySQL, postgres infinite loop exploit GL-Vuln: VUL-2026-0057 https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/first-party/89 GL-Partner-Rel: 2026-04-09 GL-Public-After: 2026-05-12 GL-Partner-Ack: 2026-03-20 * patch(security): patch(security): ServiceAccounts RBAC cache invalidation on permission change GL-Vuln: VUL-2026-0061 GL-Public-After: 2026-05-12 GL-Partner-Rel: 2026-04-15 GL-Partner-Ack: 2026-04-06 * patch(security): SQL Expressions: Fix Security Bugs GL-Vuln: VUL-2026-0064 GL-Public-After: 2026-05-12 GL-Partner-Rel: 2026-04-27 GL-Partner-Ack: 2026-04-11 * fix: nolint gocyclo --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-13 12:53:11 -04:00
var maxBytesErr *http.MaxBytesError
if errors.As(err, &maxBytesErr) {
resp := response.Error(http.StatusRequestEntityTooLarge, "Request body too large", err)
resp.WriteTo(reqCtx)
return
}
resp := response.ErrOrFallback(http.StatusInternalServerError, "Failed to call resource", err)
resp.WriteTo(reqCtx)
}