Skip to content

Commit

Permalink
Merge pull request #882 from wakatime/bugfix/show-branch-names
Browse files Browse the repository at this point in the history
Ability to obfuscate project names but still show branch names
  • Loading branch information
alanhamlett authored May 9, 2023
2 parents 9ded0dd + d3e9851 commit 849d5c5
Show file tree
Hide file tree
Showing 17 changed files with 301 additions and 21 deletions.
206 changes: 206 additions & 0 deletions cmd/heartbeat/heartbeat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"github.com/wakatime/wakatime-cli/pkg/offline"
"github.com/wakatime/wakatime-cli/pkg/project"
"github.com/wakatime/wakatime-cli/pkg/version"
"github.com/wakatime/wakatime-cli/pkg/windows"
"github.com/yookoala/realpath"

"github.com/matishsiao/goInfo"
"github.com/spf13/viper"
Expand Down Expand Up @@ -881,9 +883,213 @@ func TestSendHeartbeats_ErrBackoff_Verbose(t *testing.T) {
assert.Contains(t, string(output), "will retry at")
}

func TestSendHeartbeats_ObfuscateProject(t *testing.T) {
testServerURL, router, tearDown := setupTestServer()
defer tearDown()

var (
plugin = "plugin/0.0.1"
numCalls int
)

fp := setupTestGitBasic(t)

router.HandleFunc("/users/current/heartbeats.bulk", func(w http.ResponseWriter, req *http.Request) {
// check request
assert.Equal(t, http.MethodPost, req.Method)
assert.Equal(t, []string{"application/json"}, req.Header["Accept"])
assert.Equal(t, []string{"application/json"}, req.Header["Content-Type"])
assert.Equal(t, []string{"Basic MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAw"}, req.Header["Authorization"])
assert.True(t, strings.HasSuffix(req.Header["User-Agent"][0], plugin), fmt.Sprintf(
"%q should have suffix %q",
req.Header["User-Agent"][0],
plugin,
))

expectedBody, err := os.ReadFile("testdata/api_heartbeats_request_template_obfuscated_project.json")
require.NoError(t, err)

body, err := io.ReadAll(req.Body)
require.NoError(t, err)

var entity struct {
Entity string `json:"entity"`
}

err = json.Unmarshal(body, &[]any{&entity})
require.NoError(t, err)

lines, err := project.ReadFile(filepath.Join(fp, "wakatime-cli", ".wakatime-project"), 1)
require.NoError(t, err)

expectedBodyStr := fmt.Sprintf(string(expectedBody), entity.Entity, lines[0], heartbeat.UserAgent(plugin))

assert.True(t, strings.HasSuffix(entity.Entity, "src/pkg/file.go"))
assert.JSONEq(t, expectedBodyStr, string(body))

// send response
w.WriteHeader(http.StatusCreated)

f, err := os.Open("testdata/api_heartbeats_response.json")
require.NoError(t, err)
defer f.Close()

_, err = io.Copy(w, f)
require.NoError(t, err)

numCalls++
})

v := viper.New()
v.SetDefault("sync-offline-activity", 1000)
v.Set("api-url", testServerURL)
v.Set("category", "debugging")
v.Set("cursorpos", 42)
v.Set("entity", filepath.Join(fp, "wakatime-cli/src/pkg/file.go"))
v.Set("entity-type", "file")
v.Set("key", "00000000-0000-4000-8000-000000000000")
v.Set("language", "Go")
v.Set("alternate-language", "Golang")
v.Set("hide-project-names", true)
v.Set("lineno", 13)
v.Set("local-file", "testdata/localfile.go")
v.Set("plugin", plugin)
v.Set("time", 1585598059.1)
v.Set("timeout", 5)
v.Set("write", true)

offlineQueueFile, err := os.CreateTemp(t.TempDir(), "")
require.NoError(t, err)

err = cmdheartbeat.SendHeartbeats(v, offlineQueueFile.Name())
require.NoError(t, err)

assert.Eventually(t, func() bool { return numCalls == 1 }, time.Second, 50*time.Millisecond)
}

func TestSendHeartbeats_ObfuscateProjectNotBranch(t *testing.T) {
testServerURL, router, tearDown := setupTestServer()
defer tearDown()

var (
plugin = "plugin/0.0.1"
numCalls int
)

fp := setupTestGitBasic(t)

router.HandleFunc("/users/current/heartbeats.bulk", func(w http.ResponseWriter, req *http.Request) {
// check request
assert.Equal(t, http.MethodPost, req.Method)
assert.Equal(t, []string{"application/json"}, req.Header["Accept"])
assert.Equal(t, []string{"application/json"}, req.Header["Content-Type"])
assert.Equal(t, []string{"Basic MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAw"}, req.Header["Authorization"])
assert.True(t, strings.HasSuffix(req.Header["User-Agent"][0], plugin), fmt.Sprintf(
"%q should have suffix %q",
req.Header["User-Agent"][0],
plugin,
))

expectedBody, err := os.ReadFile("testdata/api_heartbeats_request_template_obfuscated_project_not_branch.json")
require.NoError(t, err)

body, err := io.ReadAll(req.Body)
require.NoError(t, err)

var entity struct {
Entity string `json:"entity"`
}

err = json.Unmarshal(body, &[]any{&entity})
require.NoError(t, err)

lines, err := project.ReadFile(filepath.Join(fp, "wakatime-cli", ".wakatime-project"), 1)
require.NoError(t, err)

expectedBodyStr := fmt.Sprintf(string(expectedBody), entity.Entity, lines[0], heartbeat.UserAgent(plugin))

assert.True(t, strings.HasSuffix(entity.Entity, "src/pkg/file.go"))
assert.JSONEq(t, expectedBodyStr, string(body))

// send response
w.WriteHeader(http.StatusCreated)

f, err := os.Open("testdata/api_heartbeats_response.json")
require.NoError(t, err)
defer f.Close()

_, err = io.Copy(w, f)
require.NoError(t, err)

numCalls++
})

v := viper.New()
v.SetDefault("sync-offline-activity", 1000)
v.Set("api-url", testServerURL)
v.Set("category", "debugging")
v.Set("cursorpos", 42)
v.Set("entity", filepath.Join(fp, "wakatime-cli/src/pkg/file.go"))
v.Set("entity-type", "file")
v.Set("key", "00000000-0000-4000-8000-000000000000")
v.Set("language", "Go")
v.Set("alternate-language", "Golang")
v.Set("hide-project-names", true)
v.Set("hide-branch-names", false)
v.Set("lineno", 13)
v.Set("local-file", "testdata/localfile.go")
v.Set("plugin", plugin)
v.Set("time", 1585598059.1)
v.Set("timeout", 5)
v.Set("write", true)

offlineQueueFile, err := os.CreateTemp(t.TempDir(), "")
require.NoError(t, err)

err = cmdheartbeat.SendHeartbeats(v, offlineQueueFile.Name())
require.NoError(t, err)

assert.Eventually(t, func() bool { return numCalls == 1 }, time.Second, 50*time.Millisecond)
}

func setupTestServer() (string, *http.ServeMux, func()) {
router := http.NewServeMux()
srv := httptest.NewServer(router)

return srv.URL, router, func() { srv.Close() }
}

func setupTestGitBasic(t *testing.T) (fp string) {
tmpDir := t.TempDir()

tmpDir, err := realpath.Realpath(tmpDir)
require.NoError(t, err)

if runtime.GOOS == "windows" {
tmpDir = windows.FormatFilePath(tmpDir)
}

err = os.MkdirAll(filepath.Join(tmpDir, "wakatime-cli/src/pkg"), os.FileMode(int(0700)))
require.NoError(t, err)

tmpFile, err := os.Create(filepath.Join(tmpDir, "wakatime-cli/src/pkg/file.go"))
require.NoError(t, err)

defer tmpFile.Close()

err = os.Mkdir(filepath.Join(tmpDir, "wakatime-cli/.git"), os.FileMode(int(0700)))
require.NoError(t, err)

copyFile(t, "testdata/git_basic/config", filepath.Join(tmpDir, "wakatime-cli/.git/config"))
copyFile(t, "testdata/git_basic/HEAD", filepath.Join(tmpDir, "wakatime-cli/.git/HEAD"))

return tmpDir
}

func copyFile(t *testing.T, source, destination string) {
input, err := os.ReadFile(source)
require.NoError(t, err)

err = os.WriteFile(destination, input, 0600)
require.NoError(t, err)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"category": "debugging",
"entity": "%s",
"is_write": true,
"language": "Go",
"project": "%s",
"type": "file",
"time": 1585598059.1,
"user_agent": "%s"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"branch": "master",
"category": "debugging",
"entity": "%s",
"is_write": true,
"language": "Go",
"project": "%s",
"type": "file",
"time": 1585598059.1,
"user_agent": "%s"
}
]
1 change: 1 addition & 0 deletions cmd/heartbeat/testdata/git_basic/HEAD
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ref: refs/heads/master
1 change: 1 addition & 0 deletions cmd/heartbeat/testdata/git_basic/HEAD_DETACHED
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
f4f242d698fa07c298592a66d6546ac9b6b34d1e
1 change: 1 addition & 0 deletions cmd/heartbeat/testdata/git_basic/HEAD_WITH_SLASH
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ref: refs/heads/feature/detection
16 changes: 16 additions & 0 deletions cmd/heartbeat/testdata/git_basic/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = [email protected]:wakatime/wakatime-cli.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "develop"]
remote = origin
merge = refs/heads/develop
[user]
email = [email protected]
name = WakaTime
3 changes: 3 additions & 0 deletions cmd/params/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ var (
// nolint
matchAllRegex = regexp.MustCompile(".*")
// nolint
matchNoneRegex = regexp.MustCompile("a^")
// nolint
pluginRegex = regexp.MustCompile(`(?i)([a-z\/0-9.]+\s)?(?P<editor>[a-z-]+)\-wakatime\/[0-9.]+`)
// nolint
proxyRegex = regexp.MustCompile(`^((https?|socks5)://)?([^:@]+(:([^:@])+)?@)?[^:]+(:\d+)?$`)
Expand Down Expand Up @@ -1049,6 +1051,7 @@ func parseBoolOrRegexList(s string) ([]regex.Regex, error) {
switch {
case s == "":
case strings.ToLower(s) == "false":
patterns = []regex.Regex{matchNoneRegex}
case strings.ToLower(s) == "true":
patterns = []regex.Regex{matchAllRegex}
default:
Expand Down
2 changes: 1 addition & 1 deletion cmd/params/params_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func TestParseBoolOrRegexList(t *testing.T) {
},
"false string": {
Input: "false",
Expected: nil,
Expected: []regex.Regex{regexp.MustCompile("a^")},
},
"true string": {
Input: "true",
Expand Down
14 changes: 10 additions & 4 deletions cmd/params/params_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,9 @@ func TestLoadParams_SanitizeParams_HideBranchNames_False(t *testing.T) {
params, err := paramscmd.LoadHeartbeatParams(v)
require.NoError(t, err)

assert.Equal(t, paramscmd.SanitizeParams{}, params.Sanitize)
assert.Equal(t, paramscmd.SanitizeParams{
HideBranchNames: []regex.Regex{regex.MustCompile("a^")},
}, params.Sanitize)
})
}
}
Expand Down Expand Up @@ -1050,7 +1052,9 @@ func TestLoadParams_SanitizeParams_HideProjectNames_False(t *testing.T) {
params, err := paramscmd.LoadHeartbeatParams(v)
require.NoError(t, err)

assert.Equal(t, paramscmd.SanitizeParams{}, params.Sanitize)
assert.Equal(t, paramscmd.SanitizeParams{
HideProjectNames: []regex.Regex{regexp.MustCompile("a^")},
}, params.Sanitize)
})
}
}
Expand Down Expand Up @@ -1204,7 +1208,9 @@ func TestLoadParams_SanitizeParams_HideFileNames_False(t *testing.T) {
params, err := paramscmd.LoadHeartbeatParams(v)
require.NoError(t, err)

assert.Equal(t, paramscmd.SanitizeParams{}, params.Sanitize)
assert.Equal(t, paramscmd.SanitizeParams{
HideFileNames: []regex.Regex{regexp.MustCompile("a^")},
}, params.Sanitize)
})
}
}
Expand Down Expand Up @@ -1430,7 +1436,7 @@ func TestLoadParams_SubmodulesDisabled_False(t *testing.T) {
params, err := paramscmd.LoadHeartbeatParams(v)
require.NoError(t, err)

assert.Empty(t, params.Project.SubmodulesDisabled)
assert.Equal(t, params.Project.SubmodulesDisabled, []regex.Regex{regexp.MustCompile("a^")})
})
}
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/project/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (f File) Detect() (Result, bool, error) {

log.Debugf("wakatime project file found at: %s", fp)

lines, err := readFile(fp, 2)
lines, err := ReadFile(fp, 2)
if err != nil {
return Result{}, false, fmt.Errorf("error reading file: %s", err)
}
Expand All @@ -53,8 +53,8 @@ func fileExists(fp string) bool {
return err == nil || os.IsExist(err)
}

// readFile reads a file until max number of lines and return an array of lines.
func readFile(fp string, max int) ([]string, error) {
// ReadFile reads a file until max number of lines and return an array of lines.
func ReadFile(fp string, max int) ([]string, error) {
if fp == "" {
return nil, errors.New("filepath cannot be empty")
}
Expand Down
Loading

0 comments on commit 849d5c5

Please sign in to comment.