| 111 | }) |
| 112 | |
| 113 | func getFiles(d *data, path, commonPath string) ([]archives.FileInfo, error) { |
| 114 | if !d.Check(path) { |
| 115 | return nil, nil |
| 116 | } |
| 117 | |
| 118 | info, err := d.user.Fs.Stat(path) |
| 119 | if err != nil { |
| 120 | return nil, err |
| 121 | } |
| 122 | |
| 123 | var archiveFiles []archives.FileInfo |
| 124 | |
| 125 | if path != commonPath { |
| 126 | nameInArchive := strings.TrimPrefix(path, commonPath) |
| 127 | nameInArchive = strings.TrimPrefix(nameInArchive, string(filepath.Separator)) |
| 128 | nameInArchive = filepath.ToSlash(nameInArchive) |
| 129 | // A backslash is a legal filename character on POSIX hosts, so it can |
| 130 | // reach here verbatim. Rewriting it to the path separator "/" would |
| 131 | // manufacture a traversal sequence (e.g. "..\..\x" -> "../../x") that |
| 132 | // escapes the extraction directory on the victim's machine, while |
| 133 | // leaving it as "\" lets Windows extractors treat it as a separator. |
| 134 | // Neutralize it to an inert character instead of turning it into one. |
| 135 | nameInArchive = strings.ReplaceAll(nameInArchive, "\\", "_") |
| 136 | |
| 137 | // Defense in depth: never emit an archive entry whose path escapes the |
| 138 | // archive root, regardless of how the name was produced. |
| 139 | if cleaned := gopath.Clean("/" + nameInArchive); cleaned != "/"+nameInArchive { |
| 140 | return nil, fmt.Errorf("refusing unsafe archive entry name: %q", nameInArchive) |
| 141 | } |
| 142 | |
| 143 | archiveFiles = append(archiveFiles, archives.FileInfo{ |
| 144 | FileInfo: info, |
| 145 | NameInArchive: nameInArchive, |
| 146 | Open: func() (fs.File, error) { |
| 147 | return d.user.Fs.Open(path) |
| 148 | }, |
| 149 | }) |
| 150 | } |
| 151 | |
| 152 | if info.IsDir() { |
| 153 | f, err := d.user.Fs.Open(path) |
| 154 | if err != nil { |
| 155 | return nil, err |
| 156 | } |
| 157 | defer f.Close() |
| 158 | |
| 159 | names, err := f.Readdirnames(0) |
| 160 | if err != nil { |
| 161 | return nil, err |
| 162 | } |
| 163 | |
| 164 | for _, name := range names { |
| 165 | fPath := filepath.Join(path, name) |
| 166 | subFiles, err := getFiles(d, fPath, commonPath) |
| 167 | if err != nil { |
| 168 | log.Printf("Failed to get files from %s: %v", fPath, err) |
| 169 | continue |
| 170 | } |