Files
pulse/internal/api/unified_agent_test.go
T
pulse-triage[bot] 78023e0e42 Serve signed Windows agents from canonical assets
Change-source: pulse-maintainer
2026-09-01 16:16:56 +01:00

680 lines
27 KiB
Go

package api
import (
"archive/tar"
"bytes"
"compress/gzip"
"crypto/sha256"
"encoding/hex"
"errors"
"io"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"github.com/rcourtman/pulse-go-rewrite/internal/updates"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func setupUnifiedAgentRouter(t *testing.T) (*Router, string) {
tempDir := t.TempDir()
// Create required directories
err := os.MkdirAll(filepath.Join(tempDir, "scripts"), 0755)
require.NoError(t, err)
err = os.MkdirAll(filepath.Join(tempDir, "bin"), 0755)
require.NoError(t, err)
router := &Router{
projectRoot: tempDir,
checksumCache: make(map[string]checksumCacheEntry),
}
return router, tempDir
}
// expectedAgentVersionForTest mirrors what the download handler will look for,
// so fixtures that stand in for a healthy agent carry the version a real build
// would. Without it every "valid" fixture reads as a stale build.
func expectedAgentVersionForTest() string {
versionInfo, err := updates.GetCurrentVersion()
if err != nil || versionInfo == nil {
return ""
}
return expectedAgentBinaryVersion(versionInfo.Version)
}
func validTestUnifiedAgentBinary(suffix string) []byte {
return []byte("ELF test binary " + canonicalUnifiedAgentReportPath + " " +
expectedAgentVersionForTest() + " " + suffix)
}
func staleTestUnifiedAgentBinary(suffix string) []byte {
return []byte("ELF stale binary " + legacyUnifiedAgentReportPath + " " + suffix)
}
func encodedTestSSHSignature(payload string) string {
return encodeSSHSignatureForHeader([]byte(payload))
}
func TestDownloadInstallScript_Local(t *testing.T) {
router, tempDir := setupUnifiedAgentRouter(t)
// Create dummy script
scriptContent := "#!/bin/bash\necho 'installing'"
scriptPath := filepath.Join(tempDir, "scripts", "install.sh")
err := os.WriteFile(scriptPath, []byte(scriptContent), 0644)
require.NoError(t, err)
req := httptest.NewRequest(http.MethodGet, "/install.sh", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedInstallScript(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, scriptContent, w.Body.String())
assert.Equal(t, "text/x-shellscript", w.Header().Get("Content-Type"))
}
// TestDownloadInstallScript_PublishedReleaseUnsignedLocalServesAgentInstaller
// pins the issue #1470 closure: when the local agent installer is present on a
// published-release server but its .sig/.sshsig sidecars are not, the endpoint
// must serve the LOCAL agent installer, never proxy the GitHub install.sh asset
// (which is the SERVER installer). This is the state every pre-fix LXC/systemd
// install is in until it redeploys the sidecars.
func TestDownloadInstallScript_PublishedReleaseUnsignedLocalServesAgentInstaller(t *testing.T) {
router, tempDir := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.0.0-rc.6"
// A client that fails any outbound call, so a regression that re-introduces
// the GitHub proxy fallback surfaces as a test failure rather than silently
// serving the server installer.
router.installScriptClient = newTestInstallScriptClient(t, http.MethodGet, "", 0, "", errors.New("proxy must not be used"))
scriptContent := "#!/usr/bin/env bash\n# Pulse Unified Agent Installer\necho 'agent'"
scriptPath := filepath.Join(tempDir, "scripts", "install.sh")
require.NoError(t, os.WriteFile(scriptPath, []byte(scriptContent), 0644))
// Note: no .sig / .sshsig sidecars written.
req := httptest.NewRequest(http.MethodGet, "/install.sh", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedInstallScript(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, scriptContent, w.Body.String())
assert.NotEqual(t, "github-fallback", w.Header().Get("X-Served-From"))
}
func TestDownloadInstallScriptPS_Local(t *testing.T) {
router, tempDir := setupUnifiedAgentRouter(t)
// Create dummy script
scriptContent := "Write-Host 'installing'"
scriptPath := filepath.Join(tempDir, "scripts", "install.ps1")
err := os.WriteFile(scriptPath, []byte(scriptContent), 0644)
require.NoError(t, err)
req := httptest.NewRequest(http.MethodGet, "/install.ps1", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedInstallScriptPS(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, scriptContent, w.Body.String())
assert.Equal(t, "text/plain", w.Header().Get("Content-Type"))
}
func TestDownloadUnifiedAgent_Local_Generic(t *testing.T) {
router, tempDir := setupUnifiedAgentRouter(t)
// Create dummy binary in project root / bin
binContent := validTestUnifiedAgentBinary("generic")
binPath := filepath.Join(tempDir, "bin", "pulse-agent")
err := os.WriteFile(binPath, binContent, 0755)
require.NoError(t, err)
// Since cachedSHA256 might not be initialized or working without real file usage pattern,
// checking if our manual Router setup handles it.
// cachedSHA256 needs 'checksumCache' map initialized which we did in setupUnifiedAgentRouter.
req := httptest.NewRequest(http.MethodGet, "/api/install/agent", nil)
w := httptest.NewRecorder()
// Handle calls r.cachedSHA256 which reads the file
router.handleDownloadUnifiedAgent(w, req)
// We expect success if cachedSHA256 works
if w.Code == http.StatusInternalServerError {
// If cachedSHA256 fails (maybe because it's not exported or implemented elsewhere
// and depends on something I missed), we will fail here.
// cachedSHA256 is called in unified_agent.go but defined presumably in router.go or router_utils.go (unexported).
// I initialized checksumCache so it should work.
t.Logf("Handler returned 500: %s", w.Body.String())
}
require.Equal(t, http.StatusOK, w.Code, w.Body.String())
assert.Equal(t, string(binContent), w.Body.String())
// Verify Checksum Header
hash := sha256.Sum256(binContent)
expectedChecksum := hex.EncodeToString(hash[:])
assert.Equal(t, expectedChecksum, w.Header().Get("X-Checksum-Sha256"))
}
func TestDownloadUnifiedAgent_Local_SpecificArch(t *testing.T) {
router, tempDir := setupUnifiedAgentRouter(t)
// Create dummy binary for linux-amd64
binContent := validTestUnifiedAgentBinary("linux-amd64")
binPath := filepath.Join(tempDir, "bin", "pulse-agent-linux-amd64")
err := os.WriteFile(binPath, binContent, 0755)
require.NoError(t, err)
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, string(binContent), w.Body.String())
}
func TestDownloadUnifiedAgent_LocalReleaseBinaryIncludesSignatureHeader(t *testing.T) {
router, tempDir := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.0.0"
binContent := validTestUnifiedAgentBinary("linux-amd64")
binPath := filepath.Join(tempDir, "bin", "pulse-agent-linux-amd64")
err := os.WriteFile(binPath, binContent, 0755)
require.NoError(t, err)
err = os.WriteFile(binPath+".sig", []byte("signed-local-agent"), 0644)
require.NoError(t, err)
err = os.WriteFile(binPath+".sshsig", []byte("signed-local-agent-ssh"), 0644)
require.NoError(t, err)
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=linux-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "signed-local-agent", w.Header().Get(signatureHeaderName))
assert.Equal(t, encodedTestSSHSignature("signed-local-agent-ssh"), w.Header().Get(sshSignatureHeaderName))
}
// Regression coverage for #1820: release images carry Windows binaries and
// detached signatures under the canonical .exe asset name. Looking up an
// extensionless compatibility symlink made signature verification probe the
// wrong sidecar names, reject the healthy local binary, and fall through to a
// GitHub asset that might not exist yet.
func TestDownloadUnifiedAgent_LocalWindowsReleaseUsesExecutableSignatures(t *testing.T) {
router, tempDir := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.4.2"
router.installScriptClient = newTestInstallScriptClient(t, http.MethodGet, "", 0, "", errors.New("proxy must not be used"))
binContent := validTestUnifiedAgentBinary("windows-amd64")
binPath := filepath.Join(tempDir, "bin", "pulse-agent-windows-amd64.exe")
require.NoError(t, os.WriteFile(binPath, binContent, 0755))
require.NoError(t, os.WriteFile(binPath+".sig", []byte("signed-windows-agent"), 0644))
require.NoError(t, os.WriteFile(binPath+".sshsig", []byte("signed-windows-agent-ssh"), 0644))
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=windows-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusOK, w.Code, w.Body.String())
assert.Equal(t, string(binContent), w.Body.String())
assert.Equal(t, "signed-windows-agent", w.Header().Get(signatureHeaderName))
assert.Equal(t, encodedTestSSHSignature("signed-windows-agent-ssh"), w.Header().Get(sshSignatureHeaderName))
assert.NotEqual(t, "github-proxy", w.Header().Get("X-Served-From"))
}
func TestUnifiedAgentLocalBuildCommandUsesWindowsExecutableName(t *testing.T) {
assert.Contains(t, unifiedAgentLocalBuildCommand("windows-amd64"), "-o bin/pulse-agent-windows-amd64.exe")
}
func TestDownloadUnifiedAgent_SkipsStaleLocalBinaryAndProxies(t *testing.T) {
router, tempDir := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.0.0-rc.1"
stalePath := filepath.Join(tempDir, "bin", "pulse-agent-linux-amd64")
require.NoError(t, os.WriteFile(stalePath, staleTestUnifiedAgentBinary("linux-amd64"), 0755))
binaryContent := "fresh github binary"
expectedURL := "https://github.com/rcourtman/Pulse/releases/download/v6.0.0-rc.1/pulse-agent-linux-amd64"
router.installScriptClient = newTestInstallScriptClientSequence(t, []expectedHTTPExchange{
{Method: http.MethodGet, URL: expectedURL, Status: http.StatusOK, Body: binaryContent},
{Method: http.MethodGet, URL: expectedURL + ".sig", Status: http.StatusOK, Body: "signed-agent"},
{Method: http.MethodGet, URL: expectedURL + ".sshsig", Status: http.StatusOK, Body: "signed-agent-ssh"},
})
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=linux-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, binaryContent, w.Body.String())
assert.Equal(t, "github-proxy", w.Header().Get("X-Served-From"))
assert.Equal(t, "signed-agent", w.Header().Get(signatureHeaderName))
assert.Equal(t, encodedTestSSHSignature("signed-agent-ssh"), w.Header().Get(sshSignatureHeaderName))
}
func TestDownloadUnifiedAgent_DevModeRejectsStaleLocalBinary(t *testing.T) {
router, tempDir := setupUnifiedAgentRouter(t)
router.serverVersion = "dev"
stalePath := filepath.Join(tempDir, "bin", "pulse-agent-linux-amd64")
require.NoError(t, os.WriteFile(stalePath, staleTestUnifiedAgentBinary("linux-amd64"), 0755))
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=linux-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusNotFound, w.Code)
assert.Contains(t, w.Body.String(), "stale or incompatible")
assert.Contains(t, w.Body.String(), legacyUnifiedAgentReportPath)
assert.Contains(t, w.Body.String(), "go build -o bin/pulse-agent-linux-amd64")
}
func TestDownloadUnifiedAgent_ProxyFromGitHub(t *testing.T) {
router, _ := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.0.0-rc.1"
// Ensure NO local files exist (temp dir is empty of binaries)
// Set up a mock HTTP client to simulate GitHub response
binaryContent := "fake binary content for proxy test"
expectedURL := "https://github.com/rcourtman/Pulse/releases/download/v6.0.0-rc.1/pulse-agent-linux-amd64"
router.installScriptClient = newTestInstallScriptClientSequence(t, []expectedHTTPExchange{
{Method: http.MethodGet, URL: expectedURL, Status: http.StatusOK, Body: binaryContent},
{Method: http.MethodGet, URL: expectedURL + ".sig", Status: http.StatusOK, Body: "signed-agent"},
{Method: http.MethodGet, URL: expectedURL + ".sshsig", Status: http.StatusOK, Body: "signed-agent-ssh"},
})
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=linux-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
// Should proxy the binary with checksum header instead of redirecting
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, binaryContent, w.Body.String())
assert.Equal(t, "github-proxy", w.Header().Get("X-Served-From"))
// Verify checksum header is present and correct
hash := sha256.Sum256([]byte(binaryContent))
expectedChecksum := hex.EncodeToString(hash[:])
assert.Equal(t, expectedChecksum, w.Header().Get("X-Checksum-Sha256"))
assert.Equal(t, "signed-agent", w.Header().Get(signatureHeaderName))
assert.Equal(t, encodedTestSSHSignature("signed-agent-ssh"), w.Header().Get(sshSignatureHeaderName))
}
func TestDownloadUnifiedAgent_ProxyFromGitHub_UsesConfiguredRepo(t *testing.T) {
t.Setenv("PULSE_GITHUB_REPO", "example/pulse-fork")
router, _ := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.0.0-rc.1"
binaryContent := "fake binary content for proxy test"
expectedURL := "https://github.com/example/pulse-fork/releases/download/v6.0.0-rc.1/pulse-agent-linux-amd64"
router.installScriptClient = newTestInstallScriptClientSequence(t, []expectedHTTPExchange{
{Method: http.MethodGet, URL: expectedURL, Status: http.StatusOK, Body: binaryContent},
{Method: http.MethodGet, URL: expectedURL + ".sig", Status: http.StatusOK, Body: "signed-agent"},
{Method: http.MethodGet, URL: expectedURL + ".sshsig", Status: http.StatusOK, Body: "signed-agent-ssh"},
})
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=linux-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, binaryContent, w.Body.String())
}
func TestDownloadUnifiedAgent_ProxyFromGitHub_Windows(t *testing.T) {
router, _ := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.0.0-rc.1"
binaryContent := "MZ fake windows binary"
expectedURL := "https://github.com/rcourtman/Pulse/releases/download/v6.0.0-rc.1/pulse-agent-windows-amd64.exe"
router.installScriptClient = newTestInstallScriptClientSequence(t, []expectedHTTPExchange{
{Method: http.MethodGet, URL: expectedURL, Status: http.StatusOK, Body: binaryContent},
{Method: http.MethodGet, URL: expectedURL + ".sig", Status: http.StatusOK, Body: "signed-agent"},
{Method: http.MethodGet, URL: expectedURL + ".sshsig", Status: http.StatusOK, Body: "signed-agent-ssh"},
})
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=windows-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, binaryContent, w.Body.String())
assert.NotEmpty(t, w.Header().Get("X-Checksum-Sha256"))
assert.Equal(t, "signed-agent", w.Header().Get(signatureHeaderName))
assert.Equal(t, encodedTestSSHSignature("signed-agent-ssh"), w.Header().Get(sshSignatureHeaderName))
}
func TestDownloadUnifiedAgent_ProxyFromGitHub_ArchiveFallback_Darwin(t *testing.T) {
router, _ := setupUnifiedAgentRouter(t)
router.serverVersion = "v9.9.9"
binaryContent := []byte("darwin arm64 binary payload")
archivePayload := buildTestTarGz(t, "pulse-agent-darwin-arm64", binaryContent)
binaryURL := "https://github.com/rcourtman/Pulse/releases/download/v9.9.9/pulse-agent-darwin-arm64"
signatureURL := binaryURL + ".sig"
sshSignatureURL := binaryURL + ".sshsig"
archiveURL := "https://github.com/rcourtman/Pulse/releases/download/v9.9.9/pulse-agent-v9.9.9-darwin-arm64.tar.gz"
router.installScriptClient = &http.Client{
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
switch req.URL.String() {
case binaryURL:
return &http.Response{
StatusCode: http.StatusNotFound,
Status: "404 Not Found",
Header: make(http.Header),
Body: io.NopCloser(strings.NewReader("not found")),
}, nil
case archiveURL:
return &http.Response{
StatusCode: http.StatusOK,
Status: "200 OK",
Header: make(http.Header),
Body: io.NopCloser(bytes.NewReader(archivePayload)),
}, nil
case signatureURL:
return &http.Response{
StatusCode: http.StatusOK,
Status: "200 OK",
Header: make(http.Header),
Body: io.NopCloser(strings.NewReader("signed-agent")),
}, nil
case sshSignatureURL:
return &http.Response{
StatusCode: http.StatusOK,
Status: "200 OK",
Header: make(http.Header),
Body: io.NopCloser(strings.NewReader("signed-agent-ssh")),
}, nil
default:
t.Fatalf("unexpected URL: %s", req.URL.String())
return nil, nil
}
}),
}
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=darwin-arm64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "github-proxy-archive", w.Header().Get("X-Served-From"))
assert.Equal(t, string(binaryContent), w.Body.String())
hash := sha256.Sum256(binaryContent)
expectedChecksum := hex.EncodeToString(hash[:])
assert.Equal(t, expectedChecksum, w.Header().Get("X-Checksum-Sha256"))
assert.Equal(t, "signed-agent", w.Header().Get(signatureHeaderName))
assert.Equal(t, encodedTestSSHSignature("signed-agent-ssh"), w.Header().Get(sshSignatureHeaderName))
}
func TestDownloadUnifiedAgent_ProxyFromGitHub_ArchiveFallback_UsesConfiguredRepo(t *testing.T) {
t.Setenv("PULSE_GITHUB_REPO", "example/pulse-fork")
router, _ := setupUnifiedAgentRouter(t)
router.serverVersion = "v9.9.9"
binaryContent := []byte("darwin arm64 binary payload")
archivePayload := buildTestTarGz(t, "pulse-agent-darwin-arm64", binaryContent)
binaryURL := "https://github.com/example/pulse-fork/releases/download/v9.9.9/pulse-agent-darwin-arm64"
signatureURL := binaryURL + ".sig"
sshSignatureURL := binaryURL + ".sshsig"
archiveURL := "https://github.com/example/pulse-fork/releases/download/v9.9.9/pulse-agent-v9.9.9-darwin-arm64.tar.gz"
router.installScriptClient = &http.Client{
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
switch req.URL.String() {
case binaryURL:
return &http.Response{
StatusCode: http.StatusNotFound,
Status: "404 Not Found",
Header: make(http.Header),
Body: io.NopCloser(strings.NewReader("not found")),
}, nil
case archiveURL:
return &http.Response{
StatusCode: http.StatusOK,
Status: "200 OK",
Header: make(http.Header),
Body: io.NopCloser(bytes.NewReader(archivePayload)),
}, nil
case signatureURL:
return &http.Response{
StatusCode: http.StatusOK,
Status: "200 OK",
Header: make(http.Header),
Body: io.NopCloser(strings.NewReader("signed-agent")),
}, nil
case sshSignatureURL:
return &http.Response{
StatusCode: http.StatusOK,
Status: "200 OK",
Header: make(http.Header),
Body: io.NopCloser(strings.NewReader("signed-agent-ssh")),
}, nil
default:
t.Fatalf("unexpected URL: %s", req.URL.String())
return nil, nil
}
}),
}
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=darwin-arm64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "github-proxy-archive", w.Header().Get("X-Served-From"))
assert.Equal(t, string(binaryContent), w.Body.String())
assert.Equal(t, "signed-agent", w.Header().Get(signatureHeaderName))
assert.Equal(t, encodedTestSSHSignature("signed-agent-ssh"), w.Header().Get(sshSignatureHeaderName))
}
func TestDownloadUnifiedAgent_ProxyFromGitHub_NotFound(t *testing.T) {
router, _ := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.0.0-rc.1"
router.installScriptClient = newTestInstallScriptClientSequence(t, []expectedHTTPExchange{
{Method: http.MethodGet, URL: "https://github.com/rcourtman/Pulse/releases/download/v6.0.0-rc.1/pulse-agent-linux-amd64", Status: http.StatusNotFound, Body: ""},
{Method: http.MethodGet, URL: "https://github.com/rcourtman/Pulse/releases/download/v6.0.0-rc.1/pulse-agent-v6.0.0-rc.1-linux-amd64.tar.gz", Status: http.StatusNotFound, Body: ""},
})
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=linux-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusNotFound, w.Code)
}
func TestDownloadUnifiedAgent_ProxyFromGitHub_Error(t *testing.T) {
router, _ := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.0.0-rc.1"
// GitHub is unreachable
router.installScriptClient = newTestInstallScriptClient(t, http.MethodGet, "https://github.com/rcourtman/Pulse/releases/download/v6.0.0-rc.1/pulse-agent-linux-amd64", 0, "", errors.New("connection refused"))
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=linux-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusServiceUnavailable, w.Code)
}
func TestDownloadUnifiedAgent_DevPrereleaseRejectsGitHubFallback(t *testing.T) {
router, _ := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.0.0-dev"
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=linux-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusNotFound, w.Code)
assert.Contains(t, w.Body.String(), "dev mode")
assert.Contains(t, w.Body.String(), "go build -o bin/pulse-agent-linux-amd64")
}
func TestDownloadUnifiedAgent_DevModeReportsRequestedLocalBuildCommand(t *testing.T) {
router, _ := setupUnifiedAgentRouter(t)
router.serverVersion = "dev"
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=darwin-arm64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusNotFound, w.Code)
assert.Contains(t, w.Body.String(), "Agent binary not found for \"darwin-arm64\" in dev mode.")
assert.Contains(t, w.Body.String(), "CGO_ENABLED=0 GOOS=darwin GOARCH=arm64")
assert.Contains(t, w.Body.String(), "go build -o bin/pulse-agent-darwin-arm64 ./cmd/pulse-agent")
assert.NotContains(t, w.Body.String(), "bin/pulse-agent-linux-amd64")
}
func TestUnifiedAgentLocalBuildCommandHandlesArmVariants(t *testing.T) {
assert.Equal(
t,
"CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -o bin/pulse-agent-linux-armv7 ./cmd/pulse-agent",
unifiedAgentLocalBuildCommand("linux-armv7"),
)
assert.Equal(
t,
"CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -o bin/pulse-agent-linux-armv6 ./cmd/pulse-agent",
unifiedAgentLocalBuildCommand("linux-armv6"),
)
}
func TestNormalizeUnifiedAgentArch(t *testing.T) {
tests := []struct {
input string
expected string
}{
{"amd64", "linux-amd64"},
{"x86_64", "linux-amd64"},
{"linux-amd64", "linux-amd64"},
{"arm64", "linux-arm64"},
{"aarch64", "linux-arm64"},
{"windows-amd64", "windows-amd64"},
{"darwin-arm64", "darwin-arm64"},
{"unknown", ""},
{"", ""},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
assert.Equal(t, tt.expected, normalizeUnifiedAgentArch(tt.input))
})
}
}
func buildTestTarGz(t *testing.T, name string, payload []byte) []byte {
t.Helper()
var buf bytes.Buffer
gzw := gzip.NewWriter(&buf)
tw := tar.NewWriter(gzw)
header := &tar.Header{
Name: name,
Mode: 0o755,
Size: int64(len(payload)),
}
require.NoError(t, tw.WriteHeader(header))
_, err := tw.Write(payload)
require.NoError(t, err)
require.NoError(t, tw.Close())
require.NoError(t, gzw.Close())
return buf.Bytes()
}
// writeAgentBinaryFixture builds a stand-in for a compiled agent. The real
// validator is a byte scan, so a file carrying the same needles exercises it
// faithfully without shipping a multi-megabyte binary into the test tree.
func writeAgentBinaryFixture(t *testing.T, reportPath string, versionString string) string {
t.Helper()
path := filepath.Join(t.TempDir(), "pulse-agent")
body := "\x7fELF padding " + reportPath + " more padding " + versionString + " trailing\n"
if err := os.WriteFile(path, []byte(body), 0o755); err != nil {
t.Fatalf("write agent fixture: %v", err)
}
return path
}
func TestExpectedAgentBinaryVersionDropsBuildMetadata(t *testing.T) {
cases := map[string]string{
// A dev server carries git metadata the agent never has.
"6.2.0-rc.8+git.44.gdd72bd149.dirty": "v6.2.0-rc.8",
"6.2.0-rc.8": "v6.2.0-rc.8",
"v6.1.2": "v6.1.2",
// Unknown versions disable the check rather than rejecting everything.
// "dev-pro" is the enterprise build's compiled-in placeholder: it is
// why the caller resolves the version through updates.GetCurrentVersion
// instead of the compiled-in serverVersion, because a dev server is
// exactly where agent binaries go stale and this value would silently
// switch the freshness check off there.
"dev": "",
"dev-pro": "",
"": "",
"not-a-version": "",
}
for input, want := range cases {
if got := expectedAgentBinaryVersion(input); got != want {
t.Errorf("expectedAgentBinaryVersion(%q) = %q, want %q", input, got, want)
}
}
}
func TestValidateUnifiedAgentBinaryRejectsStaleBuild(t *testing.T) {
// The shape that broke a live host: a binary on the canonical report
// contract, so every existing check passes, but built weeks earlier.
stale := writeAgentBinaryFixture(t, canonicalUnifiedAgentReportPath, "v6.0.5-54-gc862fb0ca0")
if err := validateUnifiedAgentBinary(stale, "v6.2.0-rc.8"); err == nil {
t.Fatal("a binary that does not carry this server's agent version must be refused")
} else if !strings.Contains(err.Error(), "stale build") {
t.Fatalf("error should name staleness, got %v", err)
}
// Premise check: without the version guard this same binary is accepted,
// which is exactly how it reached a host in the first place.
if err := validateUnifiedAgentBinary(stale, ""); err != nil {
t.Fatalf("premise check failed: the stale binary is supposed to pass every other check, got %v", err)
}
}
func TestValidateUnifiedAgentBinaryAcceptsMatchingBuild(t *testing.T) {
current := writeAgentBinaryFixture(t, canonicalUnifiedAgentReportPath, "v6.2.0-rc.8")
if err := validateUnifiedAgentBinary(current, "v6.2.0-rc.8"); err != nil {
t.Fatalf("a binary carrying this server's agent version must be served: %v", err)
}
}
func TestValidateUnifiedAgentBinaryStillRejectsLegacyContract(t *testing.T) {
// The version guard must not displace the endpoint guard: a correctly
// versioned binary on the deprecated report path is still refused.
legacy := writeAgentBinaryFixture(t, legacyUnifiedAgentReportPath, "v6.2.0-rc.8")
if err := validateUnifiedAgentBinary(legacy, "v6.2.0-rc.8"); err == nil {
t.Fatal("a binary on the deprecated report endpoint must still be refused")
}
}