AzureMonitorDatasource: Add bounds check to fix panics (#110879)

(azure-monitor-datasource): add bounds check to fix panics
This commit is contained in:
Tim Mulqueen
2025-09-11 12:31:11 +02:00
committed by GitHub
parent 5ce13061d5
commit 01b5543121
4 changed files with 202 additions and 0 deletions
@@ -1184,3 +1184,118 @@ func TestBuildAppInsightsQuery(t *testing.T) {
})
}
}
func TestBuildAppInsightsQuery_EmptyResources(t *testing.T) {
fromStart := time.Date(2018, 3, 15, 13, 0, 0, 0, time.UTC).In(time.Local)
timeRange := backend.TimeRange{From: fromStart, To: fromStart.Add(34 * time.Minute)}
ctx := context.Background()
// Create a mock HTTP server that returns empty correlation resources
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
// Return empty correlation response
correlationRes := AzureCorrelationAPIResponse{
ID: "/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.Insights/components/r1",
Name: "guid-1",
Type: "microsoft.insights/transactions",
Properties: AzureCorrelationAPIResponseProperties{
Resources: []string{}, // Empty resources array
NextLink: nil,
},
}
err := json.NewEncoder(w).Encode(correlationRes)
if err != nil {
t.Errorf("failed to encode correlation API response")
}
}))
defer svr.Close()
provider := httpclient.NewProvider(httpclient.ProviderOptions{Timeout: &httpclient.DefaultTimeoutOptions})
client, err := provider.New()
require.NoError(t, err)
dsInfo := types.DatasourceInfo{
Services: map[string]types.DatasourceService{
"Azure Monitor": {URL: svr.URL, HTTPClient: client},
},
JSONData: map[string]any{
"azureLogAnalyticsSameAs": false,
},
Settings: types.AzureMonitorSettings{
SubscriptionId: "test-sub-id",
},
}
appInsightsRegExp, err := regexp.Compile("providers/Microsoft.Insights/components")
require.NoError(t, err)
logger := log.NewNullLogger()
tests := []struct {
name string
queryModel backend.DataQuery
expectedErrorString string
}{
{
name: "empty resources array should return error",
queryModel: backend.DataQuery{
JSON: []byte(fmt.Sprintf(`{
"queryType": "Azure Traces",
"azureTraces": {
"resources": [],
"resultFormat": "%s",
"traceTypes": ["trace"]
}
}`, dataquery.ResultFormatTable)),
RefID: "A",
TimeRange: timeRange,
QueryType: string(dataquery.AzureQueryTypeAzureTraces),
},
expectedErrorString: "no resources specified for Azure traces query",
},
{
name: "missing resources field should return error",
queryModel: backend.DataQuery{
JSON: []byte(fmt.Sprintf(`{
"queryType": "Azure Traces",
"azureTraces": {
"resultFormat": "%s",
"traceTypes": ["trace"]
}
}`, dataquery.ResultFormatTable)),
RefID: "A",
TimeRange: timeRange,
QueryType: string(dataquery.AzureQueryTypeAzureTraces),
},
expectedErrorString: "no resources specified for Azure traces query",
},
{
name: "trace exemplar with empty correlation resources should return error",
queryModel: backend.DataQuery{
JSON: []byte(fmt.Sprintf(`{
"queryType": "Azure Traces",
"azureTraces": {
"resources": ["/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.Insights/components/r1"],
"resultFormat": "%s",
"traceTypes": ["trace"],
"operationId": "missing-op-id"
}
}`, dataquery.ResultFormatTable)),
RefID: "A",
TimeRange: timeRange,
QueryType: string(dataquery.AzureQueryTypeTraceExemplar),
},
expectedErrorString: "no correlation resources found for trace exemplar query with operation ID: missing-op-id",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
query, err := buildAppInsightsQuery(ctx, tt.queryModel, dsInfo, appInsightsRegExp, logger)
require.Error(t, err)
require.Nil(t, query)
require.Contains(t, err.Error(), tt.expectedErrorString)
})
}
}