diff --git a/commands/ci/artifact/artifact.go b/commands/ci/artifact/artifact.go index 964a3d0f92dd8f8aeb7f06576973f9c9d42d1356..e885552516f183c23571b54ddad3042197bbcca9 100644 --- a/commands/ci/artifact/artifact.go +++ b/commands/ci/artifact/artifact.go @@ -28,6 +28,12 @@ func ensurePathIsCreated(filename string) error { return nil } +// Read limit is 4GB +const ( + zipReadLimit int64 = 4 * 1024 * 1024 * 1024 + zipFileLimit int = 100000 +) + func sanitizeAssetName(asset string) string { if !strings.HasPrefix(asset, "/") { // Prefix the asset with "/" ensures that filepath.Clean removes all `/..` @@ -82,6 +88,12 @@ func NewCmdRun(f *cmdutils.Factory) *cobra.Command { path = path + "/" } + var written int64 = 0 + + if len(zipReader.File) > zipFileLimit { + return fmt.Errorf("zip archive includes too many files: limit is %d files", zipFileLimit) + } + for _, v := range zipReader.File { sanitizedAssetName := sanitizeAssetName(v.Name) @@ -105,6 +117,8 @@ func NewCmdRun(f *cmdutils.Factory) *cobra.Command { } defer srcFile.Close() + limitedReader := io.LimitReader(srcFile, zipReadLimit) + err = ensurePathIsCreated(destPath) if err != nil { return err @@ -120,9 +134,15 @@ func NewCmdRun(f *cmdutils.Factory) *cobra.Command { if err != nil { return err } - if _, err := io.Copy(dstFile, srcFile); err != nil { + var writtenPerFile int64 + if writtenPerFile, err = io.Copy(dstFile, limitedReader); err != nil { return err } + + written += writtenPerFile + if written >= zipReadLimit { + return fmt.Errorf("Extracted zip too large: limit is %d bytes", zipReadLimit) + } } } return nil