64f704d0f7
A hand-built proof binary and the published artifact share a version string but not their bytes, so no version check could see the divergence. The agent now reports agent_sha256 (hash of /proc/self/exe, once per process; empty = unknown) beside agent_version, the same mechanism as host.wrapper_sha256. The hub half (compare against the vouched agent_sha256, surface drift) is owed in the hub repo. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
65 lines
2.1 KiB
Go
65 lines
2.1 KiB
Go
package hub
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// R-349: the report carries the sha256 of the binary that is RUNNING, so the hub can tell a
|
|
// hand-built proof binary from the vouched artifact of the same version string. The consequence
|
|
// asserted: the wire field equals the hash of this very test binary's bytes (read independently via
|
|
// os.Executable, a different channel from /proc/self/exe), and it is on the wire as agent_sha256.
|
|
func TestCollect_AgentSHA256IsTheRunningBinary(t *testing.T) {
|
|
exe, err := os.Executable()
|
|
if err != nil {
|
|
t.Skipf("os.Executable: %v", err)
|
|
}
|
|
raw, err := os.ReadFile(exe)
|
|
if err != nil {
|
|
t.Fatalf("read own binary: %v", err)
|
|
}
|
|
sum := sha256.Sum256(raw)
|
|
want := hex.EncodeToString(sum[:])
|
|
|
|
px := &fakePx{node: "n", ns: newTestNodeStatus()}
|
|
c := NewCollector(px, fakeProber{status: "running"}, nil, nil, nil, nil, "h", "0.3.0", quietLogger())
|
|
r, err := c.Collect(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("Collect: %v", err)
|
|
}
|
|
if r.AgentSHA256 != want {
|
|
t.Fatalf("agent_sha256 = %q, want the running binary's %q", r.AgentSHA256, want)
|
|
}
|
|
b, err := json.Marshal(r)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(string(b), `"agent_sha256":"`+want+`"`) {
|
|
t.Fatalf("agent_sha256 not on the wire: %s", b)
|
|
}
|
|
}
|
|
|
|
// An unreadable binary is UNKNOWN (empty, omitted) — never a made-up hash, never a failed report.
|
|
func TestFileSHA256_UnreadableIsEmpty(t *testing.T) {
|
|
if got := fileSHA256(filepath.Join(t.TempDir(), "absent")); got != "" {
|
|
t.Fatalf("absent file hashed to %q, want empty", got)
|
|
}
|
|
px := &fakePx{node: "n", ns: newTestNodeStatus()}
|
|
c := NewCollector(px, fakeProber{status: "running"}, nil, nil, nil, nil, "h", "0.3.0", quietLogger())
|
|
c.selfSHA = func() string { return "" }
|
|
r, err := c.Collect(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("Collect must not fail on an unreadable binary: %v", err)
|
|
}
|
|
b, _ := json.Marshal(r)
|
|
if strings.Contains(string(b), "agent_sha256") {
|
|
t.Fatalf("empty agent_sha256 must be omitted: %s", b)
|
|
}
|
|
}
|