Vulnerability: Insecure path traversal in Git Trigger Source can lead to arbitrary file read ### Impact
A path traversal issue was found in the (g *GitArtifactReader).Read() API. Read() calls into (g *GitArtifactReader).readFromRepository() that opens and reads the file that contains the trigger resource definition:

```go
func (g *GitArtifactReader) readFromRepository(r *git.Repository, dir string)
```

No checks are made on this file at read time, which could lead an attacker to read files anywhere on the system. This could be achieved by either using symbolic links, or putting `../` in the path.

### Patches
A patch for this vulnerability has been released in the following Argo Events version:

v1.7.1

### Credits
Disclosed by [Ada Logics](https://adalogics.com/) in a security audit sponsored by CNCF and facilitated by OSTIF.

### For more information
Open an issue in the [Argo Events issue tracker](https://github.com/argoproj/argo-events/issues) or [discussions](https://github.com/argoproj/argo-events/discussions)
Join us on [Slack](https://argoproj.github.io/community/join-slack) in channel #argo-events
Vulnerability ID: GHSA-qpgx-64h2-gc3c
CWE: CWE-22
Severity score: 7.5
Project: https://github.com/argoproj/argo-events
Published: 2022-06-17
File: sensors/artifacts/git.go
Commit message: fix: git artifactory arbitrary file read issue (#1965)

Signed-off-by: Derek Wang <whynowy@gmail.com>
Commit author: Derek Wang

Vulnerable version (before fix):
 import ( 	"fmt" 	"io/ioutil" 	"os"  	"github.com/go-git/go-git/v5" 	"github.com/go-git/go-git/v5/config" var ( 		"refs/*:refs/*", 		"HEAD:refs/heads/HEAD", 	} )  type GitArtifactReader struct { type GitArtifactReader struct {  // NewGitReader returns a new git reader func NewGitReader(gitArtifact *v1alpha1.GitArtifact) (*GitArtifactReader, error) { 	return &GitArtifactReader{ 		artifact: gitArtifact, 	}, nil func (g *GitArtifactReader) readFromRepository(r *git.Repository, dir string) ([ 			return nil, fmt.Errorf("failed to pull latest updates. err: %+v", err) 		} 	}  	return ioutil.ReadFile(fmt.Sprintf("%s/%s", dir, g.artifact.FilePath)) }  func (g *GitArtifactReader) getBranchOrTag() *git.CheckoutOptions { func (g *GitArtifactReader) Read() ([]byte, error) { 	} 	return g.readFromRepository(r, cloneDir) }

Fixed version (after fix):
 import ( 	"fmt" 	"io/ioutil" 	"os" 	"path" 	"strings"  	"github.com/go-git/go-git/v5" 	"github.com/go-git/go-git/v5/config" var ( 		"refs/*:refs/*", 		"HEAD:refs/heads/HEAD", 	}  	notAllowedInPath = []string{"..", "~", "\\"} )  type GitArtifactReader struct { type GitArtifactReader struct {  // NewGitReader returns a new git reader func NewGitReader(gitArtifact *v1alpha1.GitArtifact) (*GitArtifactReader, error) { 	if gitArtifact == nil { 		return nil, fmt.Errorf("nil git artifact") 	} 	for _, na := range notAllowedInPath { 		if strings.Contains(gitArtifact.FilePath, na) { 			return nil, fmt.Errorf("%q is not allowed in the filepath", na) 		} 	}  	return &GitArtifactReader{ 		artifact: gitArtifact, 	}, nil func (g *GitArtifactReader) readFromRepository(r *git.Repository, dir string) ([ 			return nil, fmt.Errorf("failed to pull latest updates. err: %+v", err) 		} 	} 	filePath := fmt.Sprintf("%s/%s", dir, g.artifact.FilePath) 	// symbol link is not allowed due to security concern 	isSymbolLink, err := isSymbolLink(filePath) 	if err != nil { 		return nil, err 	} 	if isSymbolLink { 		return nil, fmt.Errorf("%q is a symbol link which is not allowed", g.artifact.FilePath) 	} 	return ioutil.ReadFile(filePath) }  func (g *GitArtifactReader) getBranchOrTag() *git.CheckoutOptions { func (g *GitArtifactReader) Read() ([]byte, error) { 	} 	return g.readFromRepository(r, cloneDir) }  func isSymbolLink(filepath string) (bool, error) { 	fi, err := os.Lstat(path.Clean(filepath)) 	if err != nil { 		return false, err 	} 	if fi.Mode()&os.ModeSymlink != 0 { 		return true, nil 	} 	return false, nil }

Patch:
patch: @@ -20,6 +20,8 @@ import (
 	"fmt"
 	"io/ioutil"
 	"os"
+	"path"
+	"strings"
 
 	"github.com/go-git/go-git/v5"
 	"github.com/go-git/go-git/v5/config"
@@ -44,6 +46,8 @@ var (
 		"refs/*:refs/*",
 		"HEAD:refs/heads/HEAD",
 	}
+
+	notAllowedInPath = []string{"..", "~", "\\"}
 )
 
 type GitArtifactReader struct {
@@ -52,6 +56,15 @@ type GitArtifactReader struct {
 
 // NewGitReader returns a new git reader
 func NewGitReader(gitArtifact *v1alpha1.GitArtifact) (*GitArtifactReader, error) {
+	if gitArtifact == nil {
+		return nil, fmt.Errorf("nil git artifact")
+	}
+	for _, na := range notAllowedInPath {
+		if strings.Contains(gitArtifact.FilePath, na) {
+			return nil, fmt.Errorf("%q is not allowed in the filepath", na)
+		}
+	}
+
 	return &GitArtifactReader{
 		artifact: gitArtifact,
 	}, nil
@@ -176,8 +189,16 @@ func (g *GitArtifactReader) readFromRepository(r *git.Repository, dir string) ([
 			return nil, fmt.Errorf("failed to pull latest updates. err: %+v", err)
 		}
 	}
-
-	return ioutil.ReadFile(fmt.Sprintf("%s/%s", dir, g.artifact.FilePath))
+	filePath := fmt.Sprintf("%s/%s", dir, g.artifact.FilePath)
+	// symbol link is not allowed due to security concern
+	isSymbolLink, err := isSymbolLink(filePath)
+	if err != nil {
+		return nil, err
+	}
+	if isSymbolLink {
+		return nil, fmt.Errorf("%q is a symbol link which is not allowed", g.artifact.FilePath)
+	}
+	return ioutil.ReadFile(filePath)
 }
 
 func (g *GitArtifactReader) getBranchOrTag() *git.CheckoutOptions {
@@ -241,3 +262,14 @@ func (g *GitArtifactReader) Read() ([]byte, error) {
 	}
 	return g.readFromRepository(r, cloneDir)
 }
+
+func isSymbolLink(filepath string) (bool, error) {
+	fi, err := os.Lstat(path.Clean(filepath))
+	if err != nil {
+		return false, err
+	}
+	if fi.Mode()&os.ModeSymlink != 0 {
+		return true, nil
+	}
+	return false, nil
+}
path: sensors/artifacts/git.go