@@ -2,8 +2,11 @@ package main
22
33import (
44 "archive/zip"
5+ "fmt"
56 "io"
67 "os"
8+ "path"
9+ "path/filepath"
710 "strings"
811
912 "golang.org/x/text/encoding"
@@ -32,6 +35,39 @@ func getDecoderByCoder(code string) *encoding.Decoder {
3235 return decoder
3336}
3437
38+ // safeExtractPath resolves a zip entry name under baseDir and rejects path traversal.
39+ func safeExtractPath (baseDir , name string ) (string , error ) {
40+ name = path .Clean (strings .ReplaceAll (name , "\\ " , "/" ))
41+ if name == "." || name == ".." {
42+ return "" , fmt .Errorf ("illegal path: %q" , name )
43+ }
44+ if strings .HasPrefix (name , "/" ) {
45+ return "" , fmt .Errorf ("illegal absolute path: %q" , name )
46+ }
47+ for _ , part := range strings .Split (name , "/" ) {
48+ if part == ".." {
49+ return "" , fmt .Errorf ("illegal path traversal: %q" , name )
50+ }
51+ }
52+
53+ if baseDir == "" || baseDir == "." {
54+ baseDir = "."
55+ }
56+ baseAbs , err := filepath .Abs (baseDir )
57+ if err != nil {
58+ return "" , err
59+ }
60+ destAbs , err := filepath .Abs (filepath .Join (baseAbs , filepath .FromSlash (name )))
61+ if err != nil {
62+ return "" , err
63+ }
64+ baseWithSep := baseAbs + string (filepath .Separator )
65+ if destAbs != baseAbs && ! strings .HasPrefix (destAbs , baseWithSep ) {
66+ return "" , fmt .Errorf ("illegal path escapes destination: %q" , name )
67+ }
68+ return destAbs , nil
69+ }
70+
3571func fileToZipWriter (file * os.File , prefix string , zw * zip.Writer ) error {
3672 info , err := file .Stat ()
3773 if err != nil {
0 commit comments