Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
* Masked the sensitive credential data in the connection string (DSN, data source name) from error messages for security reasons

## v3.121.0
* Changed internal pprof label to pyroscope supported format
* Added `query.ImplicitTxControl()` transaction control (the same as `query.NoTx()` and `query.EmptyTxControl()`). See more about implicit transactions on [ydb.tech](https://ydb.tech/docs/en/concepts/transactions?version=v25.2#implicit)
Expand Down
3 changes: 2 additions & 1 deletion driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
schemeConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/scheme/config"
internalScripting "github.com/ydb-platform/ydb-go-sdk/v3/internal/scripting"
scriptingConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/scripting/config"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/secret"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
internalTable "github.com/ydb-platform/ydb-go-sdk/v3/internal/table"
tableConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/table/config"
Expand Down Expand Up @@ -290,7 +291,7 @@ func Open(ctx context.Context, dsn string, opts ...Option) (_ *Driver, _ error)
if parser := dsnParsers[parserIdx]; parser != nil {
optsFromParser, err := parser(dsn)
if err != nil {
return nil, xerrors.WithStackTrace(fmt.Errorf("data source name '%s' wrong: %w", dsn, err))
return nil, xerrors.WithStackTrace(fmt.Errorf("data source name '%s' wrong: %w", secret.DSN(dsn), err))
}
opts = append(opts, optsFromParser...)
}
Expand Down
2 changes: 1 addition & 1 deletion driver_string_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func TestDriver_String(t *testing.T) {
config.WithSecure(true),
config.WithCredentials(credentials.NewStaticCredentials("user", "password", "")),
)},
s: `Driver{Endpoint:"localhost",Database:"local",Secure:true,Credentials:Static{User:"user",Password:"pas***rd",Token:"****(CRC-32c: 00000000)",From:"github.com/ydb-platform/ydb-go-sdk/v3/credentials.NewStaticCredentials(credentials.go:35)"}}`, //nolint:lll
s: `Driver{Endpoint:"localhost",Database:"local",Secure:true,Credentials:Static{User:"user",Password:"p******d",Token:"****(CRC-32c: 00000000)",From:"github.com/ydb-platform/ydb-go-sdk/v3/credentials.NewStaticCredentials(credentials.go:35)"}}`, //nolint:lll
},
{
name: xtest.CurrentFileLine(),
Expand Down
4 changes: 2 additions & 2 deletions internal/credentials/errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func TestAccessError(t *testing.T) {
errorString: "something went wrong (" +
"endpoint:\"grps://localhost:2135\"," +
"database:\"/local\"," +
"credentials:\"Static{User:\\\"USER\\\",Password:\\\"SEC**********RD\\\",Token:\\\"****(CRC-32c: 00000000)\\\"}\"" + //nolint:lll
"credentials:\"Static{User:\\\"USER\\\",Password:\\\"S*************D\\\",Token:\\\"****(CRC-32c: 00000000)\\\"}\"" + //nolint:lll
"): test " +
"at `github.com/ydb-platform/ydb-go-sdk/v3/internal/credentials.TestAccessError(errors_test.go:93)`",
},
Expand All @@ -123,7 +123,7 @@ func TestAccessError(t *testing.T) {
errorString: "something went wrong (" +
"endpoint:\"grps://localhost:2135\"," +
"database:\"/local\"," +
"credentials:\"Static{User:\\\"USER\\\",Password:\\\"SEC**********RD\\\",Token:\\\"****(CRC-32c: 00000000)\\\",From:\\\"TestAccessError\\\"}\"" + //nolint:lll
"credentials:\"Static{User:\\\"USER\\\",Password:\\\"S*************D\\\",Token:\\\"****(CRC-32c: 00000000)\\\",From:\\\"TestAccessError\\\"}\"" + //nolint:lll
"): test " +
"at `github.com/ydb-platform/ydb-go-sdk/v3/internal/credentials.TestAccessError(errors_test.go:112)`",
},
Expand Down
56 changes: 56 additions & 0 deletions internal/secret/dsn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package secret

import (
"net/url"
"strings"

"github.com/ydb-platform/ydb-go-sdk/v3/pkg/xstring"
)

func DSN(dsn string) string {
u, err := url.Parse(dsn)
if err != nil {
return "<invalid DSN>"
}

buffer := xstring.Buffer()
defer buffer.Free()

buffer.WriteString(u.Scheme + "://")

if u.User != nil {
buffer.WriteString(u.User.Username())
if password, has := u.User.Password(); has {
buffer.WriteString(":" + Password(password))
}
buffer.WriteString("@")
}

buffer.WriteString(u.Host)
buffer.WriteString(u.Path)

if len(u.RawQuery) > 0 {
buffer.WriteString("?")

params := strings.Split(u.RawQuery, "&")

for i, param := range params {
if i > 0 {
buffer.WriteString("&")
}
paramValue := strings.Split(param, "=")
buffer.WriteString(paramValue[0])
if len(paramValue) > 1 {
buffer.WriteString("=")
switch paramValue[0] {
case "token", "password":
buffer.WriteString(Mask(paramValue[1]))
default:
buffer.WriteString(paramValue[1])
}
}
}
}

return buffer.String()
}
47 changes: 47 additions & 0 deletions internal/secret/dsn_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package secret

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestDSN(t *testing.T) {
for _, tt := range []struct {
dsn string
exp string
}{
{
dsn: "grpc://192.168.0.%31:2136/",
exp: "<invalid DSN>",
},
{
dsn: "grpc://debuguser:debugpassword@localhost:2136/local1",
exp: "grpc://debuguser:d***********d@localhost:2136/local1",
},
{
dsn: "grpc://localhost:2136/local1?user=debuguser&password=debugpassword",
exp: "grpc://localhost:2136/local1?user=debuguser&password=d***********d",
},
{
dsn: "grpc://localhost:2136/local1?login=debuguser&password=debugpassword",
exp: "grpc://localhost:2136/local1?login=debuguser&password=d***********d",
},
{
dsn: "grpc://localhost:2136/local1?param1=value1&login=debuguser&param2=value2&password=debugpassword&param2=value3",
exp: "grpc://localhost:2136/local1?param1=value1&login=debuguser&param2=value2&password=d***********d&param2=value3",
},
{
dsn: "grpc://localhost:2136/local1?param1&login=debuguser&param2=value2&password=debugpassword&param2=value3",
exp: "grpc://localhost:2136/local1?param1&login=debuguser&param2=value2&password=d***********d&param2=value3",
},
{
dsn: "grpc://localhost:2136/local1?token=secrettoken123",
exp: "grpc://localhost:2136/local1?token=s************3",
},
} {
t.Run(tt.dsn, func(t *testing.T) {
require.Equal(t, tt.exp, DSN(tt.dsn))
})
}
}
20 changes: 20 additions & 0 deletions internal/secret/mask.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package secret

func Mask(s string) string {
var (
runes = []rune(s)
startPosition = 1
endPosition = len(runes) - 1
)

if len(runes) < 5 {
startPosition = 0
endPosition = len(runes)
}

for i := startPosition; i < endPosition; i++ {
runes[i] = '*'
}

return string(runes)
}
35 changes: 35 additions & 0 deletions internal/secret/mask_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package secret

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestMask(t *testing.T) {
for _, tt := range []struct {
s string
exp string
}{
{
s: "test",
exp: "****",
},
{
s: "test-long-password",
exp: "t****************d",
},
{
s: "пароль",
exp: "п****ь",
},
{
s: "пар",
exp: "***",
},
} {
t.Run("", func(t *testing.T) {
require.Equal(t, tt.exp, Mask(tt.s))
})
}
}
21 changes: 1 addition & 20 deletions internal/secret/password.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,5 @@
package secret

import (
"github.com/ydb-platform/ydb-go-sdk/v3/pkg/xstring"
)

func Password(password string) string {
var (
bytes = []byte(password)
startPosition = 3
endPosition = len(bytes) - 2
)
if startPosition > endPosition {
for i := range bytes {
bytes[i] = '*'
}
} else {
for i := startPosition; i < endPosition; i++ {
bytes[i] = '*'
}
}

return xstring.FromBytes(bytes)
return Mask(password)
}
10 changes: 9 additions & 1 deletion internal/secret/password_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,15 @@ func TestPassword(t *testing.T) {
},
{
password: "test-long-password",
exp: "tes*************rd",
exp: "t****************d",
},
{
password: "пароль",
exp: "п****ь",
},
{
password: "пар",
exp: "***",
},
} {
t.Run("", func(t *testing.T) {
Expand Down
3 changes: 2 additions & 1 deletion options.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
ratelimiterConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/ratelimiter/config"
schemeConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/scheme/config"
scriptingConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/scripting/config"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/secret"
tableConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/table/config"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xsql"
Expand Down Expand Up @@ -203,7 +204,7 @@ func WithConnectionString(connectionString string) Option {
info, err := dsn.Parse(connectionString)
if err != nil {
return xerrors.WithStackTrace(
fmt.Errorf("parse connection string '%s' failed: %w", connectionString, err),
fmt.Errorf("parse connection string '%s' failed: %w", secret.DSN(connectionString), err),
)
}
d.options = append(d.options, info.Options...)
Expand Down
6 changes: 5 additions & 1 deletion sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"

"github.com/ydb-platform/ydb-go-sdk/v3/internal/bind"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/secret"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/tx"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xsql"
Expand Down Expand Up @@ -47,7 +48,10 @@ func (d *sqlDriver) Open(string) (driver.Conn, error) {
func (d *sqlDriver) OpenConnector(dataSourceName string) (driver.Connector, error) {
db, err := Open(context.Background(), dataSourceName)
if err != nil {
return nil, xerrors.WithStackTrace(fmt.Errorf("failed to connect by data source name '%s': %w", dataSourceName, err))
return nil, xerrors.WithStackTrace(fmt.Errorf(
"failed to connect by data source name '%s': %w",
secret.DSN(dataSourceName), err,
))
}

c, err := Connector(db, append(db.databaseSQLOptions,
Expand Down
Loading