From 347273cdeae77cebf0812144d6da127e9e4cf2c3 Mon Sep 17 00:00:00 2001 From: Domas Date: Thu, 27 May 2021 14:12:29 +0300 Subject: [PATCH] Alerting: check upstream response content type in lotex proxy (#34760) --- pkg/services/ngalert/api/promql_compat.go | 9 ++++-- pkg/services/ngalert/api/util.go | 28 ++++++++++++------- .../features/alerting/unified/api/ruler.ts | 5 +++- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/pkg/services/ngalert/api/promql_compat.go b/pkg/services/ngalert/api/promql_compat.go index e93ff9f890e..889771ccb23 100644 --- a/pkg/services/ngalert/api/promql_compat.go +++ b/pkg/services/ngalert/api/promql_compat.go @@ -14,6 +14,7 @@ import ( "github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/promql/parser" + "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/util" ) @@ -112,9 +113,13 @@ func instantQueryResults(resp instantQueryResponse) (eval.Results, error) { } } -func instantQueryResultsExtractor(b []byte) (interface{}, error) { +func instantQueryResultsExtractor(r *response.NormalResponse) (interface{}, error) { + contentType := r.Header().Get("Content-Type") + if !strings.Contains(contentType, "json") { + return nil, fmt.Errorf("unexpected content type from upstream. expected JSON, got %v", contentType) + } var resp instantQueryResponse - err := json.Unmarshal(b, &resp) + err := json.Unmarshal(r.Body(), &resp) if err != nil { return nil, err } diff --git a/pkg/services/ngalert/api/util.go b/pkg/services/ngalert/api/util.go index 2862af18bd6..5645e5243bc 100644 --- a/pkg/services/ngalert/api/util.go +++ b/pkg/services/ngalert/api/util.go @@ -88,7 +88,7 @@ func (p *AlertingProxy) withReq( method string, u *url.URL, body io.Reader, - extractor func([]byte) (interface{}, error), + extractor func(*response.NormalResponse) (interface{}, error), headers map[string]string, ) response.Response { req, err := http.NewRequest(method, u.String(), body) @@ -119,7 +119,7 @@ func (p *AlertingProxy) withReq( return response.Error(status, errMessage, nil) } - t, err := extractor(resp.Body()) + t, err := extractor(resp) if err != nil { return response.Error(500, err.Error(), nil) } @@ -132,9 +132,13 @@ func (p *AlertingProxy) withReq( return response.JSON(status, b) } -func yamlExtractor(v interface{}) func([]byte) (interface{}, error) { - return func(b []byte) (interface{}, error) { - decoder := yaml.NewDecoder(bytes.NewReader(b)) +func yamlExtractor(v interface{}) func(*response.NormalResponse) (interface{}, error) { + return func(resp *response.NormalResponse) (interface{}, error) { + contentType := resp.Header().Get("Content-Type") + if !strings.Contains(contentType, "yaml") { + return nil, fmt.Errorf("unexpected content type from upstream. expected YAML, got %v", contentType) + } + decoder := yaml.NewDecoder(bytes.NewReader(resp.Body())) decoder.KnownFields(true) err := decoder.Decode(v) @@ -143,18 +147,22 @@ func yamlExtractor(v interface{}) func([]byte) (interface{}, error) { } } -func jsonExtractor(v interface{}) func([]byte) (interface{}, error) { +func jsonExtractor(v interface{}) func(*response.NormalResponse) (interface{}, error) { if v == nil { // json unmarshal expects a pointer v = &map[string]interface{}{} } - return func(b []byte) (interface{}, error) { - return v, json.Unmarshal(b, v) + return func(resp *response.NormalResponse) (interface{}, error) { + contentType := resp.Header().Get("Content-Type") + if !strings.Contains(contentType, "json") { + return nil, fmt.Errorf("unexpected content type from upstream. expected JSON, got %v", contentType) + } + return v, json.Unmarshal(resp.Body(), v) } } -func messageExtractor(b []byte) (interface{}, error) { - return map[string]string{"message": string(b)}, nil +func messageExtractor(resp *response.NormalResponse) (interface{}, error) { + return map[string]string{"message": string(resp.Body())}, nil } func validateCondition(c ngmodels.Condition, user *models.SignedInUser, skipCache bool, datasourceCache datasources.CacheService) error { diff --git a/public/app/features/alerting/unified/api/ruler.ts b/public/app/features/alerting/unified/api/ruler.ts index f73440025ff..23dca2687ea 100644 --- a/public/app/features/alerting/unified/api/ruler.ts +++ b/public/app/features/alerting/unified/api/ruler.ts @@ -77,7 +77,10 @@ async function rulerGetRequest(url: string, empty: T): Promise { } catch (e) { if (e?.status === 404 || e?.data?.message?.includes('group does not exist')) { return empty; - } else if (e?.status === 500 && e?.data?.message?.includes('mapping values are not allowed in this context')) { + } else if ( + e?.status === 500 && + e?.data?.message?.includes('unexpected content type from upstream. expected YAML, got text/html') + ) { throw { ...e, data: {