github.com/hermo/npmi-go@v0.10.1/pkg/archive/tar_test.go (about)

     1  package archive
     2  
     3  import (
     4  	"archive/tar"
     5  	"bytes"
     6  	"compress/gzip"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"path"
    11  	"path/filepath"
    12  	"runtime"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  )
    17  
    18  func getBaseDir() string {
    19  	_, filename, _, ok := runtime.Caller(0)
    20  	if !ok {
    21  		panic("No caller information")
    22  	}
    23  	return path.Dir(filename)
    24  }
    25  
    26  func Test_ExtractFilesNormal(t *testing.T) {
    27  	var buf bytes.Buffer
    28  	gzw := gzip.NewWriter(&buf)
    29  	tw := tar.NewWriter(gzw)
    30  	mDate := time.Date(2021, time.September, 6, 11, 27, 4, 0, time.UTC)
    31  	zeroDate := time.Time{}
    32  
    33  	tarContents := []struct {
    34  		Name    string
    35  		Content string
    36  		Date    time.Time
    37  		Mode    os.FileMode
    38  		Type    byte
    39  	}{
    40  		{"root.txt", "rootfile", mDate, 0655, tar.TypeReg},
    41  		{"somedir", "", mDate, 0700, tar.TypeDir},
    42  		{"somedir/sub_link.txt", "sub.txt", zeroDate, 0655, tar.TypeSymlink}, // Link created before actual file on purpose
    43  		{"somedir/sub.txt", "subfile", mDate, 0644, tar.TypeReg},
    44  		{"sub_link.txt", "somedir/sub.txt", zeroDate, 0650, tar.TypeSymlink},
    45  		{"somedir/root_link.txt", "../root.txt", zeroDate, 0666, tar.TypeSymlink},
    46  	}
    47  	// Number of files/links expected in archive
    48  	wantManifestLen := 5
    49  
    50  	wantWarnings := 0
    51  
    52  	for _, f := range tarContents {
    53  		data := []byte(f.Content)
    54  		hdr := tar.Header{
    55  			Format:   tar.FormatPAX,
    56  			Typeflag: f.Type,
    57  			Name:     f.Name,
    58  			ModTime:  f.Date,
    59  		}
    60  		if f.Type == tar.TypeReg {
    61  			hdr.Size = int64(len(data))
    62  			hdr.Mode = int64(f.Mode)
    63  		}
    64  		if f.Type == tar.TypeSymlink {
    65  			hdr.Linkname = f.Content
    66  			hdr.Mode = int64(f.Mode & os.ModePerm)
    67  		}
    68  
    69  		if f.Type == tar.TypeDir {
    70  			hdr.Mode = int64(f.Mode & 0777)
    71  		}
    72  
    73  		err := tw.WriteHeader(&hdr)
    74  		if err != nil {
    75  			t.Fatal(err)
    76  		}
    77  		if f.Type == tar.TypeReg {
    78  			_, err := tw.Write(data)
    79  			if err != nil {
    80  				t.Fatal(err)
    81  			}
    82  		}
    83  
    84  	}
    85  
    86  	tw.Close()
    87  	gzw.Close()
    88  
    89  	testDir, err := prepareTestDir()
    90  	if err != nil {
    91  		t.Fatalf("Can't create temporary test directory: %v", err)
    92  	}
    93  
    94  	defer removeTestDir(testDir)
    95  
    96  	err = os.Mkdir("extract", 0700)
    97  	if err != nil {
    98  		t.Fatalf("Could not create directory: %v", err)
    99  	}
   100  
   101  	err = os.Chdir("extract")
   102  	if err != nil {
   103  		t.Fatalf("Can't chdir to test directory: %v", err)
   104  	}
   105  
   106  	options := TarOptions{
   107  		AllowAbsolutePaths:   false,
   108  		AllowDoubleDotPaths:  false,
   109  		AllowLinksOutsideCwd: false,
   110  	}
   111  
   112  	manifest, warnings, err := Extract(&buf, &options)
   113  	if err != nil {
   114  		t.Fatalf("Extract failed: %v", err)
   115  	}
   116  
   117  	if len(warnings) != wantWarnings {
   118  		t.Fatalf("Expected %d warnings, got %d", wantWarnings, len(warnings))
   119  	}
   120  
   121  	if len(manifest) != wantManifestLen {
   122  		t.Fatalf("Manifest length=%d,want=%d", len(manifest), wantManifestLen)
   123  	}
   124  
   125  	for _, f := range tarContents {
   126  		switch f.Type {
   127  		case tar.TypeDir:
   128  			fi, err := os.Stat(f.Name)
   129  			if err != nil {
   130  				t.Fatal(err)
   131  			}
   132  			if fi.IsDir() != true {
   133  				t.Fatalf("%s should be a directory but is not", f.Name)
   134  			}
   135  			if (fi.Mode() & 0777) != f.Mode {
   136  				t.Fatalf("%s mode=%v, want=%v", f.Name, fi.Mode(), f.Mode)
   137  			}
   138  
   139  			if fi.ModTime().UTC() != f.Date {
   140  				t.Fatalf("%s mtime=%v, want=%v", f.Name, fi.ModTime().UTC(), f.Date)
   141  			}
   142  		case tar.TypeSymlink:
   143  			li, err := os.Lstat(f.Name)
   144  			if err != nil {
   145  				t.Fatal(err)
   146  			}
   147  
   148  			if !f.Date.IsZero() {
   149  				t.Fatal("Symlink timestamps are not supported and need to be zero")
   150  			}
   151  
   152  			target, err := os.Readlink(f.Name)
   153  			if err != nil {
   154  				t.Fatal(err)
   155  			}
   156  			if target != f.Content {
   157  				t.Fatalf("Link %s points to %s, want=%s", li.Name(), f.Content, target)
   158  			}
   159  		case tar.TypeReg:
   160  			fi, err := os.Stat(f.Name)
   161  			if err != nil {
   162  				t.Fatal(err)
   163  			}
   164  
   165  			if fi.Mode() != f.Mode {
   166  				t.Fatalf("%s mode=%v, want=%v", f.Name, fi.Mode(), f.Mode)
   167  			}
   168  
   169  			if fi.ModTime().UTC() != f.Date {
   170  				t.Fatalf("%s mtime=%v, want=%v", f.Name, fi.ModTime().UTC(), f.Date)
   171  			}
   172  		}
   173  	}
   174  }
   175  
   176  func Test_ExtractFilesEvil(t *testing.T) {
   177  	tests := []struct {
   178  		Name           string
   179  		Content        string
   180  		Type           byte
   181  		WantedWarnings int
   182  	}{
   183  		{"../evil.txt", "evil", tar.TypeReg, 0},
   184  		{"./../evil.txt", "evil", tar.TypeReg, 0},
   185  		{"/evil.txt", "evil", tar.TypeReg, 0},
   186  		{"C:/Users/Public/evil.txt", "evil", tar.TypeReg, 0},
   187  		{"C:|Users/Public/evil.txt", "evil", tar.TypeReg, 0},
   188  		{"C:\\Users\\Public\\evil2.txt", "evil2", tar.TypeReg, 0},
   189  		{"abs_link", "/etc/passwd", tar.TypeSymlink, 0},
   190  		{"outside_link", "../outside_cwd", tar.TypeSymlink, 0},
   191  	}
   192  
   193  	for _, tt := range tests {
   194  		t.Run(tt.Name, func(t *testing.T) {
   195  			testDir, err := prepareTestDir()
   196  			if err != nil {
   197  				t.Fatalf("Can't create temporary test directory: %v", err)
   198  			}
   199  
   200  			defer removeTestDir(testDir)
   201  
   202  			err = os.Mkdir("extract", 0700)
   203  			if err != nil {
   204  				t.Fatalf("Could not create directory: %v", err)
   205  			}
   206  
   207  			err = os.Chdir("extract")
   208  			if err != nil {
   209  				t.Fatalf("Can't chdir to test directory: %v", err)
   210  			}
   211  
   212  			var buf bytes.Buffer
   213  			gzw := gzip.NewWriter(&buf)
   214  			tw := tar.NewWriter(gzw)
   215  
   216  			data := []byte(tt.Content)
   217  			hdr := tar.Header{
   218  				Typeflag: tt.Type,
   219  				Name:     tt.Name,
   220  			}
   221  			if tt.Type == tar.TypeReg {
   222  				hdr.Size = int64(len(data))
   223  			}
   224  			if tt.Type == tar.TypeSymlink {
   225  				hdr.Linkname = tt.Content
   226  			}
   227  			err = tw.WriteHeader(&hdr)
   228  			if err != nil {
   229  				t.Fatal(err)
   230  			}
   231  			if tt.Type == tar.TypeReg {
   232  				_, err = tw.Write(data)
   233  				if err != nil {
   234  					t.Fatal(err)
   235  				}
   236  			}
   237  
   238  			tw.Close()
   239  			gzw.Close()
   240  
   241  			options := TarOptions{
   242  				AllowAbsolutePaths:   false,
   243  				AllowDoubleDotPaths:  false,
   244  				AllowLinksOutsideCwd: false,
   245  			}
   246  
   247  			_, warnings, err := Extract(&buf, &options)
   248  			if len(warnings) != tt.WantedWarnings {
   249  				t.Errorf("Expected %d warnings, got %d", tt.WantedWarnings, len(warnings))
   250  			}
   251  
   252  			if err == nil {
   253  				t.Errorf("Extract should have failed but did not: %s", tt.Name)
   254  			} else {
   255  				if !strings.Contains(err.Error(), "invalid path") {
   256  					t.Errorf("Unexpected error: %v", err)
   257  				}
   258  			}
   259  		})
   260  	}
   261  }
   262  
   263  // Some tests for disallowing bad symlinks
   264  func Test_CreateArchiveSymlinks(t *testing.T) {
   265  	tests := []struct {
   266  		Source       string
   267  		Target       string
   268  		IsEvil       bool
   269  		WarningCount int
   270  	}{
   271  		// Normal cases
   272  		{"hello2.txt", "hello.txt", false, 1},
   273  		{"hello_subdir_1.txt", "subdir/hello.txt", false, 1},
   274  		{"hello_subdir_2.txt", "subdir/.foo/hello.txt", false, 1},
   275  		{"subdir/hello_parent_1.txt", "../hello.txt", false, 1},
   276  		// Evil cases that are allowed by default for compatibility 🤦‍♂️
   277  		{"abs_1.txt", "/hello.txt", false, 2},
   278  		{"abs_2.txt", "/etc/passwd", false, 1},
   279  		{"subdir/evil_parent_0.txt", "../../hello.txt", false, 2},
   280  		{"evil_parent_1.txt", "../evil.txt", false, 2},
   281  		{"evil_parent_2.txt", "./../evil.txt", false, 2},
   282  		// Still evil
   283  		{"evil_abs_win_1.txt", "C:/Users/Public/evil.txt", true, 0},
   284  		{"evil_abs_win_2.txt", "C:|Users/Public/evil.txt", true, 0},
   285  		{"evil_abs_win_3.txt", "C:\\Users\\Public\\evil2.txt", true, 0},
   286  	}
   287  
   288  	for _, tt := range tests {
   289  		var testType string
   290  		if tt.IsEvil {
   291  			testType = "EVIL"
   292  		} else {
   293  			testType = "NORMAL"
   294  		}
   295  		testName := fmt.Sprintf("%s/%s", testType, tt.Source)
   296  
   297  		t.Run(testName, func(t *testing.T) {
   298  			testDir, err := prepareTestDir()
   299  			if err != nil {
   300  				t.Fatalf("Can't create temporary test directory: %v", err)
   301  			}
   302  
   303  			defer removeTestDir(testDir)
   304  
   305  			err = os.Mkdir("subdir", 0750)
   306  			if err != nil {
   307  				t.Fatalf("Can't create test subdir: %v", err)
   308  			}
   309  
   310  			err = os.Symlink(tt.Target, tt.Source)
   311  			if err != nil {
   312  				t.Fatalf("Can't create test symlink: %v", err)
   313  			}
   314  
   315  			options := TarOptions{
   316  				AllowAbsolutePaths:   true,
   317  				AllowDoubleDotPaths:  true,
   318  				AllowLinksOutsideCwd: true,
   319  			}
   320  			warnings, err := Create("temp.tgz", ".", &options)
   321  
   322  			if len(warnings) != tt.WarningCount {
   323  				t.Errorf("Expected %d warnings, got %d", tt.WarningCount, len(warnings))
   324  				fmt.Printf("[%s] Warnings: %v\n", testName, warnings)
   325  			}
   326  
   327  			if tt.IsEvil {
   328  				if err == nil {
   329  					t.Fatalf("Evil symlink (%s -> %s) should have failed but did not\n", tt.Source, tt.Target)
   330  				} else {
   331  					if !strings.Contains(err.Error(), "invalid path") {
   332  						t.Fatalf("Unexpected error when creating evil symlink (%s -> %s): %v", tt.Source, tt.Target, err)
   333  					}
   334  				}
   335  			} else {
   336  				if err != nil {
   337  					if strings.Contains(err.Error(), "invalid path") {
   338  						t.Fatalf("Normal symlink (%s -> %s) failed: %v", tt.Source, tt.Target, err)
   339  					} else {
   340  						t.Fatalf("Unexpected error when creating normal symlink (%s -> %s): %v", tt.Source, tt.Target, err)
   341  					}
   342  				}
   343  			}
   344  		})
   345  	}
   346  }
   347  
   348  func Test_CreateArchive_DisallowSymlinksOutsideCwd(t *testing.T) {
   349  	tests := []struct {
   350  		Source       string
   351  		Target       string
   352  		IsEvil       bool
   353  		WarningCount int
   354  	}{
   355  		// Normal cases
   356  		{"hello2.txt", "hello.txt", false, 1},
   357  		{"hello_subdir_1.txt", "subdir/hello.txt", false, 1},
   358  		{"hello_subdir_2.txt", "subdir/.foo/hello.txt", false, 1},
   359  		{"subdir/hello_parent_1.txt", "../hello.txt", false, 1},
   360  		// Evil cases
   361  		{"subdir/evil_parent_0.txt", "../../hello.txt", true, 0},
   362  		{"evil_parent_1.txt", "../evil.txt", true, 0},
   363  		{"evil_parent_2.txt", "./../evil.txt", true, 0},
   364  		{"evil_abs_1.txt", "/evil.txt", true, 0},
   365  		{"evil_abs_2.txt", "/etc/passwd", true, 0},
   366  		{"evil_abs_win_1.txt", "C:/Users/Public/evil.txt", true, 0},
   367  		{"evil_abs_win_2.txt", "C:|Users/Public/evil.txt", true, 0},
   368  		{"evil_abs_win_3.txt", "C:\\Users\\Public\\evil2.txt", true, 0},
   369  	}
   370  
   371  	for _, tt := range tests {
   372  		var testType string
   373  		if tt.IsEvil {
   374  			testType = "EVIL"
   375  		} else {
   376  			testType = "NORMAL"
   377  		}
   378  		testName := fmt.Sprintf("%s/%s", testType, tt.Source)
   379  
   380  		t.Run(testName, func(t *testing.T) {
   381  			testDir, err := prepareTestDir()
   382  			if err != nil {
   383  				t.Fatalf("Can't create temporary test directory: %v", err)
   384  			}
   385  
   386  			defer removeTestDir(testDir)
   387  
   388  			err = os.Mkdir("subdir", 0750)
   389  			if err != nil {
   390  				t.Fatalf("Can't create test subdir: %v", err)
   391  			}
   392  
   393  			err = os.Symlink(tt.Target, tt.Source)
   394  			if err != nil {
   395  				t.Fatalf("Can't create test symlink: %v", err)
   396  			}
   397  
   398  			options := TarOptions{
   399  				AllowAbsolutePaths:   false,
   400  				AllowDoubleDotPaths:  true,
   401  				AllowLinksOutsideCwd: false,
   402  			}
   403  			warnings, err := Create("temp.tgz", ".", &options)
   404  
   405  			if len(warnings) != tt.WarningCount {
   406  				t.Errorf("Expected %d warnings, got only %d", tt.WarningCount, len(warnings))
   407  			}
   408  
   409  			if tt.IsEvil {
   410  				if err == nil {
   411  					t.Fatalf("Evil symlink (%s -> %s) should have failed but did not\n", tt.Source, tt.Target)
   412  				} else {
   413  					if !strings.Contains(err.Error(), "invalid path") {
   414  						t.Fatalf("Unexpected error when creating evil symlink (%s -> %s): %v", tt.Source, tt.Target, err)
   415  					}
   416  				}
   417  			} else {
   418  				if err != nil {
   419  					if strings.Contains(err.Error(), "invalid path") {
   420  						t.Fatalf("Normal symlink (%s -> %s) failed: %v", tt.Source, tt.Target, err)
   421  					} else {
   422  						t.Fatalf("Unexpected error when creating normal symlink (%s -> %s): %v", tt.Source, tt.Target, err)
   423  					}
   424  				}
   425  			}
   426  		})
   427  	}
   428  }
   429  
   430  // Create a temporary directory and chdir into it
   431  func prepareTestDir() (string, error) {
   432  	testBaseDir, err := filepath.Abs(fmt.Sprintf("%s/../../testdata", getBaseDir()))
   433  	if err != nil {
   434  		return "", fmt.Errorf("can't find test directory: %v", err)
   435  	}
   436  	testDir, err := os.MkdirTemp(testBaseDir, "test")
   437  	if err != nil {
   438  		return "", fmt.Errorf("can't create temporary test directory: %v", err)
   439  	}
   440  
   441  	err = os.Chdir(testDir)
   442  	if err != nil {
   443  		return "", fmt.Errorf("can't chdir to test directory: %v", err)
   444  	}
   445  
   446  	return testDir, nil
   447  }
   448  
   449  // Remove test directory after testing
   450  func removeTestDir(testDir string) {
   451  	err := os.Chdir(getBaseDir())
   452  	if err != nil {
   453  		return
   454  	}
   455  	err = os.RemoveAll(testDir)
   456  	if err != nil {
   457  		fmt.Printf("ERROR: could not remove temp directory: %v", err)
   458  	}
   459  }
   460  
   461  func BenchmarkExtract(b *testing.B) {
   462  	testDir, err := prepareTestDir()
   463  	if err != nil {
   464  		b.Fatalf("Can't create temporary test directory: %v", err)
   465  	}
   466  
   467  	defer removeTestDir(testDir)
   468  
   469  	err = os.Mkdir("extract", 0700)
   470  	if err != nil {
   471  		b.Fatalf("Could not create directory: %v", err)
   472  	}
   473  
   474  	err = os.Chdir("extract")
   475  	if err != nil {
   476  		b.Fatalf("Can't chdir to test directory: %v", err)
   477  	}
   478  	testArchive, err := filepath.Abs(fmt.Sprintf("%s/../../bench/extract/test.tgz", getBaseDir()))
   479  	if err != nil {
   480  		b.Fatal(err)
   481  	}
   482  
   483  	f, err := os.Open(testArchive)
   484  	if err != nil {
   485  		b.Fatal(err)
   486  	}
   487  	defer f.Close()
   488  
   489  	options := TarOptions{
   490  		AllowAbsolutePaths:   false,
   491  		AllowDoubleDotPaths:  false,
   492  		AllowLinksOutsideCwd: false,
   493  	}
   494  
   495  	for i := 0; i < b.N; i++ {
   496  		f.Seek(0, io.SeekStart)
   497  
   498  		_, _, err := Extract(f, &options)
   499  		if err != nil {
   500  			b.Fatalf("Extract failed: %v", err)
   501  		}
   502  	}
   503  }
   504  
   505  func TestBadPath(t *testing.T) {
   506  	tests := []struct {
   507  		allowDoubleDot     bool
   508  		allowAbsolutePaths bool
   509  		Path               string
   510  		Expected           bool
   511  	}{
   512  		// Evil inputs, double dots not allowed
   513  		{false, false, "/evil1.txt", true},
   514  		{false, false, `\evil1.txt`, true},
   515  		{false, false, "evil11..txt", true},
   516  		{false, false, "../evil2.txt", true},
   517  		{false, false, "C:/Users/Public/evil3.txt", true},
   518  		{false, false, "C:|Users/Public/evil4.txt", true},
   519  		{false, false, "<", true},
   520  		{false, false, "<foo", true},
   521  		{false, false, " <foo2", true},
   522  		{false, false, "bar>", true},
   523  		{false, false, "win\\separator", true},
   524  		{false, false, "\tbadpath", true},
   525  		{false, false, "\x01badpath", true},
   526  		{false, false, "!badpath", true},
   527  		{false, false, "@badpath", true},
   528  		{false, false, ":evil.txt", true},
   529  		{false, false, ";evil.txt", true},
   530  		{false, false, "!evil.txt", true},
   531  		{false, false, "@evil.txt", true},
   532  		{false, false, "#evil.txt", true},
   533  		{false, false, "$evil.txt", true},
   534  		{false, false, "%evil.txt", true},
   535  		{false, false, "^evil.txt", true},
   536  		{false, false, "&evil.txt", true},
   537  		{false, false, "*evil.txt", true},
   538  		{false, false, "(evil.txt", true},
   539  		{false, false, ")evil.txt", true},
   540  		{false, false, "+evil.txt", true},
   541  		{false, false, "=evil.txt", true},
   542  		{false, false, "{evil.txt", true},
   543  		{false, false, "}evil.txt", true},
   544  		{false, false, "[evil.txt", true},
   545  		{false, false, "]evil.txt", true},
   546  		{false, false, "|evil.txt", true},
   547  		{false, false, "\\evil.txt", true},
   548  		{false, false, "/evil.txt", true},
   549  		{false, false, "?evil.txt", true},
   550  		{false, false, "<evil.txt", true},
   551  		{false, false, ">evil.txt", true},
   552  		{false, false, ",evil.txt", true},
   553  		{false, false, "`evil.txt", true},
   554  		{false, false, "~evil.txt", true},
   555  		{false, false, " evil.txt", true},
   556  		{false, false, "\tevil.txt", true},
   557  		{false, false, "\x01evil.txt", true},
   558  		{false, false, "\x02evil.txt", true},
   559  		{false, false, "\x03evil.txt", true},
   560  		{false, false, "\x04evil.txt", true},
   561  		{false, false, "\x05evil.txt", true},
   562  		{false, false, "\x06evil.txt", true},
   563  		{false, false, "\x07evil.txt", true},
   564  		{false, false, "\x08evil.txt", true},
   565  		{false, false, "\x09evil.txt", true},
   566  		{false, false, "\x0aevil.txt", true},
   567  		{false, false, "\x0bevil.txt", true},
   568  		{false, false, "\x0cevil.txt", true},
   569  		{false, false, "\x0devil.txt", true},
   570  		{false, false, "\x0eevil.txt", true},
   571  		{false, false, "\x0fevil.txt", true},
   572  		{false, false, "\x10evil.txt", true},
   573  		{false, false, "\x11evil.txt", true},
   574  		{false, false, "\x12evil.txt", true},
   575  		{false, false, "\x13evil.txt", true},
   576  		{false, false, "\x14evil.txt", true},
   577  		{false, false, "\x15evil.txt", true},
   578  		{false, false, "\x16evil.txt", true},
   579  		{false, false, "\x17evil.txt", true},
   580  		{false, false, "\x18evil.txt", true},
   581  		{false, false, "\x19evil.txt", true},
   582  		{false, false, "\x1aevil.txt", true},
   583  		{false, false, "\x1bevil.txt", true},
   584  		{false, false, "\x1cevil.txt", true},
   585  		{false, false, "\x1devil.txt", true},
   586  		{false, false, "\x1eevil.txt", true},
   587  		{false, false, "\x1fevil.txt", true},
   588  		{false, false, " Spaceman", true},
   589  		{false, false, "C:\\Users\\Public\\evil5.txt", true},
   590  
   591  		// Evil inputs, double dots allowed
   592  		{true, false, "/../evil_double_dots_6.txt", true},
   593  		{true, false, "/../evil_double_dots_61..txt", true},
   594  
   595  		// Good inputs, double dots disallowed
   596  		{false, false, "kissa7.txt", false},
   597  		{false, false, "foo/bar//double_dots71.txt", false},
   598  		{false, false, "Hello dolly", false},
   599  
   600  		// Good inputs, double dots allowed
   601  		{true, false, "../double_dots8.txt", false},
   602  		{true, false, "double_dots9..txt", false},
   603  
   604  		// Good inputs, double dots disallowed, absolute paths allowed
   605  		{false, true, "/kissa.txt", false},
   606  
   607  		// Evil inputs, double dots disallowed, absolute paths allowed
   608  		{false, true, "../kissa.txt", true},
   609  		{false, true, "/../kissa.txt", true},
   610  
   611  		// Good inputs, double dots allowed, absolute paths allowed
   612  		{true, true, "/kissa.txt", false},
   613  		{true, true, "../kissa.txt", false},
   614  		{true, true, "/../kissa.txt", false},
   615  	}
   616  	for _, tt := range tests {
   617  		t.Run(fmt.Sprintf("allowDoubleDots:%v,Path:%s", tt.allowDoubleDot, tt.Path), func(t *testing.T) {
   618  			bp := NewBadPath(tt.allowDoubleDot, tt.allowAbsolutePaths)
   619  			if bp.IsBad(tt.Path) != tt.Expected {
   620  				t.Errorf("IsBad(%s) did not return %v", tt.Path, tt.Expected)
   621  			}
   622  		})
   623  	}
   624  }
   625  
   626  func BenchmarkBadPath(b *testing.B) {
   627  	bp := NewBadPath(false, false)
   628  	path := "node_modules/foo_bar/baz.js/somepath"
   629  	for i := 0; i < b.N; i++ {
   630  		bp.IsBad(path)
   631  	}
   632  }
   633  
   634  func BenchmarkCreate(b *testing.B) {
   635  	testDir, err := prepareTestDir()
   636  	if err != nil {
   637  		b.Fatalf("Can't create temporary test directory: %v", err)
   638  	}
   639  
   640  	defer removeTestDir(testDir)
   641  
   642  	err = os.Mkdir("compress", 0700)
   643  	if err != nil {
   644  		b.Fatalf("Could not create directory: %v", err)
   645  	}
   646  
   647  	err = os.Chdir("compress")
   648  	if err != nil {
   649  		b.Fatalf("Can't chdir to test directory: %v", err)
   650  	}
   651  	testArchive, err := filepath.Abs(fmt.Sprintf("%s/../../bench/extract/test.tgz", getBaseDir()))
   652  	if err != nil {
   653  		b.Fatal(err)
   654  	}
   655  
   656  	f, err := os.Open(testArchive)
   657  	if err != nil {
   658  		b.Fatal(err)
   659  	}
   660  	options := TarOptions{
   661  		AllowAbsolutePaths:   false,
   662  		AllowDoubleDotPaths:  false,
   663  		AllowLinksOutsideCwd: false,
   664  	}
   665  
   666  	_, _, err = Extract(f, &options)
   667  	if err != nil {
   668  		b.Fatalf("Extract failed: %v", err)
   669  	}
   670  	f.Close()
   671  
   672  	for i := 0; i < b.N; i++ {
   673  		warnings, err := Create("compressed.tgz", "node_modules", &options)
   674  		if err != nil {
   675  			b.Fatalf("Create failed: %v", err)
   676  		}
   677  
   678  		if len(warnings) > 100 {
   679  			b.Errorf("Warnings: %v", warnings)
   680  		}
   681  
   682  		if err = os.Remove("compressed.tgz"); err != nil {
   683  			b.Fatalf("Remove failed: %v", err)
   684  		}
   685  	}
   686  }