package logsanitizer import ( "bytes" "fmt" "io/ioutil" "testing" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" ) func TestUrlSanitizerHook(t *testing.T) { outBuf := &bytes.Buffer{} urlSanitizer := NewURLSanitizerHook() urlSanitizer.AddPossibleGrpcMethod( "UpdateRemoteMirror", "CreateRepositoryFromURL", "FetchRemote", ) logger := log.New() logger.Out = outBuf logger.Hooks.Add(urlSanitizer) testCases := []struct { desc string logFunc func() expectedString string }{ { desc: "with args", logFunc: func() { logger.WithFields(log.Fields{ "grpc.method": "CreateRepositoryFromURL", "args": []string{"/usr/bin/git", "clone", "--bare", "--", "https://foo_the_user:hUntEr1@gitlab.com/foo/bar", "/home/git/repositories/foo/bar"}, }).Info("spawn") }, expectedString: "[/usr/bin/git clone --bare -- https://[FILTERED]@gitlab.com/foo/bar /home/git/repositories/foo/bar]", }, { desc: "with error", logFunc: func() { logger.WithFields(log.Fields{ "grpc.method": "UpdateRemoteMirror", "error": fmt.Errorf("rpc error: code = Unknown desc = remote: Invalid username or password. fatal: Authentication failed for 'https://foo_the_user:hUntEr1@gitlab.com/foo/bar'"), }).Error("ERROR") }, expectedString: "rpc error: code = Unknown desc = remote: Invalid username or password. fatal: Authentication failed for 'https://[FILTERED]@gitlab.com/foo/bar'", }, { desc: "with message", logFunc: func() { logger.WithFields(log.Fields{ "grpc.method": "CreateRepositoryFromURL", }).Info("asked for: https://foo_the_user:hUntEr1@gitlab.com/foo/bar") }, expectedString: "asked for: https://[FILTERED]@gitlab.com/foo/bar", }, { desc: "with URL without scheme output", logFunc: func() { logger.WithFields(log.Fields{ "grpc.method": "FetchRemote", }).Info("fatal: unable to look up foo:bar@non-existent.org (port 9418) (nodename nor servname provided, or not known") }, expectedString: "unable to look up [FILTERED]@non-existent.org (port 9418) (nodename nor servname provided, or not known", }, { desc: "with gRPC method not added to the list", logFunc: func() { logger.WithFields(log.Fields{ "grpc.method": "UserDeleteTag", }).Error("fatal: 'https://foo_the_user:hUntEr1@gitlab.com/foo/bar' is not a valid tag name.") }, expectedString: "fatal: 'https://foo_the_user:hUntEr1@gitlab.com/foo/bar' is not a valid tag name.", }, { desc: "log with URL that does not require sanitization", logFunc: func() { logger.WithFields(log.Fields{ "grpc.method": "CreateRepositoryFromURL", }).Info("asked for: https://gitlab.com/gitlab-org/gitaly") }, expectedString: "asked for: https://gitlab.com/gitlab-org/gitaly", }, } for _, testCase := range testCases { t.Run(testCase.desc, func(t *testing.T) { testCase.logFunc() logOutput := outBuf.String() require.Contains(t, logOutput, testCase.expectedString) }) } } func BenchmarkUrlSanitizerWithoutSanitization(b *testing.B) { urlSanitizer := NewURLSanitizerHook() logger := log.New() logger.Out = ioutil.Discard logger.Hooks.Add(urlSanitizer) benchmarkLogging(logger, b) } func BenchmarkUrlSanitizerWithSanitization(b *testing.B) { urlSanitizer := NewURLSanitizerHook() urlSanitizer.AddPossibleGrpcMethod( "UpdateRemoteMirror", "CreateRepositoryFromURL", ) logger := log.New() logger.Out = ioutil.Discard logger.Hooks.Add(urlSanitizer) benchmarkLogging(logger, b) } func benchmarkLogging(logger *log.Logger, b *testing.B) { for n := 0; n < b.N; n++ { logger.WithFields(log.Fields{ "grpc.method": "CreateRepositoryFromURL", "args": []string{"/usr/bin/git", "clone", "--bare", "--", "https://foo_the_user:hUntEr1@gitlab.com/foo/bar", "/home/git/repositories/foo/bar"}, }).Info("spawn") logger.WithFields(log.Fields{ "grpc.method": "UpdateRemoteMirror", "error": fmt.Errorf("rpc error: code = Unknown desc = remote: Invalid username or password. fatal: Authentication failed for 'https://foo_the_user:hUntEr1@gitlab.com/foo/bar'"), }).Error("ERROR") logger.WithFields(log.Fields{ "grpc.method": "CreateRepositoryFromURL", }).Info("asked for: https://foo_the_user:hUntEr1@gitlab.com/foo/bar") } }