Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

profiler: add API key check at initialization of Profiler object #718

Merged
merged 12 commits into from
Sep 10, 2020
24 changes: 24 additions & 0 deletions profiler/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"runtime"
"strings"
"time"
"unicode"

"gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig"
"gopkg.in/DataDog/dd-trace-go.v1/internal/log"
Expand Down Expand Up @@ -71,6 +72,29 @@ func urlForSite(site string) (string, error) {
return u, err
}

// isAPIKeyValid reports whether the given string is a structurally valid API key
func isAPIKeyValid(key string) bool {
if len(key) != 32 {
return false
}
for _, c := range key {
if !unicode.IsLetter(c) && !unicode.IsLower(c) && !unicode.IsNumber(c) {
return false
}
}
return true
}

// sanitizeAPIKey prepares an API key for display, by censoring everything
// beyond the first 8 characters of the input
func sanitizeAPIKey(key string) string {
gbbr marked this conversation as resolved.
Show resolved Hide resolved
ret := []byte(key)
for i := 8; i < len(ret); i++ {
ret[i] = '*'
}
return string(ret)
}

func (c *config) addProfileType(t ProfileType) {
if c.types == nil {
c.types = make(map[ProfileType]struct{})
Expand Down
15 changes: 9 additions & 6 deletions profiler/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,20 @@ func TestOptions(t *testing.T) {
})

t.Run("WithAPIKey", func(t *testing.T) {
var validKey = "12345678901234567890123456789012"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var validKey = "12345678901234567890123456789012"
validKey := "12345678901234567890123456789012"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, maybe we can have just one global constant testAPIKey

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great suggestions.

Writing the tests revealed an issue with non-ASCII glyphs returning true by unicode.IsLower(), so this already paid off.

Also added a simple positive and negative check for Profiler instantiation.

var cfg config
WithAPIKey("123")(&cfg)
assert.Equal(t, "123", cfg.apiKey)
WithAPIKey(validKey)(&cfg)
assert.Equal(t, validKey, cfg.apiKey)
assert.Equal(t, cfg.apiURL, cfg.targetURL)
})

t.Run("WithAPIKey/override", func(t *testing.T) {
os.Setenv("DD_API_KEY", "apikey")
defer os.Unsetenv("DD_API_KEY")
var validKey = "12345678901234567890123456789012"
var cfg config
WithAPIKey("123")(&cfg)
assert.Equal(t, "123", cfg.apiKey)
WithAPIKey(validKey)(&cfg)
assert.Equal(t, validKey, cfg.apiKey)
})

t.Run("WithURL", func(t *testing.T) {
Expand Down Expand Up @@ -179,10 +181,11 @@ func TestEnvVars(t *testing.T) {
})

t.Run("DD_API_KEY", func(t *testing.T) {
os.Setenv("DD_API_KEY", "123")
var validKey = "12345678901234567890123456789012"
os.Setenv("DD_API_KEY", validKey)
defer os.Unsetenv("DD_API_KEY")
cfg := defaultConfig()
assert.Equal(t, "123", cfg.apiKey)
assert.Equal(t, validKey, cfg.apiKey)
})

t.Run("DD_SITE", func(t *testing.T) {
Expand Down
3 changes: 3 additions & 0 deletions profiler/profiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ func newProfiler(opts ...Option) (*profiler, error) {
opt(cfg)
}
if cfg.apiKey != "" {
if !isAPIKeyValid(cfg.apiKey) {
return nil, fmt.Errorf("API key has incorrect format: %s", sanitizeAPIKey(cfg.apiKey))
}
cfg.targetURL = cfg.apiURL
} else {
cfg.targetURL = cfg.agentURL
Expand Down