From a0c04215fdff4bb1860c6e3f7637f75e484cf91b Mon Sep 17 00:00:00 2001
From: Alan Hamlett <alan.hamlett@gmail.com>
Date: Thu, 26 Sep 2024 14:08:25 +0200
Subject: [PATCH] Prevent overwriting existing wakatime-project files

---
 cmd/run.go                |  2 +-
 pkg/offline/offline.go    |  2 +-
 pkg/project/file.go       | 15 ++++-----------
 pkg/project/git.go        | 12 ++++++------
 pkg/project/mercurial.go  |  4 ++--
 pkg/project/project.go    | 17 ++++++++++++++---
 pkg/project/subversion.go |  4 ++--
 pkg/project/tfvc.go       |  4 ++--
 8 files changed, 32 insertions(+), 28 deletions(-)

diff --git a/cmd/run.go b/cmd/run.go
index 7774e0a1..aaad374b 100644
--- a/cmd/run.go
+++ b/cmd/run.go
@@ -242,7 +242,7 @@ func SetupLogging(v *viper.Viper) (*logfile.Params, error) {
 			}
 		}
 
-		logFile, err = os.OpenFile(logfileParams.File, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
+		logFile, err = os.OpenFile(logfileParams.File, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) // nolint:gosec
 		if err != nil {
 			return nil, fmt.Errorf("error opening log file: %s", err)
 		}
diff --git a/pkg/offline/offline.go b/pkg/offline/offline.go
index f4ab8ab1..8687a83f 100644
--- a/pkg/offline/offline.go
+++ b/pkg/offline/offline.go
@@ -420,7 +420,7 @@ func openDB(filepath string) (db *bolt.DB, _ func(), err error) {
 		}
 	}()
 
-	db, err = bolt.Open(filepath, 0600, &bolt.Options{Timeout: 30 * time.Second})
+	db, err = bolt.Open(filepath, 0644, &bolt.Options{Timeout: 30 * time.Second})
 	if err != nil {
 		return nil, nil, fmt.Errorf("failed to open db file: %s", err)
 	}
diff --git a/pkg/project/file.go b/pkg/project/file.go
index 430714c1..b1bd5556 100644
--- a/pkg/project/file.go
+++ b/pkg/project/file.go
@@ -20,8 +20,8 @@ type File struct {
 // a given file. First line of .wakatime-project sets the project
 // name. Second line sets the current branch name.
 func (f File) Detect() (Result, bool, error) {
-	fp, ok := FindFileOrDirectory(f.Filepath, WakaTimeProjectFile)
-	if !ok {
+	fp, found := FindFileOrDirectory(f.Filepath, WakaTimeProjectFile)
+	if !found {
 		return Result{}, false, nil
 	}
 
@@ -33,11 +33,10 @@ func (f File) Detect() (Result, bool, error) {
 	}
 
 	result := Result{
-		Folder: filepath.Dir(fp),
+		Folder:  filepath.Dir(fp),
+		Project: filepath.Base(filepath.Dir(fp)),
 	}
 
-	result.Project = filepath.Base(filepath.Dir(fp))
-
 	if len(lines) > 0 {
 		result.Project = strings.TrimSpace(lines[0])
 	}
@@ -49,12 +48,6 @@ func (f File) Detect() (Result, bool, error) {
 	return result, true, nil
 }
 
-// fileOrDirExists checks if a file or directory exist.
-func fileOrDirExists(fp string) bool {
-	_, err := os.Stat(fp)
-	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) {
 	if fp == "" {
diff --git a/pkg/project/git.go b/pkg/project/git.go
index 915fe38c..bd761c28 100644
--- a/pkg/project/git.go
+++ b/pkg/project/git.go
@@ -62,8 +62,8 @@ func (g Git) Detect() (Result, bool, error) {
 	}
 
 	// Find for .git file or directory
-	dotGit, ok := FindFileOrDirectory(fp, ".git")
-	if !ok {
+	dotGit, found := FindFileOrDirectory(fp, ".git")
+	if !found {
 		return Result{}, false, nil
 	}
 
@@ -132,9 +132,9 @@ func (g Git) Detect() (Result, bool, error) {
 	}
 
 	// Find for .git/config file
-	gitConfigFile, ok := FindFileOrDirectory(fp, filepath.Join(".git", "config"))
+	gitConfigFile, found := FindFileOrDirectory(fp, filepath.Join(".git", "config"))
 
-	if ok {
+	if found {
 		gitDir := filepath.Dir(gitConfigFile)
 		projectDir := filepath.Join(gitDir, "..")
 
@@ -164,8 +164,8 @@ func findSubmodule(fp string, patterns []regex.Regex) (string, bool, error) {
 		return "", false, nil
 	}
 
-	gitConfigFile, ok := FindFileOrDirectory(fp, ".git")
-	if !ok {
+	gitConfigFile, found := FindFileOrDirectory(fp, ".git")
+	if !found {
 		return "", false, nil
 	}
 
diff --git a/pkg/project/mercurial.go b/pkg/project/mercurial.go
index b16bca04..c3402f03 100644
--- a/pkg/project/mercurial.go
+++ b/pkg/project/mercurial.go
@@ -24,8 +24,8 @@ func (m Mercurial) Detect() (Result, bool, error) {
 	}
 
 	// Find for .hg folder
-	hgDirectory, ok := FindFileOrDirectory(fp, ".hg")
-	if !ok {
+	hgDirectory, found := FindFileOrDirectory(fp, ".hg")
+	if !found {
 		return Result{}, false, nil
 	}
 
diff --git a/pkg/project/project.go b/pkg/project/project.go
index 71ddab04..4f9a42f2 100644
--- a/pkg/project/project.go
+++ b/pkg/project/project.go
@@ -309,6 +309,11 @@ func DetectWithRevControl(
 }
 
 func obfuscateProjectName(folder string) string {
+	// prevent overwriting existing project files, use Unknown Project instead
+	if fileOrDirExists(filepath.Join(folder, WakaTimeProjectFile)) {
+		return ""
+	}
+
 	project := generateProjectName()
 
 	err := Write(folder, project)
@@ -321,7 +326,7 @@ func obfuscateProjectName(folder string) string {
 
 // Write saves wakatime project file.
 func Write(folder, project string) error {
-	err := os.WriteFile(filepath.Join(folder, WakaTimeProjectFile), []byte(project+"\n"), 0600)
+	err := os.WriteFile(filepath.Join(folder, WakaTimeProjectFile), []byte(project+"\n"), 0644) // nolint:gosec
 	if err != nil {
 		return fmt.Errorf("failed to save wakatime project file: %s", err)
 	}
@@ -620,8 +625,8 @@ func CountSlashesInProjectFolder(directory string) int {
 	return strings.Count(directory, `/`)
 }
 
-// FindFileOrDirectory searches for a file or directory named `filename`.
-// Search starts in `directory` and will traverse through all parent directories.
+// FindFileOrDirectory searches current and all parent folders for a file or directory named `filename`.
+// Starts in `directory` and traverses through all parent directories.
 // `directory` may also be a file, and in that case will start from the file's directory.
 func FindFileOrDirectory(directory, filename string) (string, bool) {
 	i := 0
@@ -689,3 +694,9 @@ func FormatProjectFolder(fp string) string {
 
 	return formatted
 }
+
+// fileOrDirExists checks if a file or directory exist.
+func fileOrDirExists(fp string) bool {
+	_, err := os.Stat(fp)
+	return err == nil || os.IsExist(err)
+}
diff --git a/pkg/project/subversion.go b/pkg/project/subversion.go
index bcecb134..67955721 100644
--- a/pkg/project/subversion.go
+++ b/pkg/project/subversion.go
@@ -32,8 +32,8 @@ func (s Subversion) Detect() (Result, bool, error) {
 	}
 
 	// Find for .svn/wc.db file
-	svnConfigFile, ok := FindFileOrDirectory(fp, filepath.Join(".svn", "wc.db"))
-	if !ok {
+	svnConfigFile, found := FindFileOrDirectory(fp, filepath.Join(".svn", "wc.db"))
+	if !found {
 		return Result{}, false, nil
 	}
 
diff --git a/pkg/project/tfvc.go b/pkg/project/tfvc.go
index dbd772af..521929e7 100644
--- a/pkg/project/tfvc.go
+++ b/pkg/project/tfvc.go
@@ -26,8 +26,8 @@ func (t Tfvc) Detect() (Result, bool, error) {
 	}
 
 	// Find for tf/properties.tf1 file
-	tfDirectory, ok := FindFileOrDirectory(fp, filepath.Join(tfFolderName, "properties.tf1"))
-	if !ok {
+	tfDirectory, found := FindFileOrDirectory(fp, filepath.Join(tfFolderName, "properties.tf1"))
+	if !found {
 		return Result{}, false, nil
 	}