Files
dsh-web/internal/api/workspace_instance_test.go

71 lines
2.4 KiB
Go

package api
import (
"git.misaka.ren/m1saka/dsh-web/internal/store"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
)
func TestWorkspaceInstanceScope(t *testing.T) {
a, sid := runtimeTestAPI(t, func(w http.ResponseWriter, r *http.Request) { t.Error("unexpected Docker call") })
for _, x := range []struct{ id, uid string }{{"one", "owner"}, {"two", "owner"}, {"foreign", "someone"}} {
if err := a.s.AddInstance(store.Instance{ID: x.id, UID: x.uid}); err != nil {
t.Fatal(err)
}
dir := filepath.Join(a.root, "workspaces", x.uid, "instances", x.id, "home")
if err := os.MkdirAll(dir, 0750); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, x.id+".txt"), []byte(x.id), 0600); err != nil {
t.Fatal(err)
}
}
for _, tc := range []struct {
query string
status int
contains string
}{
{"instance=one", 200, "one.txt"}, {"instance=two", 200, "two.txt"},
{"instance=foreign", 404, ""}, {"instance=missing", 404, ""},
{"instance=one&path=../two", 400, ""},
} {
w := httptest.NewRecorder()
a.workspace(w, runtimeRequest("GET", "/api/ws/tree?"+tc.query, "", sid))
if w.Code != tc.status || tc.contains != "" && !strings.Contains(w.Body.String(), tc.contains) {
t.Fatalf("%s: %d %s", tc.query, w.Code, w.Body.String())
}
}
w := httptest.NewRecorder()
a.workspace(w, runtimeRequest("POST", "/api/ws/mkdir?instance=two", `{"path":"created"}`, sid))
if w.Code != 200 {
t.Fatal(w.Code, w.Body.String())
}
if _, err := os.Stat(filepath.Join(a.root, "workspaces/owner/instances/two/home/created")); err != nil {
t.Fatal(err)
}
if _, err := os.Stat(filepath.Join(a.root, "workspaces/owner/instances/one/home/created")); !os.IsNotExist(err) {
t.Fatal("cross instance write")
}
dir := filepath.Join(a.root, "workspaces/owner/instances/one/home")
if err := os.Symlink(t.TempDir(), filepath.Join(dir, "escape")); err != nil {
t.Fatal(err)
}
w = httptest.NewRecorder()
a.workspace(w, runtimeRequest("GET", "/api/ws/tree?instance=one&path=escape", "", sid))
if w.Code != 404 {
t.Fatal("symlink traversal allowed", w.Code)
}
if _, err := a.s.DB.Exec("UPDATE instances SET status='error' WHERE id='two'"); err != nil {
t.Fatal(err)
}
w = httptest.NewRecorder()
a.workspace(w, runtimeRequest("POST", "/api/ws/mkdir?instance=two", "{\"path\":\"blocked\"}", sid))
if w.Code != 409 {
t.Fatal("recovery write permitted", w.Code)
}
}