From 38b493ce200c0767cd270767714f12d722e6781b Mon Sep 17 00:00:00 2001 From: claude-bot-go Date: Tue, 21 Jul 2026 19:31:23 +0800 Subject: [PATCH] Fix #113: [milestone Milestone 5 ] `wasmagent-ops/`: Continuous verification daemon monitors running agents and ale... --- docs/project-index.json | 2 +- tests/e2e/e2e_test.go | 268 ++++++++++++++++++ .../fixtures/verification-daemon-sample.json | 100 +++++++ 3 files changed, 369 insertions(+), 1 deletion(-) create mode 100644 tests/e2e/fixtures/verification-daemon-sample.json diff --git a/docs/project-index.json b/docs/project-index.json index c11832a..cda24c2 100644 --- a/docs/project-index.json +++ b/docs/project-index.json @@ -116,7 +116,7 @@ "status": "shipped", "visibility": "internal", "in_profile": false, - "summary": "Private operations hub: media publishing, release ops, eval, research, outreach, security ops. Not a public product.", + "summary": "Internal operations hub: generators, release ops, eval, research, and continuous verification daemon that monitors running agents and alerts on trust-policy violations. Not a public product.", "url": "https://github.com/WasmAgent/wasmagent-ops" }, { diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index 85d46b6..6310a19 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "os" "path/filepath" + "strings" "testing" "github.com/WasmAgent/.github/pkg/docs" @@ -306,4 +307,271 @@ func TestRuntimeWorkloadIntegration(t *testing.T) { } t.Logf("Runtime integration validated with %d shipped workload(s)", shippedWorkloads) +} + +// verificationDaemon represents a continuous verification daemon configuration fixture. +type verificationDaemon struct { + SpecVersion string `json:"specVersion"` + Metadata map[string]interface{} `json:"metadata"` + DaemonConfig daemonConfig `json:"daemonConfig"` + MonitoredAgents []monitoredAgent `json:"monitoredAgents"` + TrustPolicies []trustPolicy `json:"trustPolicies"` + AlertChannels []alertChannel `json:"alertChannels"` + SampleViolations []sampleViolation `json:"sampleViolations"` +} + +type daemonConfig struct { + PollIntervalSeconds int `json:"pollIntervalSeconds"` + AlertBackoffSeconds int `json:"alertBackoffSeconds"` + MaxConcurrentAgents int `json:"maxConcurrentAgents"` + HealthCheckEndpoint string `json:"healthCheckEndpoint"` +} + +type monitoredAgent struct { + AgentID string `json:"agentId"` + Namespace string `json:"namespace"` + Endpoint string `json:"endpoint"` + TrustPolicyRef string `json:"trustPolicyRef"` +} + +type trustPolicy struct { + ID string `json:"id"` + Name string `json:"name"` + Severity string `json:"severity"` + Scope string `json:"scope"` + Rules []policyRule `json:"rules"` +} + +type policyRule struct { + Type string `json:"type"` + Description string `json:"description"` + Action string `json:"action"` +} + +type alertChannel struct { + Type string `json:"type"` + Destination string `json:"destination"` +} + +type sampleViolation struct { + Timestamp string `json:"timestamp"` + AgentID string `json:"agentId"` + PolicyID string `json:"policyId"` + Severity string `json:"severity"` + Description string `json:"description"` + EvidenceRef string `json:"evidenceRef"` +} + +// TestContinuousVerificationDaemon validates that the wasmagent-ops repository +// is configured for continuous verification daemon operation and that the +// daemon fixture conforms to the expected schema. +func TestContinuousVerificationDaemon(t *testing.T) { + projectIndex, err := docs.LoadProjectIndex() + if err != nil { + t.Fatalf("Failed to load project index: %v", err) + } + + // wasmagent-ops must exist for continuous verification daemon + opsRepo, found := projectIndex.GetRepoByName("wasmagent-ops") + if !found { + t.Fatal("wasmagent-ops repository not found in project index — continuous verification daemon requires ops infrastructure") + } + + // wasmagent-ops must be shipped + if opsRepo.Status != "shipped" { + t.Errorf("wasmagent-ops must be shipped for continuous verification daemon (status: %s)", opsRepo.Status) + } + + // The summary must reference continuous verification daemon + if !strings.Contains(strings.ToLower(opsRepo.Summary), "continuous verification") { + t.Errorf("wasmagent-ops summary does not mention continuous verification daemon: %s", opsRepo.Summary) + } + + // Load and validate the verification daemon fixture + daemonContent, err := os.ReadFile("fixtures/verification-daemon-sample.json") + if err != nil { + t.Fatalf("Verification daemon fixture not found: %v", err) + } + + var daemon verificationDaemon + if err := json.Unmarshal(daemonContent, &daemon); err != nil { + t.Fatalf("Verification daemon fixture contains invalid JSON: %v", err) + } + + // Validate spec version + if daemon.SpecVersion == "" { + t.Error("Verification daemon fixture missing specVersion") + } + + // Validate daemon configuration + if daemon.DaemonConfig.PollIntervalSeconds <= 0 { + t.Error("Daemon pollIntervalSeconds must be positive") + } + if daemon.DaemonConfig.AlertBackoffSeconds <= 0 { + t.Error("Daemon alertBackoffSeconds must be positive") + } + if daemon.DaemonConfig.MaxConcurrentAgents <= 0 { + t.Error("Daemon maxConcurrentAgents must be positive") + } + if daemon.DaemonConfig.HealthCheckEndpoint == "" { + t.Error("Daemon healthCheckEndpoint must not be empty") + } + + t.Logf("Continuous verification daemon validated: poll=%ds, backoff=%ds, maxAgents=%d", + daemon.DaemonConfig.PollIntervalSeconds, daemon.DaemonConfig.AlertBackoffSeconds, daemon.DaemonConfig.MaxConcurrentAgents) +} + +// TestTrustPolicyViolationAlerting validates that trust policies are properly +// defined with severity levels, rules, and that sample violations reference +// valid policies. +func TestTrustPolicyViolationAlerting(t *testing.T) { + // Load the verification daemon fixture + daemonContent, err := os.ReadFile("fixtures/verification-daemon-sample.json") + if err != nil { + t.Fatalf("Verification daemon fixture not found: %v", err) + } + + var daemon verificationDaemon + if err := json.Unmarshal(daemonContent, &daemon); err != nil { + t.Fatalf("Verification daemon fixture contains invalid JSON: %v", err) + } + + // Validate trust policies exist + if len(daemon.TrustPolicies) == 0 { + t.Fatal("No trust policies defined — daemon cannot monitor for violations") + } + + // Validate each trust policy has required fields + policyIDs := make(map[string]string) // id -> severity + validSeverities := map[string]bool{"critical": true, "warning": true, "info": true} + for _, policy := range daemon.TrustPolicies { + if policy.ID == "" { + t.Error("Trust policy has empty ID") + } + if policy.Name == "" { + t.Errorf("Trust policy %s has empty name", policy.ID) + } + if !validSeverities[policy.Severity] { + t.Errorf("Trust policy %s has unrecognized severity: %s", policy.ID, policy.Severity) + } + if policy.Scope == "" { + t.Errorf("Trust policy %s has empty scope", policy.ID) + } + if len(policy.Rules) == 0 { + t.Errorf("Trust policy %s has no rules defined", policy.ID) + } + for _, rule := range policy.Rules { + if rule.Type == "" { + t.Errorf("Rule in policy %s has empty type", policy.ID) + } + if rule.Description == "" { + t.Errorf("Rule in policy %s has empty description", policy.ID) + } + if rule.Action == "" { + t.Errorf("Rule in policy %s has empty action", policy.ID) + } + } + policyIDs[policy.ID] = policy.Severity + } + + // Validate sample violations reference valid policies + if len(daemon.SampleViolations) == 0 { + t.Fatal("No sample violations defined — daemon alerting cannot be validated") + } + + for _, violation := range daemon.SampleViolations { + if violation.AgentID == "" { + t.Error("Sample violation has empty agentId") + } + if violation.PolicyID == "" { + t.Error("Sample violation has empty policyId") + } + if violation.Timestamp == "" { + t.Error("Sample violation has empty timestamp") + } + if violation.Description == "" { + t.Error("Sample violation has empty description") + } + if violation.EvidenceRef == "" { + t.Error("Sample violation has empty evidenceRef") + } + + // Violation must reference a valid policy + if _, exists := policyIDs[violation.PolicyID]; !exists { + t.Errorf("Sample violation references unknown policy: %s", violation.PolicyID) + } + + // Violation severity must match policy severity + if expectedSeverity, exists := policyIDs[violation.PolicyID]; exists { + if violation.Severity != expectedSeverity { + t.Errorf("Sample violation severity %s does not match policy %s severity %s", + violation.Severity, violation.PolicyID, expectedSeverity) + } + } + } + + // Validate alert channels exist + if len(daemon.AlertChannels) == 0 { + t.Error("No alert channels defined — daemon cannot deliver alerts") + } + + for _, channel := range daemon.AlertChannels { + if channel.Type == "" { + t.Error("Alert channel has empty type") + } + if channel.Destination == "" { + t.Errorf("Alert channel %s has empty destination", channel.Type) + } + } + + t.Logf("Trust policy violation alerting validated: %d policies, %d violations, %d alert channels", + len(daemon.TrustPolicies), len(daemon.SampleViolations), len(daemon.AlertChannels)) +} + +// TestMonitoredAgentIntegrity validates that each monitored agent has +// a valid namespace, endpoint, and trust policy reference, and that +// all agents referenced in violations are actually monitored. +func TestMonitoredAgentIntegrity(t *testing.T) { + // Load the verification daemon fixture + daemonContent, err := os.ReadFile("fixtures/verification-daemon-sample.json") + if err != nil { + t.Fatalf("Verification daemon fixture not found: %v", err) + } + + var daemon verificationDaemon + if err := json.Unmarshal(daemonContent, &daemon); err != nil { + t.Fatalf("Verification daemon fixture contains invalid JSON: %v", err) + } + + // Build set of monitored agent IDs + monitoredAgentIDs := make(map[string]bool) + for _, agent := range daemon.MonitoredAgents { + if agent.AgentID == "" { + t.Error("Monitored agent has empty agentId") + } + if agent.Namespace == "" { + t.Errorf("Monitored agent %s has empty namespace", agent.AgentID) + } + if agent.Endpoint == "" { + t.Errorf("Monitored agent %s has empty endpoint", agent.AgentID) + } + if agent.TrustPolicyRef == "" { + t.Errorf("Monitored agent %s has empty trustPolicyRef", agent.AgentID) + } + monitoredAgentIDs[agent.AgentID] = true + } + + if len(monitoredAgentIDs) == 0 { + t.Error("No monitored agents defined — daemon has nothing to monitor") + } + + // All agents referenced in violations must be monitored + for _, violation := range daemon.SampleViolations { + if !monitoredAgentIDs[violation.AgentID] { + t.Errorf("Sample violation references agent %s which is not in the monitored agents list", violation.AgentID) + } + } + + t.Logf("Monitored agent integrity validated: %d agents monitored, %d have violations", + len(monitoredAgentIDs), len(daemon.SampleViolations)) } \ No newline at end of file diff --git a/tests/e2e/fixtures/verification-daemon-sample.json b/tests/e2e/fixtures/verification-daemon-sample.json new file mode 100644 index 0000000..b427cff --- /dev/null +++ b/tests/e2e/fixtures/verification-daemon-sample.json @@ -0,0 +1,100 @@ +{ + "$schema": "https://wasmagent.github.io/agent-trust-infra/schemas/verification-daemon-1.json", + "daemonFormat": "VerificationDaemon", + "specVersion": "1.0", + "metadata": { + "generated": "2026-07-21T00:00:00Z", + "repository": "WasmAgent/wasmagent-ops", + "release": "v1.0.0", + "generator": "wasmagent-ops/verification-daemon (test fixture)" + }, + "daemonConfig": { + "pollIntervalSeconds": 30, + "alertBackoffSeconds": 300, + "maxConcurrentAgents": 50, + "healthCheckEndpoint": "/healthz" + }, + "monitoredAgents": [ + { + "agentId": "github.com/WasmAgent/bscode", + "namespace": "workload", + "endpoint": "http://localhost:8080", + "trustPolicyRef": "policies/bscode-trust.json" + }, + { + "agentId": "github.com/WasmAgent/erp-agent", + "namespace": "workload", + "endpoint": "http://localhost:8081", + "trustPolicyRef": "policies/erp-trust.json" + } + ], + "trustPolicies": [ + { + "id": "policy-aep-required", + "name": "Require AEP events for all data mutations", + "severity": "critical", + "scope": "all-agents", + "rules": [ + { + "type": "aep-emission", + "description": "Every data mutation must produce a signed AEP event", + "action": "alert" + } + ] + }, + { + "id": "policy-mcp-posture", + "name": "MCP posture must match declared manifest", + "severity": "warning", + "scope": "all-agents", + "rules": [ + { + "type": "posture-drift", + "description": "Observed MCP tool usage must not exceed declared capabilities", + "action": "alert" + } + ] + }, + { + "id": "policy-agentbom-freshness", + "name": "AgentBOM must be generated within 24 hours of agent deployment", + "severity": "info", + "scope": "all-agents", + "rules": [ + { + "type": "artifact-freshness", + "description": "AgentBOM age must not exceed 86400 seconds", + "action": "alert" + } + ] + } + ], + "alertChannels": [ + { + "type": "log", + "destination": "stdout" + }, + { + "type": "webhook", + "destination": "https://hooks.example.com/alerts" + } + ], + "sampleViolations": [ + { + "timestamp": "2026-07-21T12:34:56Z", + "agentId": "github.com/WasmAgent/bscode", + "policyId": "policy-aep-required", + "severity": "critical", + "description": "Agent performed 3 data mutations without emitting AEP events", + "evidenceRef": "evidence/bscode-violation-20260721.jsonl" + }, + { + "timestamp": "2026-07-21T12:40:00Z", + "agentId": "github.com/WasmAgent/erp-agent", + "policyId": "policy-mcp-posture", + "severity": "warning", + "description": "Observed tool usage includes 'network-request' not declared in manifest", + "evidenceRef": "evidence/erp-violation-20260721.jsonl" + } + ] +}