1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
package admin
import (
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"thehouseoficarus/internal/config"
"thehouseoficarus/internal/player"
)
// TestEntityAPICannotEscapeData: entity create/rename IDs and directory
// targets join into file paths, so a traversal ID ("../../x") must be
// rejected instead of writing outside the data directory.
func TestEntityAPICannotEscapeData(t *testing.T) {
root := t.TempDir()
dir := filepath.Join(root, "data")
for _, sub := range []string{"objects", "players/accounts"} {
if err := os.MkdirAll(filepath.Join(dir, sub), 0o700); err != nil {
t.Fatal(err)
}
}
accounts := player.NewAccountStore(dir)
if err := accounts.SaveAccount(&player.Account{Name: "auditor", Admin: true}); err != nil {
t.Fatal(err)
}
s, err := NewServer(config.Default(), false, accounts, nil, nil, nil, nil, dir,
nil, nil, nil, nil, filepath.Join(dir, "config.yaml"),
func(fn func()) bool { fn(); return true })
if err != nil {
t.Fatal(err)
}
cookieRec := httptest.NewRecorder()
s.setAuthCookie(cookieRec, "auditor")
cookie := cookieRec.Result().Cookies()[0]
post := func(path, body string) *httptest.ResponseRecorder {
req := httptest.NewRequest(http.MethodPost, path, strings.NewReader(body))
req.AddCookie(cookie)
rec := httptest.NewRecorder()
s.ServeHTTP(rec, req)
return rec
}
// Traversal entity ID via the structured create route.
if rec := post("/api/objects", `{"id":"../../outside","name":"Escaped object"}`); rec.Code != http.StatusBadRequest {
t.Fatalf("traversal object id: status %d body %s", rec.Code, rec.Body.String())
}
// Traversal new ID via the entity rename route: the rename handler
// must reject the request, not rename anything.
rec := post("/api/objects/rename", `{"id":"test","new_id":"../../outside"}`)
if !strings.Contains(rec.Body.String(), "error") {
t.Fatalf("traversal rename id not rejected: status %d body %s", rec.Code, rec.Body.String())
}
// 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")
}
}
|