stat must reject a regular file reached through a symlinked ancestor that escapes the scope (GHSA-hf77-9m7w-fq8q), while still serving in-scope files.
(t *testing.T)
| 216 | // stat must reject a regular file reached through a symlinked ancestor that |
| 217 | // escapes the scope (GHSA-hf77-9m7w-fq8q), while still serving in-scope files. |
| 218 | func TestStatRejectsLinkedAncestorEscape(t *testing.T) { |
| 219 | scope := t.TempDir() |
| 220 | if err := os.MkdirAll(filepath.Join(scope, "shared"), 0o755); err != nil { |
| 221 | t.Fatal(err) |
| 222 | } |
| 223 | if err := os.MkdirAll(filepath.Join(scope, "private"), 0o755); err != nil { |
| 224 | t.Fatal(err) |
| 225 | } |
| 226 | if err := os.WriteFile(filepath.Join(scope, "private", "secret.txt"), []byte("secret"), 0o600); err != nil { |
| 227 | t.Fatal(err) |
| 228 | } |
| 229 | if err := os.WriteFile(filepath.Join(scope, "shared", "ok.txt"), []byte("ok"), 0o600); err != nil { |
| 230 | t.Fatal(err) |
| 231 | } |
| 232 | if err := os.Symlink(filepath.Join(scope, "private"), filepath.Join(scope, "shared", "link")); err != nil { |
| 233 | t.Skipf("cannot create symlink: %v", err) |
| 234 | } |
| 235 | |
| 236 | // Filesystem scoped to the shared directory, as a public share would be. |
| 237 | bfs := NewScopedFs(afero.NewOsFs(), filepath.Join(scope, "shared")) |
| 238 | |
| 239 | if _, err := stat(&FileOptions{Fs: bfs, Path: "/link/secret.txt"}); !os.IsPermission(err) { |
| 240 | t.Fatalf("expected permission error for linked-ancestor escape, got %v", err) |
| 241 | } |
| 242 | if _, err := stat(&FileOptions{Fs: bfs, Path: "/ok.txt"}); err != nil { |
| 243 | t.Fatalf("expected in-scope file to be served, got %v", err) |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | type allowAllChecker struct{} |
| 248 |
nothing calls this directly
no test coverage detected