@@ -32,7 +32,6 @@ import (
32
32
"errors"
33
33
"fmt"
34
34
"io"
35
- "io/ioutil"
36
35
"log"
37
36
"net/http"
38
37
"os"
@@ -125,7 +124,7 @@ func joinMaps(args ...map[string]interface{}) map[string]interface{} {
125
124
}
126
125
127
126
func expandFile (src , dst string , args ... map [string ]interface {}) error {
128
- tmplData , err := ioutil .ReadFile (src )
127
+ tmplData , err := os .ReadFile (src )
129
128
if err != nil {
130
129
return fmt .Errorf ("failed reading from template %v: %w" , src , err )
131
130
}
@@ -140,7 +139,7 @@ func expandFile(src, dst string, args ...map[string]interface{}) error {
140
139
return err
141
140
}
142
141
143
- if err = ioutil .WriteFile (createDir (dst ), []byte (output ), 0644 ); err != nil {
142
+ if err = os .WriteFile (createDir (dst ), []byte (output ), 0644 ); err != nil {
144
143
return fmt .Errorf ("failed to write rendered template: %w" , err )
145
144
}
146
145
@@ -262,13 +261,13 @@ func FindReplace(file string, re *regexp.Regexp, repl string) error {
262
261
return err
263
262
}
264
263
265
- contents , err := ioutil .ReadFile (file )
264
+ contents , err := os .ReadFile (file )
266
265
if err != nil {
267
266
return err
268
267
}
269
268
270
269
out := re .ReplaceAllString (string (contents ), repl )
271
- return ioutil .WriteFile (file , []byte (out ), info .Mode ().Perm ())
270
+ return os .WriteFile (file , []byte (out ), info .Mode ().Perm ())
272
271
}
273
272
274
273
// MustFindReplace invokes FindReplace and panics if an error occurs.
@@ -283,9 +282,14 @@ func MustFindReplace(file string, re *regexp.Regexp, repl string) {
283
282
func DownloadFile (url , destinationDir string ) (string , error ) {
284
283
log .Println ("Downloading" , url )
285
284
286
- resp , err := http .Get ( url )
285
+ req , err := http .NewRequestWithContext ( context . TODO (), http . MethodGet , url , nil )
287
286
if err != nil {
288
- return "" , fmt .Errorf ("http get failed: %w" , err )
287
+ return "" , fmt .Errorf ("failed to create http request: %w" , err )
288
+ }
289
+
290
+ resp , err := http .DefaultClient .Do (req )
291
+ if err != nil {
292
+ return "" , fmt .Errorf ("failed to download file: %w" , err )
289
293
}
290
294
defer resp .Body .Close ()
291
295
@@ -338,9 +342,9 @@ func unzip(sourceFile, destinationDir string) error {
338
342
}
339
343
defer innerFile .Close ()
340
344
341
- path := filepath . Join ( destinationDir , f .Name )
342
- if ! strings . HasPrefix ( path , destinationDir ) {
343
- return fmt . Errorf ( "illegal file path in zip: %v" , f . Name )
345
+ path , err := sanitizeFilePath ( f .Name , destinationDir )
346
+ if err != nil {
347
+ return err
344
348
}
345
349
346
350
if f .FileInfo ().IsDir () {
@@ -357,7 +361,7 @@ func unzip(sourceFile, destinationDir string) error {
357
361
}
358
362
defer out .Close ()
359
363
360
- if _ , err = io .Copy (out , innerFile ); err != nil {
364
+ if _ , err = io .Copy (out , innerFile ); err != nil { //nolint:gosec // this is only used for dev tools
361
365
return err
362
366
}
363
367
@@ -374,6 +378,16 @@ func unzip(sourceFile, destinationDir string) error {
374
378
return nil
375
379
}
376
380
381
+ // sanitizeExtractPath sanitizes against path traversal attacks.
382
+ // See https://security.snyk.io/research/zip-slip-vulnerability.
383
+ func sanitizeFilePath (filePath string , workdir string ) (string , error ) {
384
+ destPath := filepath .Join (workdir , filePath )
385
+ if ! strings .HasPrefix (destPath , filepath .Clean (workdir )+ string (os .PathSeparator )) {
386
+ return filePath , fmt .Errorf ("failed to extract illegal file path: %s" , filePath )
387
+ }
388
+ return destPath , nil
389
+ }
390
+
377
391
// Tar compress a directory using tar + gzip algorithms but without adding
378
392
// the directory
379
393
func TarWithOptions (src string , targetFile string , trimSource bool ) error {
@@ -390,7 +404,7 @@ func TarWithOptions(src string, targetFile string, trimSource bool) error {
390
404
tw := tar .NewWriter (zr )
391
405
392
406
// walk through every file in the folder
393
- filepath .Walk (src , func (file string , fi os.FileInfo , errFn error ) error {
407
+ err = filepath .Walk (src , func (file string , fi os.FileInfo , errFn error ) error {
394
408
if errFn != nil {
395
409
return fmt .Errorf ("error traversing the file system: %w" , errFn )
396
410
}
@@ -438,6 +452,9 @@ func TarWithOptions(src string, targetFile string, trimSource bool) error {
438
452
}
439
453
return nil
440
454
})
455
+ if err != nil {
456
+ return fmt .Errorf ("error walking dir: %w" , err )
457
+ }
441
458
442
459
// produce tar
443
460
if err := tw .Close (); err != nil {
@@ -477,15 +494,15 @@ func untar(sourceFile, destinationDir string) error {
477
494
for {
478
495
header , err := tarReader .Next ()
479
496
if err != nil {
480
- if err == io .EOF {
497
+ if errors . Is ( err , io .EOF ) {
481
498
break
482
499
}
483
500
return err
484
501
}
485
502
486
- path := filepath . Join ( destinationDir , header .Name )
487
- if ! strings . HasPrefix ( path , destinationDir ) {
488
- return fmt . Errorf ( "illegal file path in tar: %v" , header . Name )
503
+ path , err := sanitizeFilePath ( header .Name , destinationDir )
504
+ if err != nil {
505
+ return err
489
506
}
490
507
491
508
switch header .Typeflag {
@@ -499,7 +516,7 @@ func untar(sourceFile, destinationDir string) error {
499
516
return err
500
517
}
501
518
502
- if _ , err = io .Copy (writer , tarReader ); err != nil {
519
+ if _ , err = io .Copy (writer , tarReader ); err != nil { //nolint:gosec // this is only used for dev tools
503
520
return err
504
521
}
505
522
@@ -613,7 +630,7 @@ func ParallelCtx(ctx context.Context, fns ...interface{}) {
613
630
614
631
wg .Wait ()
615
632
if len (errs ) > 0 {
616
- panic (fmt . Errorf (strings .Join (errs , "\n " )))
633
+ panic (errors . New (strings .Join (errs , "\n " )))
617
634
}
618
635
}
619
636
@@ -773,7 +790,7 @@ func CreateSHA512File(file string) error {
773
790
computedHash := hex .EncodeToString (sum .Sum (nil ))
774
791
out := fmt .Sprintf ("%v %v" , computedHash , filepath .Base (file ))
775
792
776
- return ioutil .WriteFile (file + ".sha512" , []byte (out ), 0644 )
793
+ return os .WriteFile (file + ".sha512" , []byte (out ), 0644 )
777
794
}
778
795
779
796
// Mage executes mage targets in the specified directory.
@@ -800,7 +817,7 @@ func IsUpToDate(dst string, sources ...string) bool {
800
817
801
818
var files []string
802
819
for _ , s := range sources {
803
- filepath .Walk (s , func (path string , info os.FileInfo , err error ) error {
820
+ err := filepath .Walk (s , func (path string , info os.FileInfo , err error ) error {
804
821
if err != nil {
805
822
if os .IsNotExist (err ) {
806
823
return nil
@@ -814,6 +831,9 @@ func IsUpToDate(dst string, sources ...string) bool {
814
831
815
832
return nil
816
833
})
834
+ if err != nil {
835
+ panic (fmt .Errorf ("failed to walk source %v: %w" , s , err ))
836
+ }
817
837
}
818
838
819
839
execute , err := target .Path (dst , files ... )
@@ -896,7 +916,7 @@ func ParseVersion(version string) (major, minor, patch int, err error) {
896
916
matches := parseVersionRegex .FindStringSubmatch (version )
897
917
if len (matches ) == 0 {
898
918
err = fmt .Errorf ("failed to parse version '%v'" , version )
899
- return
919
+ return major , minor , patch , err
900
920
}
901
921
902
922
data := map [string ]string {}
@@ -906,7 +926,7 @@ func ParseVersion(version string) (major, minor, patch int, err error) {
906
926
major , _ = strconv .Atoi (data ["major" ])
907
927
minor , _ = strconv .Atoi (data ["minor" ])
908
928
patch , _ = strconv .Atoi (data ["patch" ])
909
- return
929
+ return major , minor , patch , nil
910
930
}
911
931
912
932
// ListMatchingEnvVars returns all of the environment variables names that begin
0 commit comments