diff --git a/.frogbot/frogbot-config.yml b/.frogbot/frogbot-config.yml deleted file mode 100644 index b6845a99b..000000000 --- a/.frogbot/frogbot-config.yml +++ /dev/null @@ -1,5 +0,0 @@ -- params: - git: - repoName: jfrog-client-go - branches: - - dev \ No newline at end of file diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index a49d91bbf..4c288ef9b 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -15,7 +15,8 @@ jobs: - name: Static Code Analysis uses: golangci/golangci-lint-action@v3 with: - args: -E=errcheck,gosimple,govet,ineffassign,staticcheck,typecheck,unused,gocritic,asasalint,asciicheck,errchkjson,errname,exportloopref,forcetypeassert,makezero,nilerr,unparam,unconvert,wastedassign,usestdlibvars --timeout=5m + args: | + --timeout 5m --out-${NO_FUTURE}format colored-line-number --enable errcheck,gosimple,govet,ineffassign,staticcheck,typecheck,unused,gocritic,asasalint,asciicheck,errchkjson,exportloopref,forcetypeassert,makezero,nilerr,unparam,unconvert,wastedassign,usestdlibvars Go-Sec: diff --git a/.github/workflows/frogbot-fix.yml b/.github/workflows/frogbot-scan-and-fix.yml similarity index 67% rename from .github/workflows/frogbot-fix.yml rename to .github/workflows/frogbot-scan-and-fix.yml index 3a25fb26c..3f2679563 100644 --- a/.github/workflows/frogbot-fix.yml +++ b/.github/workflows/frogbot-scan-and-fix.yml @@ -1,8 +1,17 @@ -name: "Frogbot Fix" +name: "Frogbot Scan and Fix" on: push: + # Creating fix pull requests will be triggered by any push to one of the these branches. + # You can add or replace to any branch you want to open fix pull requests for. branches: - 'dev' + schedule: + # The job will run once a day at 00:00 GMT. + - cron: "0 0 * * *" +permissions: + contents: write + pull-requests: write + security-events: write jobs: frogbot: runs-on: ubuntu-latest diff --git a/artifactory/services/download.go b/artifactory/services/download.go index f14d0dee6..618e22063 100644 --- a/artifactory/services/download.go +++ b/artifactory/services/download.go @@ -381,7 +381,7 @@ func (ds *DownloadService) downloadFile(downloadFileDetails *httpclient.Download if bulkDownload { var resp *http.Response resp, err := ds.client.DownloadFileWithProgress(downloadFileDetails, logMsgPrefix, &httpClientsDetails, - downloadParams.IsExplode(), ds.Progress) + downloadParams.IsExplode(), downloadParams.IsBypassArchiveInspection(), ds.Progress) if err != nil { return err } @@ -547,7 +547,7 @@ func (ds *DownloadService) downloadFileIfNeeded(downloadPath, localPath, localFi if isEqual { log.Debug(logMsgPrefix, "File already exists locally.") if downloadParams.IsExplode() { - e = clientutils.ExtractArchive(localPath, localFileName, downloadData.Dependency.Name, logMsgPrefix) + e = clientutils.ExtractArchive(localPath, localFileName, downloadData.Dependency.Name, logMsgPrefix, downloadParams.IsBypassArchiveInspection()) } return e } @@ -589,14 +589,15 @@ type DownloadData struct { type DownloadParams struct { *utils.CommonParams - Symlink bool - ValidateSymlink bool - Flat bool - Explode bool - MinSplitSize int64 - SplitCount int - PublicGpgKey string - SkipChecksum bool + Symlink bool + ValidateSymlink bool + Flat bool + Explode bool + BypassArchiveInspection bool + MinSplitSize int64 + SplitCount int + PublicGpgKey string + SkipChecksum bool } func (ds *DownloadParams) IsFlat() bool { @@ -607,6 +608,10 @@ func (ds *DownloadParams) IsExplode() bool { return ds.Explode } +func (ds *DownloadParams) IsBypassArchiveInspection() bool { + return ds.BypassArchiveInspection +} + func (ds *DownloadParams) GetFile() *utils.CommonParams { return ds.CommonParams } diff --git a/go.mod b/go.mod index 85f6ea353..92dd09c78 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,8 @@ require ( github.com/go-git/go-git/v5 v5.6.1 github.com/golang-jwt/jwt/v4 v4.5.0 github.com/gookit/color v1.5.3 - github.com/jfrog/build-info-go v1.9.2 - github.com/jfrog/gofrog v1.2.5 + github.com/jfrog/build-info-go v1.9.3 + github.com/jfrog/gofrog v1.3.0 github.com/mholt/archiver/v3 v3.5.1 github.com/stretchr/testify v1.8.2 github.com/xanzy/ssh-agent v0.3.3 @@ -57,4 +57,4 @@ require ( // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20230418123708-71a0dbbcb331 -// replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.2.5-0.20221107113836-a4c9225c690e +// replace github.com/jfrog/gofrog => github.com/jfrog/gofrog dev diff --git a/go.sum b/go.sum index 2e9d2d0a1..058d7b788 100644 --- a/go.sum +++ b/go.sum @@ -55,10 +55,10 @@ github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -github.com/jfrog/build-info-go v1.9.2 h1:gSX9PH3whFcAMtM9dlPxRE7u9YuYcx8IkfVXQKRjWw0= -github.com/jfrog/build-info-go v1.9.2/go.mod h1:hHXyLsG0SW1jQa4g6q8x2LGAvvX/MMqWVFTcIUAF2PI= -github.com/jfrog/gofrog v1.2.5 h1:jCgJC0iGQ8bU7jCC+YEFJTNINyngApIrhd8BjZAVRIE= -github.com/jfrog/gofrog v1.2.5/go.mod h1:o00tSRff6IapTgaCMuX1Cs9MH08Y1JqnsKgRtx91Gc4= +github.com/jfrog/build-info-go v1.9.3 h1:ZpVcNM4hH+r6dK0ERdSNaizuZALPgSdE29Da1Iki1fo= +github.com/jfrog/build-info-go v1.9.3/go.mod h1:GbuFS+viHCKZYx9nWHYu7ab1DgQkFdtVN3BJPUNb2D4= +github.com/jfrog/gofrog v1.3.0 h1:o4zgsBZE4QyDbz2M7D4K6fXPTBJht+8lE87mS9bw7Gk= +github.com/jfrog/gofrog v1.3.0/go.mod h1:IFMc+V/yf7rA5WZ74CSbXe+Lgf0iApEQLxRZVzKRUR0= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= diff --git a/http/httpclient/client.go b/http/httpclient/client.go index 7281c39dc..2baa213a1 100644 --- a/http/httpclient/client.go +++ b/http/httpclient/client.go @@ -326,31 +326,31 @@ func (jc *HttpClient) ReadRemoteFile(downloadPath string, httpClientsDetails htt // Bulk downloads a file. // You may implement the log.Progress interface, or pass nil to run without progress display. func (jc *HttpClient) DownloadFileWithProgress(downloadFileDetails *DownloadFileDetails, logMsgPrefix string, - httpClientsDetails httputils.HttpClientDetails, isExplode bool, progress ioutils.ProgressMgr) (*http.Response, error) { - resp, _, err := jc.downloadFile(downloadFileDetails, logMsgPrefix, true, httpClientsDetails, isExplode, progress) + httpClientsDetails httputils.HttpClientDetails, isExplode, isBypassArchiveInspection bool, progress ioutils.ProgressMgr) (*http.Response, error) { + resp, _, err := jc.downloadFile(downloadFileDetails, logMsgPrefix, true, httpClientsDetails, isExplode, isBypassArchiveInspection, progress) return resp, err } // Bulk downloads a file. func (jc *HttpClient) DownloadFile(downloadFileDetails *DownloadFileDetails, logMsgPrefix string, - httpClientsDetails httputils.HttpClientDetails, isExplode bool) (*http.Response, error) { - return jc.DownloadFileWithProgress(downloadFileDetails, logMsgPrefix, httpClientsDetails, isExplode, nil) + httpClientsDetails httputils.HttpClientDetails, isExplode, bypassArchiveInspection bool) (*http.Response, error) { + return jc.DownloadFileWithProgress(downloadFileDetails, logMsgPrefix, httpClientsDetails, isExplode, bypassArchiveInspection, nil) } func (jc *HttpClient) DownloadFileNoRedirect(downloadPath, localPath, fileName string, httpClientsDetails httputils.HttpClientDetails) (*http.Response, string, error) { downloadFileDetails := &DownloadFileDetails{DownloadPath: downloadPath, LocalPath: localPath, FileName: fileName} - return jc.downloadFile(downloadFileDetails, "", false, httpClientsDetails, false, nil) + return jc.downloadFile(downloadFileDetails, "", false, httpClientsDetails, false, false, nil) } func (jc *HttpClient) downloadFile(downloadFileDetails *DownloadFileDetails, logMsgPrefix string, followRedirect bool, - httpClientsDetails httputils.HttpClientDetails, isExplode bool, progress ioutils.ProgressMgr) (resp *http.Response, redirectUrl string, err error) { + httpClientsDetails httputils.HttpClientDetails, isExplode, bypassArchiveInspection bool, progress ioutils.ProgressMgr) (resp *http.Response, redirectUrl string, err error) { retryExecutor := utils.RetryExecutor{ MaxRetries: jc.retries, RetriesIntervalMilliSecs: jc.retryWaitMilliSecs, ErrorMessage: fmt.Sprintf("Failure occurred while downloading %s", downloadFileDetails.DownloadPath), LogMsgPrefix: logMsgPrefix, ExecutionHandler: func() (bool, error) { - resp, redirectUrl, err = jc.doDownloadFile(downloadFileDetails, logMsgPrefix, followRedirect, httpClientsDetails, isExplode, progress) + resp, redirectUrl, err = jc.doDownloadFile(downloadFileDetails, logMsgPrefix, followRedirect, httpClientsDetails, isExplode, bypassArchiveInspection, progress) // In case followRedirect is 'false' and doDownloadFile did redirect, an error is returned and redirectUrl // receives the redirect address. This case should not retry. if err != nil && !followRedirect && redirectUrl != "" { @@ -379,7 +379,7 @@ func (jc *HttpClient) downloadFile(downloadFileDetails *DownloadFileDetails, log } func (jc *HttpClient) doDownloadFile(downloadFileDetails *DownloadFileDetails, logMsgPrefix string, followRedirect bool, - httpClientsDetails httputils.HttpClientDetails, isExplode bool, progress ioutils.ProgressMgr) (resp *http.Response, redirectUrl string, err error) { + httpClientsDetails httputils.HttpClientDetails, isExplode, bypassArchiveInspection bool, progress ioutils.ProgressMgr) (resp *http.Response, redirectUrl string, err error) { resp, redirectUrl, err = jc.sendGetForFileDownload(downloadFileDetails.DownloadPath, followRedirect, httpClientsDetails, "") if err != nil { return @@ -404,7 +404,7 @@ func (jc *HttpClient) doDownloadFile(downloadFileDetails *DownloadFileDetails, l // Extract archive. if isExplode { - err = utils.ExtractArchive(downloadFileDetails.LocalPath, downloadFileDetails.LocalFileName, downloadFileDetails.FileName, logMsgPrefix) + err = utils.ExtractArchive(downloadFileDetails.LocalPath, downloadFileDetails.LocalFileName, downloadFileDetails.FileName, logMsgPrefix, bypassArchiveInspection) } return } @@ -517,7 +517,7 @@ func (jc *HttpClient) DownloadFileConcurrently(flags ConcurrentDownloadFlags, lo } if flags.Explode { - if err = utils.ExtractArchive(flags.LocalPath, flags.LocalFileName, flags.FileName, logMsgPrefix); err != nil { + if err = utils.ExtractArchive(flags.LocalPath, flags.LocalFileName, flags.FileName, logMsgPrefix, flags.BypassArchiveInspection); err != nil { return } } @@ -798,14 +798,15 @@ type DownloadFileDetails struct { } type ConcurrentDownloadFlags struct { - FileName string - DownloadPath string - RelativePath string - LocalFileName string - LocalPath string - ExpectedSha1 string - FileSize int64 - SplitCount int - Explode bool - SkipChecksum bool + FileName string + DownloadPath string + RelativePath string + LocalFileName string + LocalPath string + ExpectedSha1 string + FileSize int64 + SplitCount int + Explode bool + BypassArchiveInspection bool + SkipChecksum bool } diff --git a/http/jfroghttpclient/client.go b/http/jfroghttpclient/client.go index 28b19d7b8..ba0a70fc3 100644 --- a/http/jfroghttpclient/client.go +++ b/http/jfroghttpclient/client.go @@ -118,17 +118,17 @@ func (rtc *JfrogHttpClient) ReadRemoteFile(downloadPath string, httpClientsDetai } func (rtc *JfrogHttpClient) DownloadFileWithProgress(downloadFileDetails *httpclient.DownloadFileDetails, logMsgPrefix string, - httpClientsDetails *httputils.HttpClientDetails, isExplode bool, progress ioutils.ProgressMgr) (resp *http.Response, err error) { + httpClientsDetails *httputils.HttpClientDetails, isExplode, bypassArchiveInspection bool, progress ioutils.ProgressMgr) (resp *http.Response, err error) { err = rtc.runPreRequestInterceptors(httpClientsDetails) if err != nil { return } - return rtc.httpClient.DownloadFileWithProgress(downloadFileDetails, logMsgPrefix, *httpClientsDetails, isExplode, progress) + return rtc.httpClient.DownloadFileWithProgress(downloadFileDetails, logMsgPrefix, *httpClientsDetails, isExplode, bypassArchiveInspection, progress) } func (rtc *JfrogHttpClient) DownloadFile(downloadFileDetails *httpclient.DownloadFileDetails, logMsgPrefix string, - httpClientsDetails *httputils.HttpClientDetails, isExplode bool) (resp *http.Response, err error) { - return rtc.DownloadFileWithProgress(downloadFileDetails, logMsgPrefix, httpClientsDetails, isExplode, nil) + httpClientsDetails *httputils.HttpClientDetails, isExplode, bypassArchiveInspection bool) (resp *http.Response, err error) { + return rtc.DownloadFileWithProgress(downloadFileDetails, logMsgPrefix, httpClientsDetails, isExplode, bypassArchiveInspection, nil) } func (rtc *JfrogHttpClient) DownloadFileConcurrently(flags httpclient.ConcurrentDownloadFlags, diff --git a/utils/archiveutils.go b/utils/archiveutils.go index bf3983f03..014828e78 100644 --- a/utils/archiveutils.go +++ b/utils/archiveutils.go @@ -1,21 +1,26 @@ package utils import ( - "github.com/jfrog/jfrog-client-go/utils/errorutils" - "github.com/jfrog/jfrog-client-go/utils/io/fileutils" - "github.com/jfrog/jfrog-client-go/utils/log" "os" "path/filepath" "strings" + + "github.com/jfrog/gofrog/unarchive" + "github.com/jfrog/jfrog-client-go/utils/errorutils" + "github.com/jfrog/jfrog-client-go/utils/log" ) // localPath - The path of the downloaded archive file. -// localFileName - name of the archive file. -// originFileName - name of the archive file in Artifactory. -// logMsgPrefix - prefix log message. +// localFileName - The name of the archive file. +// originFileName - The name of the archive file in Artifactory. +// logMsgPrefix - A prefix to the log message. +// bypassInspection - Set to true to bypass archive inspection against ZipSlip // Extract an archive file to the 'localPath'. -func ExtractArchive(localPath, localFileName, originFileName, logMsgPrefix string) error { - if !fileutils.IsSupportedArchive(originFileName) { +func ExtractArchive(localPath, localFileName, originFileName, logMsgPrefix string, bypassInspection bool) error { + unarchiver := &unarchive.Unarchiver{ + BypassInspection: bypassInspection, + } + if !unarchiver.IsSupportedArchive(originFileName) { return nil } extractionPath, err := getExtractionPath(localPath) @@ -37,13 +42,12 @@ func ExtractArchive(localPath, localFileName, originFileName, logMsgPrefix strin return err } log.Info(logMsgPrefix+"Extracting archive:", archivePath, "to", extractionPath) - return extract(archivePath, originFileName, extractionPath) + return errorutils.CheckError(extract(archivePath, originFileName, extractionPath, unarchiver)) } -func extract(localFilePath, originArchiveName, extractionPath string) error { - err := fileutils.Unarchive(localFilePath, originArchiveName, extractionPath) - if err != nil { - return err +func extract(localFilePath, originArchiveName, extractionPath string, unarchiver *unarchive.Unarchiver) error { + if err := unarchiver.Unarchive(localFilePath, originArchiveName, extractionPath); err != nil{ + return errorutils.CheckError(err) } // If the file was extracted successfully, remove it from the file system return errorutils.CheckError(os.Remove(localFilePath)) diff --git a/utils/io/fileutils/archive.go b/utils/io/fileutils/archive.go deleted file mode 100644 index 0e385900b..000000000 --- a/utils/io/fileutils/archive.go +++ /dev/null @@ -1,207 +0,0 @@ -package fileutils - -import ( - "encoding/json" - "fmt" - "io" - "os" - "path/filepath" - "strings" - - "github.com/jfrog/jfrog-client-go/utils/errorutils" - "github.com/mholt/archiver/v3" -) - -func IsSupportedArchive(filePath string) bool { - iArchiver, err := archiver.ByExtension(filePath) - if err != nil { - return false - } - _, ok := iArchiver.(archiver.Unarchiver) - return ok -} - -// The 'archiver' dependency includes an API called 'Unarchive' to extract archive files. This API uses the archive file -// extension to determine the archive type. -// We therefore need to use the file name as it was in Artifactory, and not the file name which was downloaded. To achieve this, -// we added a new implementation of the 'Unarchive' func and use it instead of the default one. -// localArchivePath - The local file path to extract the archive -// originArchiveName - The archive file name -// destinationPath - The extraction destination directory -func Unarchive(localArchivePath, originArchiveName, destinationPath string) error { - archive, err := byExtension(originArchiveName) - if err != nil { - return err - } - u, ok := archive.(archiver.Unarchiver) - if !ok { - return errorutils.CheckErrorf("format specified by source filename is not an archive format: " + originArchiveName) - } - if err = inspectArchive(archive, localArchivePath, destinationPath); err != nil { - return err - } - return u.Unarchive(localArchivePath, destinationPath) -} - -// Instead of using 'archiver.byExtension' that by default sets OverwriteExisting to false, we implement our own. -func byExtension(filename string) (interface{}, error) { - var ec interface{} - for _, c := range extCheckers { - if err := c.CheckExt(filename); err == nil { - ec = c - break - } - } - switch ec.(type) { - case *archiver.Rar: - archiveInstance := archiver.NewRar() - archiveInstance.OverwriteExisting = true - return archiveInstance, nil - case *archiver.Tar: - archiveInstance := archiver.NewTar() - archiveInstance.OverwriteExisting = true - return archiveInstance, nil - case *archiver.TarBrotli: - archiveInstance := archiver.NewTarBrotli() - archiveInstance.OverwriteExisting = true - return archiveInstance, nil - case *archiver.TarBz2: - archiveInstance := archiver.NewTarBz2() - archiveInstance.OverwriteExisting = true - return archiveInstance, nil - case *archiver.TarGz: - archiveInstance := archiver.NewTarGz() - archiveInstance.OverwriteExisting = true - return archiveInstance, nil - case *archiver.TarLz4: - archiveInstance := archiver.NewTarLz4() - archiveInstance.OverwriteExisting = true - return archiveInstance, nil - case *archiver.TarSz: - archiveInstance := archiver.NewTarSz() - archiveInstance.OverwriteExisting = true - return archiveInstance, nil - case *archiver.TarXz: - archiveInstance := archiver.NewTarXz() - archiveInstance.OverwriteExisting = true - return archiveInstance, nil - case *archiver.TarZstd: - archiveInstance := archiver.NewTarZstd() - archiveInstance.OverwriteExisting = true - return archiveInstance, nil - case *archiver.Zip: - archiveInstance := archiver.NewZip() - archiveInstance.OverwriteExisting = true - return archiveInstance, nil - case *archiver.Gz: - return archiver.NewGz(), nil - case *archiver.Bz2: - return archiver.NewBz2(), nil - case *archiver.Lz4: - return archiver.NewLz4(), nil - case *archiver.Snappy: - return archiver.NewSnappy(), nil - case *archiver.Xz: - return archiver.NewXz(), nil - case *archiver.Zstd: - return archiver.NewZstd(), nil - } - return nil, fmt.Errorf("format unrecognized by filename: %s", filename) -} - -var extCheckers = []archiver.ExtensionChecker{ - &archiver.TarBrotli{}, - &archiver.TarBz2{}, - &archiver.TarGz{}, - &archiver.TarLz4{}, - &archiver.TarSz{}, - &archiver.TarXz{}, - &archiver.TarZstd{}, - &archiver.Rar{}, - &archiver.Tar{}, - &archiver.Zip{}, - &archiver.Brotli{}, - &archiver.Gz{}, - &archiver.Bz2{}, - &archiver.Lz4{}, - &archiver.Snappy{}, - &archiver.Xz{}, - &archiver.Zstd{}, -} - -// Make sure the archive is free from Zip Slip and Zip symlinks attacks -func inspectArchive(archive interface{}, localArchivePath, destinationDir string) error { - walker, ok := archive.(archiver.Walker) - if !ok { - return errorutils.CheckErrorf("couldn't inspect archive: " + localArchivePath) - } - return walker.Walk(localArchivePath, func(archiveEntry archiver.File) error { - header, err := extractArchiveEntryHeader(archiveEntry) - if err != nil { - return err - } - if !isEntryInDestination(destinationDir, "", header.EntryPath) { - return errorutils.CheckErrorf( - "illegal path in archive: '%s'. To prevent Zip Slip exploit, the path can't lead to an entry outside '%s'", - header.EntryPath, destinationDir) - } - - if (archiveEntry.Mode()&os.ModeSymlink) != 0 || len(header.TargetLink) > 0 { - err = checkSymlinkEntry(header, archiveEntry, destinationDir) - } - return err - }) -} - -// Make sure the extraction path of the symlink entry target is under the destination dir -func checkSymlinkEntry(header *archiveHeader, archiveEntry archiver.File, destinationDir string) error { - targetLinkPath := header.TargetLink - if targetLinkPath == "" { - // The link destination path is not always in the archive header - // In that case, we will look at the link content to get the link destination path - content, err := io.ReadAll(archiveEntry.ReadCloser) - if err != nil { - return errorutils.CheckError(err) - } - targetLinkPath = string(content) - } - - if !isEntryInDestination(destinationDir, filepath.Dir(header.EntryPath), targetLinkPath) { - return errorutils.CheckErrorf( - "illegal link path in archive: '%s'. To prevent Zip Slip Symlink exploit, the path can't lead to an entry outside '%s'", - targetLinkPath, destinationDir) - } - return nil -} - -// Make sure the extraction path of the archive entry is under the destination dir -func isEntryInDestination(destinationDir, entryDirInArchive, pathInArchive string) bool { - // If pathInArchive starts with '/' and we are on Windows, the path is illegal - pathInArchive = strings.TrimSpace(pathInArchive) - if os.IsPathSeparator('\\') && strings.HasPrefix(pathInArchive, "/") { - return false - } - - pathInArchive = filepath.Clean(pathInArchive) - if !filepath.IsAbs(pathInArchive) { - // If path is relative, concatenate it to the destination dir - pathInArchive = filepath.Join(destinationDir, entryDirInArchive, pathInArchive) - } - return strings.HasPrefix(pathInArchive, destinationDir) -} - -// Extract the header of the archive entry -func extractArchiveEntryHeader(f archiver.File) (*archiveHeader, error) { - headerBytes, err := json.Marshal(f.Header) - if err != nil { - return nil, err - } - archiveHeader := &archiveHeader{} - err = json.Unmarshal(headerBytes, archiveHeader) - return archiveHeader, err -} - -type archiveHeader struct { - EntryPath string `json:"Name,omitempty"` - TargetLink string `json:"Linkname,omitempty"` -} diff --git a/utils/io/fileutils/archive_test.go b/utils/io/fileutils/archive_test.go deleted file mode 100644 index d9da14a8d..000000000 --- a/utils/io/fileutils/archive_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package fileutils - -import ( - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestUnarchive(t *testing.T) { - tests := []string{"zip", "tar", "tar.gz"} - for _, extension := range tests { - t.Run(extension, func(t *testing.T) { - // Create temp directory - tmpDir, createTempDirCallback := createTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - // Run unarchive on archive created on Unix - err := runUnarchive("unix."+extension, "archives", filepath.Join(tmpDir, "unix")) - assert.NoError(t, err) - assert.FileExists(t, filepath.Join(tmpDir, "unix", "link")) - assert.FileExists(t, filepath.Join(tmpDir, "unix", "dir", "file")) - - // Run unarchive on archive created on Windows - err = runUnarchive("win."+extension, "archives", filepath.Join(tmpDir, "win")) - assert.NoError(t, err) - assert.FileExists(t, filepath.Join(tmpDir, "win", "link.lnk")) - assert.FileExists(t, filepath.Join(tmpDir, "win", "dir", "file.txt")) - }) - } -} - -func TestUnarchiveSymlink(t *testing.T) { - tests := []string{"zip", "tar", "tar.gz"} - for _, extension := range tests { - t.Run(extension, func(t *testing.T) { - // Create temp directory - tmpDir, createTempDirCallback := createTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - - // Run unarchive - err := runUnarchive("softlink-rel."+extension, "archives", tmpDir) - assert.NoError(t, err) - assert.FileExists(t, filepath.Join(tmpDir, "softlink-rel", "a", "softlink-rel")) - assert.FileExists(t, filepath.Join(tmpDir, "softlink-rel", "b", "c", "d", "file")) - }) - } -} - -func TestUnarchiveZipSlip(t *testing.T) { - tests := []struct { - testType string - archives []string - errorSuffix string - }{ - {"rel", []string{"zip", "tar", "tar.gz"}, "illegal path in archive: '../file'"}, - {"abs", []string{"tar", "tar.gz"}, "illegal path in archive: '/tmp/bla/file'"}, - {"softlink-abs", []string{"zip", "tar", "tar.gz"}, "illegal link path in archive: '/tmp/bla/file'"}, - {"softlink-rel", []string{"zip", "tar", "tar.gz"}, "illegal link path in archive: '../../file'"}, - {"hardlink-tilde", []string{"tar", "tar.gz"}, "walking hardlink: illegal link path in archive: '~/../../../../../../../../../Users/Shared/sharedFile.txt'"}, - } - for _, test := range tests { - t.Run(test.testType, func(t *testing.T) { - // Create temp directory - tmpDir, createTempDirCallback := createTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - for _, archive := range test.archives { - // Unarchive and make sure an error returns - err := runUnarchive(test.testType+"."+archive, "zipslip", tmpDir) - assert.Error(t, err) - assert.Contains(t, err.Error(), test.errorSuffix) - } - }) - } -} - -func runUnarchive(archiveFileName, sourceDir, targetDir string) error { - return Unarchive(filepath.Join("testdata", sourceDir, archiveFileName), archiveFileName, targetDir) -} - -func createTempDirWithCallbackAndAssert(t *testing.T) (string, func()) { - tempDirPath, err := CreateTempDir() - assert.NoError(t, err, "Couldn't create temp dir") - return tempDirPath, func() { - assert.NoError(t, RemoveTempDir(tempDirPath), "Couldn't remove temp dir") - } -} diff --git a/utils/io/fileutils/files.go b/utils/io/fileutils/files.go index ffbde3ffd..4024eb9d8 100644 --- a/utils/io/fileutils/files.go +++ b/utils/io/fileutils/files.go @@ -144,7 +144,7 @@ func GetFileAndDirFromPath(path string) (fileName, dir string) { func GetLocalPathAndFile(originalFileName, relativePath, targetPath string, flat bool, placeholdersUsed bool) (localTargetPath, fileName string) { targetFileName, targetDirPath := GetFileAndDirFromPath(targetPath) // Remove double slashes and double backslashes that may appear in the path - localTargetPath = targetDirPath + localTargetPath = filepath.Clean(targetDirPath) // When placeholders are used, the file path shouldn't be taken into account (or in other words, flat = true). if !flat && !placeholdersUsed { localTargetPath = filepath.Join(targetDirPath, relativePath) diff --git a/utils/io/fileutils/temp.go b/utils/io/fileutils/temp.go index 93d0d370e..274656071 100644 --- a/utils/io/fileutils/temp.go +++ b/utils/io/fileutils/temp.go @@ -44,6 +44,10 @@ func SetTempDirBase(dirPath string) { tempDirBase = dirPath } +func GetTempDirBase() string { + return tempDirBase +} + func RemoveTempDir(dirPath string) error { exists, err := IsDirExists(dirPath, false) if err != nil { diff --git a/utils/io/fileutils/testdata/archives/softlink-rel.tar b/utils/io/fileutils/testdata/archives/softlink-rel.tar deleted file mode 100644 index dc4189f42..000000000 Binary files a/utils/io/fileutils/testdata/archives/softlink-rel.tar and /dev/null differ diff --git a/utils/io/fileutils/testdata/archives/softlink-rel.tar.gz b/utils/io/fileutils/testdata/archives/softlink-rel.tar.gz deleted file mode 100644 index 69f5f50b5..000000000 Binary files a/utils/io/fileutils/testdata/archives/softlink-rel.tar.gz and /dev/null differ diff --git a/utils/io/fileutils/testdata/archives/softlink-rel.zip b/utils/io/fileutils/testdata/archives/softlink-rel.zip deleted file mode 100644 index 0168336dd..000000000 Binary files a/utils/io/fileutils/testdata/archives/softlink-rel.zip and /dev/null differ diff --git a/utils/io/fileutils/testdata/archives/unix.tar b/utils/io/fileutils/testdata/archives/unix.tar deleted file mode 100644 index 87fa0fdd9..000000000 Binary files a/utils/io/fileutils/testdata/archives/unix.tar and /dev/null differ diff --git a/utils/io/fileutils/testdata/archives/unix.tar.gz b/utils/io/fileutils/testdata/archives/unix.tar.gz deleted file mode 100644 index acd45a3d7..000000000 Binary files a/utils/io/fileutils/testdata/archives/unix.tar.gz and /dev/null differ diff --git a/utils/io/fileutils/testdata/archives/unix.zip b/utils/io/fileutils/testdata/archives/unix.zip deleted file mode 100644 index d490364d8..000000000 Binary files a/utils/io/fileutils/testdata/archives/unix.zip and /dev/null differ diff --git a/utils/io/fileutils/testdata/archives/win.tar b/utils/io/fileutils/testdata/archives/win.tar deleted file mode 100644 index 4629c3c8a..000000000 Binary files a/utils/io/fileutils/testdata/archives/win.tar and /dev/null differ diff --git a/utils/io/fileutils/testdata/archives/win.tar.gz b/utils/io/fileutils/testdata/archives/win.tar.gz deleted file mode 100644 index 8f47594a1..000000000 Binary files a/utils/io/fileutils/testdata/archives/win.tar.gz and /dev/null differ diff --git a/utils/io/fileutils/testdata/archives/win.zip b/utils/io/fileutils/testdata/archives/win.zip deleted file mode 100644 index 66128662c..000000000 Binary files a/utils/io/fileutils/testdata/archives/win.zip and /dev/null differ diff --git a/utils/io/fileutils/testdata/zipslip/abs.tar b/utils/io/fileutils/testdata/zipslip/abs.tar deleted file mode 100644 index 5324a6f44..000000000 Binary files a/utils/io/fileutils/testdata/zipslip/abs.tar and /dev/null differ diff --git a/utils/io/fileutils/testdata/zipslip/abs.tar.gz b/utils/io/fileutils/testdata/zipslip/abs.tar.gz deleted file mode 100644 index 2db3551fd..000000000 Binary files a/utils/io/fileutils/testdata/zipslip/abs.tar.gz and /dev/null differ diff --git a/utils/io/fileutils/testdata/zipslip/hardlink-tilde.tar b/utils/io/fileutils/testdata/zipslip/hardlink-tilde.tar deleted file mode 100644 index 1c84939a3..000000000 Binary files a/utils/io/fileutils/testdata/zipslip/hardlink-tilde.tar and /dev/null differ diff --git a/utils/io/fileutils/testdata/zipslip/hardlink-tilde.tar.gz b/utils/io/fileutils/testdata/zipslip/hardlink-tilde.tar.gz deleted file mode 100644 index d891049b3..000000000 Binary files a/utils/io/fileutils/testdata/zipslip/hardlink-tilde.tar.gz and /dev/null differ diff --git a/utils/io/fileutils/testdata/zipslip/rel.tar b/utils/io/fileutils/testdata/zipslip/rel.tar deleted file mode 100644 index 7980de6b3..000000000 Binary files a/utils/io/fileutils/testdata/zipslip/rel.tar and /dev/null differ diff --git a/utils/io/fileutils/testdata/zipslip/rel.tar.gz b/utils/io/fileutils/testdata/zipslip/rel.tar.gz deleted file mode 100644 index 7703558b6..000000000 Binary files a/utils/io/fileutils/testdata/zipslip/rel.tar.gz and /dev/null differ diff --git a/utils/io/fileutils/testdata/zipslip/rel.zip b/utils/io/fileutils/testdata/zipslip/rel.zip deleted file mode 100644 index 515b05eec..000000000 Binary files a/utils/io/fileutils/testdata/zipslip/rel.zip and /dev/null differ diff --git a/utils/io/fileutils/testdata/zipslip/softlink-abs.tar b/utils/io/fileutils/testdata/zipslip/softlink-abs.tar deleted file mode 100644 index e45fab4b3..000000000 Binary files a/utils/io/fileutils/testdata/zipslip/softlink-abs.tar and /dev/null differ diff --git a/utils/io/fileutils/testdata/zipslip/softlink-abs.tar.gz b/utils/io/fileutils/testdata/zipslip/softlink-abs.tar.gz deleted file mode 100644 index b5635faa6..000000000 Binary files a/utils/io/fileutils/testdata/zipslip/softlink-abs.tar.gz and /dev/null differ diff --git a/utils/io/fileutils/testdata/zipslip/softlink-abs.zip b/utils/io/fileutils/testdata/zipslip/softlink-abs.zip deleted file mode 100644 index 0b800a4d4..000000000 Binary files a/utils/io/fileutils/testdata/zipslip/softlink-abs.zip and /dev/null differ diff --git a/utils/io/fileutils/testdata/zipslip/softlink-rel.tar b/utils/io/fileutils/testdata/zipslip/softlink-rel.tar deleted file mode 100644 index 6e24b29d3..000000000 Binary files a/utils/io/fileutils/testdata/zipslip/softlink-rel.tar and /dev/null differ diff --git a/utils/io/fileutils/testdata/zipslip/softlink-rel.tar.gz b/utils/io/fileutils/testdata/zipslip/softlink-rel.tar.gz deleted file mode 100644 index ca0439a1d..000000000 Binary files a/utils/io/fileutils/testdata/zipslip/softlink-rel.tar.gz and /dev/null differ diff --git a/utils/io/fileutils/testdata/zipslip/softlink-rel.zip b/utils/io/fileutils/testdata/zipslip/softlink-rel.zip deleted file mode 100644 index 98ae13fbc..000000000 Binary files a/utils/io/fileutils/testdata/zipslip/softlink-rel.zip and /dev/null differ diff --git a/utils/utils.go b/utils/utils.go index 0d311f059..9519946fe 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -26,7 +26,7 @@ import ( const ( Development = "development" Agent = "jfrog-client-go" - Version = "1.28.2" + Version = "1.28.3" ) // In order to limit the number of items loaded from a reader into the memory, we use a buffers with this size limit.