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.
117 lines
4.2 KiB
Go
117 lines
4.2 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestAdmissionEvidenceUTF8Limit(t *testing.T) {
|
|
id := "sha256:" + strings.Repeat("d", 64)
|
|
a, sid := runtimeTestAPI(t, func(w http.ResponseWriter, r *http.Request) { writeImageInspect(w, id, "0.2.0-rc.2") })
|
|
seedVerifiedImage(t, a, id, "0.2.0-rc.2")
|
|
if _, err := a.s.DB.Exec("UPDATE users SET admin=1"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, tc := range []struct {
|
|
name, evidence string
|
|
want int
|
|
}{
|
|
{"ascii boundary", strings.Repeat("a", 4096), 200},
|
|
{"ascii over", strings.Repeat("a", 4097), 400},
|
|
{"Chinese regression", strings.Repeat("中", 1400), 400},
|
|
{"Chinese below", strings.Repeat("中", 1365), 200},
|
|
{"Chinese boundary", strings.Repeat("中", 1365) + "a", 200},
|
|
{"Chinese over", strings.Repeat("中", 1365) + "ab", 400},
|
|
{"emoji boundary", strings.Repeat("😀", 1024), 200},
|
|
{"emoji over", strings.Repeat("😀", 1024) + "a", 400},
|
|
{"two byte boundary", strings.Repeat("é", 2048), 200},
|
|
{"raw whitespace counts", " " + strings.Repeat("a", 4096), 400},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
body, err := json.Marshal(map[string]string{"image_id": id, "state": "compatible", "evidence": tc.evidence})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
w := httptest.NewRecorder()
|
|
a.imageAdmission(w, runtimeRequest("POST", "/api/images/admission", string(body), sid))
|
|
if w.Code != tc.want {
|
|
t.Fatal(w.Code, w.Body.String())
|
|
}
|
|
if tc.want == 400 && !strings.Contains(w.Body.String(), "4096 UTF-8 bytes") {
|
|
t.Fatal(w.Body.String())
|
|
}
|
|
if tc.want == 200 {
|
|
var evidence string
|
|
if err := a.s.DB.QueryRow("SELECT evidence FROM image_admissions WHERE image_id=?", id).Scan(&evidence); err != nil || evidence != tc.evidence {
|
|
t.Fatal(evidence, err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAdmissionRoutesEnforceAuthorization(t *testing.T) {
|
|
id := "sha256:" + strings.Repeat("d", 64)
|
|
a, sid := runtimeTestAPI(t, func(w http.ResponseWriter, r *http.Request) { writeImageInspect(w, id, "0.2.0-rc.2") })
|
|
seedVerifiedImage(t, a, id, "0.2.0-rc.2")
|
|
_, csrf, err := a.s.SessionUser(sid)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
body := fmt.Sprintf(`{"image_id":%q,"state":"compatible","evidence":"isolated validation reference"}`, id)
|
|
request := func(method, path, payload string, auth, token bool) *httptest.ResponseRecorder {
|
|
session := ""
|
|
if auth {
|
|
session = sid
|
|
}
|
|
r := runtimeRequest(method, path, payload, session)
|
|
r.Header.Set("Origin", "http://panel.test")
|
|
if token {
|
|
r.Header.Set("X-CSRF-Token", csrf)
|
|
}
|
|
w := httptest.NewRecorder()
|
|
a.Handler().ServeHTTP(w, r)
|
|
return w
|
|
}
|
|
if w := request("GET", "/api/compatibility", "", false, false); w.Code != 401 {
|
|
t.Fatal(w.Code, w.Body.String())
|
|
}
|
|
if w := request("GET", "/api/compatibility", "", true, false); w.Code != 200 {
|
|
t.Fatal(w.Code, w.Body.String())
|
|
}
|
|
if w := request("POST", "/api/images/admission", body, true, true); w.Code != 403 {
|
|
t.Fatal("nonadmin admission", w.Code, w.Body.String())
|
|
}
|
|
if w := request("POST", "/api/builds", `{"version":"9.8.7","experimental":true}`, true, true); w.Code != 403 {
|
|
t.Fatal("nonadmin experiment", w.Code, w.Body.String())
|
|
}
|
|
if _, err = a.s.DB.Exec("UPDATE users SET admin=1 WHERE id='owner'"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if w := request("POST", "/api/images/admission", body, true, false); w.Code != 403 {
|
|
t.Fatal("missing CSRF", w.Code, w.Body.String())
|
|
}
|
|
if w := request("POST", "/api/images/admission", body, true, true); w.Code != 200 {
|
|
t.Fatal("admin admission", w.Code, w.Body.String())
|
|
}
|
|
for _, payload := range []string{
|
|
body + "{}",
|
|
body + strings.Repeat(" ", 8192),
|
|
fmt.Sprintf(`{"image_id":%q,"state":"compatible","evidence":" "}`, id),
|
|
fmt.Sprintf(`{"image_id":%q,"state":"available","evidence":"invalid state"}`, id),
|
|
fmt.Sprintf(`{"image_id":%q,"state":"compatible","evidence":"test","dockerfile":"not allowed"}`, id),
|
|
} {
|
|
if w := request("POST", "/api/images/admission", payload, true, true); w.Code != 400 {
|
|
t.Fatal(w.Code, w.Body.String())
|
|
}
|
|
}
|
|
var count int
|
|
if err = a.s.DB.QueryRow("SELECT count(*) FROM audit WHERE action='image.admission'").Scan(&count); err != nil || count != 1 {
|
|
t.Fatal(count, err)
|
|
}
|
|
}
|