package web import ( "strings" "testing" "gitea.dooplex.hu/admin/felhom-hub/internal/store" ) // R-349 (hub half): the agent reports the sha256 of the binary it RUNS (top-level agent_sha256). The // hub compares it with the vouched AgentSHA256 only when the reported version IS the vouched version; // an empty hash is UNKNOWN and never drift. const r349Vouched = "a56a92a7aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" const r349Hand = "256e0829bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" func TestR349_AgentBinaryDrift(t *testing.T) { m := store.ArtifactManifest{AgentVersion: "0.130.0", AgentSHA256: strings.ToUpper(r349Vouched)} cases := []struct{ name, report, want string }{ {"same version, same bytes", `{"agent_version":"0.130.0","agent_sha256":"` + r349Vouched + `"}`, "ok"}, {"same version, different bytes (the R-349 case)", `{"agent_version":"0.130.0","agent_sha256":"` + r349Hand + `"}`, "mismatch"}, {"v-prefixed version still compares", `{"agent_version":"v0.130.0","agent_sha256":"` + r349Hand + `"}`, "mismatch"}, {"older agent: no hash = unknown", `{"agent_version":"0.130.0"}`, ""}, {"empty hash = unknown", `{"agent_version":"0.130.0","agent_sha256":""}`, ""}, {"other version = not comparable", `{"agent_version":"0.129.0","agent_sha256":"` + r349Hand + `"}`, ""}, {"nested host.agent_sha256 is not the field", `{"agent_version":"0.130.0","host":{"agent_sha256":"` + r349Hand + `"}}`, ""}, {"no report", ``, ""}, {"malformed", `{nope`, ""}, } for _, tc := range cases { if got, _ := agentBinaryDrift(tc.report, m); got != tc.want { t.Errorf("%s: drift = %q, want %q", tc.name, got, tc.want) } } if got, _ := agentBinaryDrift(`{"agent_version":"0.130.0","agent_sha256":"`+r349Hand+`"}`, store.ArtifactManifest{AgentVersion: "0.130.0"}); got != "" { t.Errorf("un-vouched hash: drift = %q, want unknown", got) } } // The page, per branch of the template gate: drift (amber, both hashes), ok, and the unknown case // (no line at all). func TestR349_HostPageShowsAgentBinaryDrift(t *testing.T) { s, st, _ := newRevealServer(t) cookie, _ := newRevealSession(t, s) if err := st.SetArtifactManifest(store.ArtifactManifest{AgentVersion: "0.130.0", AgentSHA256: r349Vouched}); err != nil { t.Fatal(err) } seedNetHost(t, st, "h-drift", "0.130.0", `{"agent_version":"0.130.0","agent_sha256":"`+r349Hand+`"}`, "") seedNetHost(t, st, "h-ok", "0.130.0", `{"agent_version":"0.130.0","agent_sha256":"`+r349Vouched+`"}`, "") seedNetHost(t, st, "h-old", "0.130.0", `{"agent_version":"0.130.0"}`, "") b := getHostPage(t, s, cookie, "h-drift") if !strings.Contains(b, `id="agent-binary-drift"`) || !strings.Contains(b, r349Hand[:12]) || !strings.Contains(b, r349Vouched[:12]) { t.Fatal("same version, different bytes: the host page shows no agent-binary DRIFT with both hashes") } b = getHostPage(t, s, cookie, "h-ok") if !strings.Contains(b, `id="agent-binary"`) || strings.Contains(b, `id="agent-binary-drift"`) { t.Fatal("matching bytes: want the 'matches vouched' line and no drift") } b = getHostPage(t, s, cookie, "h-old") if strings.Contains(b, `id="agent-binary"`) { t.Fatal("an agent that reports no hash must show no agent-binary line (unknown, never drift)") } }