diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 70cc66a..6e85c63 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -7,6 +7,8 @@ on: branches: [ master ] schedule: - cron: '0 0 * * 0' # weekly +permissions: + contents: read jobs: diff --git a/download/download.go b/download/download.go index 7690879..a71c500 100644 --- a/download/download.go +++ b/download/download.go @@ -226,7 +226,12 @@ func ExtractPkg(src string) (dst string, err error) { } name := filepath.Clean(header.Name) - if strings.HasPrefix(name, ".."+string(os.PathSeparator)) { + absDst, err := filepath.Abs(dst) + if err != nil { + return "", err + } + absPath := filepath.Join(absDst, name) + if !strings.HasPrefix(absPath, absDst) { return "", fmt.Errorf("error unpacking package, file contains path traversal: %q", name) } diff --git a/googet.goospec b/googet.goospec index d71ca3c..4a6b99b 100644 --- a/googet.goospec +++ b/googet.goospec @@ -1,4 +1,4 @@ -{{$version := "3.3.0@0" -}} +{{$version := "3.3.1@0" -}} { "name": "googet", "version": "{{$version}}", @@ -15,6 +15,7 @@ "path": "install.ps1" }, "releaseNotes": [ + "3.3.1 - Fix: Prevent path traversal vulnerabilities in file unpacking and add workflow permissions.", "3.3.0 - Refactor: Update FindRepoLatest to prioritize repo priority, version, and architecture with lock support.", "3.3.0 - Refactor: Add file ownership conflict checks to prevent overwrites.", "3.2.1 - Refactor: Update GooDB.FetchPkg to accept goolib.PackageInfo.", diff --git a/testutil/testutil.go b/testutil/testutil.go index a493e83..cd3c3ce 100644 --- a/testutil/testutil.go +++ b/testutil/testutil.go @@ -12,6 +12,7 @@ import ( "net/http/httptest" "os" "path/filepath" + "strings" "testing" "time" @@ -65,7 +66,16 @@ func GenGoo(t *testing.T, dir, dst string, ps goolib.PkgSpec) goolib.RepoSpec { func ServeGoo(t *testing.T, dir string) *httptest.Server { t.Helper() return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - f, err := os.Open(filepath.Join(dir, r.URL.Path)) + absDir, err := filepath.Abs(dir) + if err != nil { + t.Fatal(err) + } + absPath, err := filepath.Abs(filepath.Join(absDir, r.URL.Path)) + if err != nil || !strings.HasPrefix(absPath, absDir) { + http.Error(w, "Invalid file name", http.StatusBadRequest) + return + } + f, err := os.Open(absPath) if err != nil { t.Logf("couldn't find file: %v", r.URL.Path) http.Error(w, "couldn't find requested file", http.StatusNotFound)