Harden image admission, resource recovery and session renewal
Add exact-image compatibility policy, experimental admission, paginated build history and shared reauthentication checks. Preserve uncertain Docker creation records, bound frontend resource waits and renew runtime sessions proactively. Lazy-load and split Monaco, add regression and opt-in live Docker fault tests, and record full race and real-browser acceptance results.
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"git.misaka.ren/m1saka/dsh-web/internal/store"
|
||||
)
|
||||
|
||||
// Full-home access is an explicit owner capability, not a credential write-only
|
||||
// boundary. Keep ownership, authentication and traversal checks independent.
|
||||
func TestFullHomeOwnerPolicy(t *testing.T) {
|
||||
a, sid := runtimeTestAPI(t, func(w http.ResponseWriter, r *http.Request) { t.Error("unexpected Docker operation") })
|
||||
for _, i := range []store.Instance{{ID: "owned", UID: "owner", Status: "running"}, {ID: "foreign", UID: "other", Status: "running"}} {
|
||||
if err := a.s.AddInstance(i); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
home := filepath.Join(a.root, "workspaces", i.UID, "instances", i.ID, "home")
|
||||
if err := os.MkdirAll(filepath.Join(home, "profiles", "web"), 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for path, contents := range map[string]string{".credentials.yaml": "synthetic-test-credential", "dsh-ssh.json": "synthetic-test-ssh", "profiles/web/cordis.patch.yml": "old: value"} {
|
||||
if err := os.WriteFile(filepath.Join(home, path), []byte(contents), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
request := func(method, path, body, session string) *httptest.ResponseRecorder {
|
||||
w := httptest.NewRecorder()
|
||||
a.workspace(w, runtimeRequest(method, path, body, session))
|
||||
return w
|
||||
}
|
||||
for _, name := range []string{".credentials.yaml", "dsh-ssh.json"} {
|
||||
w := request("GET", "/api/ws/download?instance=owned&path="+name, "", sid)
|
||||
if w.Code != 200 || w.Body.Len() == 0 {
|
||||
t.Fatal(name, w.Code, w.Body.String())
|
||||
}
|
||||
if w = request("GET", "/api/ws/download?instance=foreign&path="+name, "", sid); w.Code != 404 {
|
||||
t.Fatal("cross-owner read", w.Code)
|
||||
}
|
||||
if w = request("GET", "/api/ws/download?instance=owned&path="+name, "", ""); w.Code != 401 {
|
||||
t.Fatal("anonymous read", w.Code)
|
||||
}
|
||||
}
|
||||
w := request("GET", "/api/ws/download?instance=owned&path=.", "", sid)
|
||||
if w.Code != 200 {
|
||||
t.Fatal(w.Code, w.Body.String())
|
||||
}
|
||||
archive, err := zip.NewReader(bytes.NewReader(w.Body.Bytes()), int64(w.Body.Len()))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
found := false
|
||||
for _, f := range archive.File {
|
||||
if f.Name == ".credentials.yaml" {
|
||||
r, e := f.Open()
|
||||
if e != nil {
|
||||
t.Fatal(e)
|
||||
}
|
||||
data, e := io.ReadAll(r)
|
||||
r.Close()
|
||||
if e != nil || string(data) != "synthetic-test-credential" {
|
||||
t.Fatal("wrong credential archive entry", e)
|
||||
}
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("owner archive unexpectedly filters credentials")
|
||||
}
|
||||
// Running state intentionally does not force the controlled profile workflow.
|
||||
w = request("POST", "/api/ws/rename?instance=owned", `{"path":"profiles/web/cordis.patch.yml","new_path":"profiles/web/saved.yml"}`, sid)
|
||||
if w.Code != 200 {
|
||||
t.Fatal("owner profile rename blocked", w.Code, w.Body.String())
|
||||
}
|
||||
w = request("GET", "/api/ws/download?instance=owned&path=../../other/instances/foreign/home/.credentials.yaml", "", sid)
|
||||
if w.Code != 400 && w.Code != 404 {
|
||||
t.Fatal("traversal allowed", w.Code)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user