diff options
Diffstat (limited to 'internal/admin/api_entity_path_test.go')
| -rw-r--r-- | internal/admin/api_entity_path_test.go | 21 |
1 files changed, 21 insertions, 0 deletions
diff --git a/internal/admin/api_entity_path_test.go b/internal/admin/api_entity_path_test.go index 75fed7a..3a0f87b 100644 --- a/internal/admin/api_entity_path_test.go +++ b/internal/admin/api_entity_path_test.go @@ -56,10 +56,31 @@ func TestEntityAPICannotEscapeData(t *testing.T) { if !strings.Contains(rec.Body.String(), "error") { t.Fatalf("traversal rename id not rejected: status %d body %s", rec.Code, rec.Body.String()) } + // Traversal SOURCE id via the rename and move routes: the source is + // joined into a path too, so it must be rejected before it can move a + // file outside data/ (e.g. the server's own config.yaml). + sentinel := filepath.Join(root, "sentinel.yaml") + if err := os.WriteFile(sentinel, []byte("name: Sentinel\n"), 0o600); err != nil { + t.Fatal(err) + } + for _, tc := range []struct{ path, body string }{ + {"/api/objects/rename", `{"id":"../../sentinel","new_id":"stolen"}`}, + {"/api/objects/move", `{"id":"../../sentinel","dir":"sub"}`}, + } { + if rec := post(tc.path, tc.body); !strings.Contains(rec.Body.String(), "error") { + t.Fatalf("traversal source id not rejected at %s: status %d body %s", tc.path, rec.Code, rec.Body.String()) + } + } + if _, err := os.Stat(sentinel); err != nil { + t.Fatalf("traversal source id moved a file outside data/: %v", err) + } // Nothing may exist outside the data directory. if _, err := os.Stat(filepath.Join(root, "outside.yaml")); !os.IsNotExist(err) { t.Fatal("entity API escaped the data directory") } + if _, err := os.Stat(filepath.Join(root, "stolen.yaml")); !os.IsNotExist(err) { + t.Fatal("traversal rename escaped the data directory") + } } // TestTechModuleCreateRejectsTraversalID: POST /api/techs and /api/modules |
