Postgres/MySQL/MSSQL: Don't return connection failure details to the client (#32408)

For security reasons, log any SQL connection error 
details rather than returning it to the client.

Fixes #26623
Fixes #22000

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
Marcus Efraimsson
2021-03-29 13:03:01 +02:00
committed by GitHub
co-authored by Arve Knudsen
parent a98fd22e34
commit b82c510581
4 changed files with 93 additions and 1 deletions
+19 -1
View File
@@ -4,8 +4,10 @@ import (
"container/list"
"context"
"database/sql"
"errors"
"fmt"
"math"
"net"
"regexp"
"strconv"
"strings"
@@ -29,6 +31,8 @@ import (
// MetaKeyExecutedQueryString is the key where the executed query should get stored
const MetaKeyExecutedQueryString = "executedQueryString"
var ErrConnectionFailed = errors.New("failed to connect to server - please inspect Grafana server log for details")
// SQLMacroEngine interpolates macros into sql. It takes in the Query to have access to query context and
// timeRange to be able to generate queries that use from and to.
type SQLMacroEngine interface {
@@ -184,7 +188,7 @@ func (e *dataPlugin) DataQuery(ctx context.Context, dsInfo *models.DataSource,
rows, err := db.Query(rawSQL)
if err != nil {
queryResult.Error = e.queryResultTransformer.TransformQueryError(err)
queryResult.Error = e.transformQueryError(err)
ch <- queryResult
return
}
@@ -431,6 +435,20 @@ func (e *dataPlugin) transformToTimeSeries(query plugins.DataSubQuery, rows *cor
return nil
}
func (e *dataPlugin) transformQueryError(err error) error {
// OpError is the error type usually returned by functions in the net
// package. It describes the operation, network type, and address of
// an error. We log this error rather than returing it to the client
// for security purposes.
var opErr *net.OpError
if errors.As(err, &opErr) {
e.log.Error("query error", "err", err)
return ErrConnectionFailed
}
return e.queryResultTransformer.TransformQueryError(err)
}
type processCfg struct {
rowCount int
columnTypes []*sql.ColumnType
+42
View File
@@ -1,15 +1,20 @@
package sqleng
import (
"database/sql"
"fmt"
"net"
"testing"
"time"
"github.com/grafana/grafana/pkg/components/null"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"xorm.io/core"
)
func TestSQLEngine(t *testing.T) {
@@ -309,4 +314,41 @@ func TestSQLEngine(t *testing.T) {
})
}
})
t.Run("Should handle connection errors", func(t *testing.T) {
randomErr := fmt.Errorf("random error")
tests := []struct {
err error
expectedErr error
expectQueryResultTransformerWasCalled bool
}{
{err: &net.OpError{Op: "Dial"}, expectedErr: ErrConnectionFailed, expectQueryResultTransformerWasCalled: false},
{err: randomErr, expectedErr: randomErr, expectQueryResultTransformerWasCalled: true},
}
for _, tc := range tests {
transformer := &testQueryResultTransformer{}
dp := dataPlugin{
log: log.New("test"),
queryResultTransformer: transformer,
}
resultErr := dp.transformQueryError(tc.err)
assert.ErrorIs(t, resultErr, tc.expectedErr)
assert.Equal(t, tc.expectQueryResultTransformerWasCalled, transformer.transformQueryErrorWasCalled)
}
})
}
type testQueryResultTransformer struct {
transformQueryErrorWasCalled bool
}
func (t *testQueryResultTransformer) TransformQueryResult(columnTypes []*sql.ColumnType, rows *core.Rows) (plugins.DataRowValues, error) {
return nil, nil
}
func (t *testQueryResultTransformer) TransformQueryError(err error) error {
t.transformQueryErrorWasCalled = true
return err
}