| 203 | } |
| 204 | |
| 205 | func TestValidateChild(t *testing.T) { |
| 206 | root, err := safepaths.OpenRoot(t.TempDir()) |
| 207 | require.NoError(t, err) |
| 208 | t.Cleanup(func() { require.NoError(t, root.Close()) }) |
| 209 | |
| 210 | validators := []struct { |
| 211 | name string |
| 212 | validate func(string) error |
| 213 | }{ |
| 214 | {name: "ValidateChild", validate: safepaths.ValidateChild}, |
| 215 | {name: "Root.Validate", validate: root.Validate}, |
| 216 | } |
| 217 | tests := []struct { |
| 218 | name string |
| 219 | path string |
| 220 | wantErr bool |
| 221 | }{ |
| 222 | {name: "empty path cleans to root", path: ""}, |
| 223 | {name: "root", path: "."}, |
| 224 | {name: "local child", path: "nested/file.txt"}, |
| 225 | {name: "contained parent component", path: "nested/../file.txt"}, |
| 226 | {name: "trailing separator", path: "nested/"}, |
| 227 | {name: "parent escape", path: "../file.txt", wantErr: true}, |
| 228 | {name: "nested parent escape", path: "nested/../../file.txt", wantErr: true}, |
| 229 | {name: "absolute", path: filepath.Join(string(os.PathSeparator), "file.txt"), wantErr: true}, |
| 230 | {name: "backslash child", path: `nested\file.txt`}, |
| 231 | {name: "backslash parent escape", path: `..\..\file.txt`, wantErr: runtime.GOOS == "windows"}, |
| 232 | {name: "mixed separator escape", path: `nested/..\..\file.txt`, wantErr: runtime.GOOS == "windows"}, |
| 233 | {name: "drive relative path", path: `C:file.txt`, wantErr: runtime.GOOS == "windows"}, |
| 234 | {name: "drive absolute path", path: `C:\file.txt`, wantErr: runtime.GOOS == "windows"}, |
| 235 | {name: "UNC path", path: `\\server\share\file.txt`, wantErr: runtime.GOOS == "windows"}, |
| 236 | {name: "reserved Windows name", path: "NUL", wantErr: runtime.GOOS == "windows"}, |
| 237 | } |
| 238 | for _, tt := range tests { |
| 239 | t.Run(tt.name, func(t *testing.T) { |
| 240 | for _, validator := range validators { |
| 241 | t.Run(validator.name, func(t *testing.T) { |
| 242 | err := validator.validate(tt.path) |
| 243 | if tt.wantErr { |
| 244 | var traversalErr safepaths.PathTraversalError |
| 245 | require.ErrorAs(t, err, &traversalErr) |
| 246 | assert.Equal(t, []string{tt.path}, traversalErr.Elems) |
| 247 | } else { |
| 248 | require.NoError(t, err) |
| 249 | } |
| 250 | }) |
| 251 | } |
| 252 | }) |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | func TestOpenFileRejectsTrailingSeparator(t *testing.T) { |
| 257 | tests := []struct { |