go.lang.security.zip.path-traversal-inside-zip-extraction

Verifed by r2c
Community Favorite
profile photo of returntocorpreturntocorp
Author
156,963
Download Count*

File traversal when extracting zip archive

Run Locally

Run in CI

Defintion

rules:
  - id: path-traversal-inside-zip-extraction
    message: File traversal when extracting zip archive
    metadata:
      cwe: "CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path
        Traversal')"
      source_rule_url: https://github.com/securego/gosec/issues/205
      category: security
      technology:
        - go
      confidence: MEDIUM
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
    languages:
      - go
    severity: WARNING
    pattern: |
      reader, $ERR := zip.OpenReader($ARCHIVE)
      ...
      for _, $FILE := range reader.File {
        ...
        path := filepath.Join($TARGET, $FILE.Name)
        ...
      }

Examples

zip.go

package unzip

import (
	"archive/zip"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
)

func unzip(archive, target string) error {
	// ruleid: path-traversal-inside-zip-extraction
	reader, err := zip.OpenReader(archive)
	if err != nil {
		return err
	}

	if err := os.MkdirAll(target, 0750); err != nil {
		return err
	}

	for _, file := range reader.File {
		path := filepath.Join(target, file.Name)
		if file.FileInfo().IsDir() {
			os.MkdirAll(path, file.Mode()) // #nosec
			continue
		}

		fileReader, err := file.Open()
		if err != nil {
			return err
		}
		defer fileReader.Close()

		targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
		if err != nil {
			return err
		}
		defer targetFile.Close()

		if _, err := io.Copy(targetFile, fileReader); err != nil {
			return err
		}
	}

	return nil
}

func unzip_good() {
	// Open a zip archive for reading.
	r, err := zip.OpenReader("testdata/readme.zip")
	if err != nil {
		log.Fatal(err)
	}
	defer r.Close()
	// Iterate through the files in the archive,
	// printing some of their contents.
	for _, f := range r.File {
		fmt.Printf("Contents of %s:\n", f.Name)
		rc, err := f.Open()
		if err != nil {
			log.Fatal(err)
		}
		_, err = io.CopyN(os.Stdout, rc, 68)
		if err != nil {
			log.Fatal(err)
		}
		rc.Close()
		fmt.Println()
	}
	// Output:
	// Contents of README:
	// This is the source code repository for the Go programming language.
}