mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -10166,6 +10166,38 @@
|
||||
"kind": "file"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "telemetry-test-binary-production-pings",
|
||||
"summary": "Go test binaries reported themselves to the production telemetry receiver as live installations. pkg/server tests boot the real server through Run() with the version literal \"test-version\", which internal/updates normalizes to 0.0.0-test-version, and each test runs against its own t.TempDir(), so every run minted a fresh install ID. The startup ping waits two minutes and never fired in a short test, but the service-health failure reporter added on 2026-08-29 sends synchronously from a deferred handler as soon as Run() returns an error, so every test run that exercised a startup failure posted one ping. The receiver recorded 317 single-ping installs between 2026-08-29 and 2026-09-03, 311 from linux/amd64 hosts (dominated by the autonomous maintainer fleet running ad-hoc go test, not GitHub Actions, which ran twice in the final 24h) and 3 from a maintainer workstation. The canonical clean denominator excludes single-ping installs and was unaffected, but raw install counts and the operator-evidence Patrol blocked-cause read counted them as real installations. Resolved by refusing production-endpoint sends from a test binary in internal/telemetry, opting the server tests out of telemetry, and putting the operator-evidence read on the production-ping basis. Note the receiver cannot filter these on version_is_development: the emitter sets that flag only for git build metadata or a prerelease of exactly dev or dev.*, so 0.0.0-test-version arrives with it clear and only version_is_published_release excludes it.",
|
||||
"owner": "project-owner",
|
||||
"status": "triaged",
|
||||
"recorded_at": "2026-09-03",
|
||||
"lane_ids": [
|
||||
"L14"
|
||||
],
|
||||
"subsystem_ids": [
|
||||
"security-privacy"
|
||||
],
|
||||
"proposed_resolution": "lane-expansion",
|
||||
"coverage_impact": 3,
|
||||
"evidence": [
|
||||
{
|
||||
"repo": "pulse",
|
||||
"path": "internal/telemetry/telemetry.go",
|
||||
"kind": "file"
|
||||
},
|
||||
{
|
||||
"repo": "pulse",
|
||||
"path": "pkg/server/server.go",
|
||||
"kind": "file"
|
||||
},
|
||||
{
|
||||
"repo": "pulse",
|
||||
"path": "pkg/server/server_test.go",
|
||||
"kind": "file"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"candidate_lanes": [
|
||||
|
||||
@@ -445,6 +445,15 @@ the `white_label` branding entitlement.
|
||||
runs per event so runtime mock toggles take effect immediately — because a
|
||||
mock-mode snapshot describes the synthetic fixture fleet rather than a real
|
||||
installation.
|
||||
A Go test binary is the same kind of suppression boundary: while
|
||||
`testing.Testing()` reports true, `internal/telemetry` must not post to the
|
||||
production receiver on any path, including the synchronous service-health
|
||||
failure event `pkg/server.Run` sends from its deferred handler. A test that
|
||||
boots the real server runs against a throwaway data directory, so it mints
|
||||
a fresh install ID on every run and the receiver counts it as a distinct
|
||||
live installation. The guard compares the resolved endpoint against
|
||||
`productionPingEndpoint`, so tests that redirect `pingEndpoint` at a local
|
||||
server keep asserting on real ping content.
|
||||
Pulse Intelligence external-agent/MCP telemetry may expose only content-free
|
||||
adapter-origin usage and capability-class counters for context, event
|
||||
stream, provisioning, operator state, finding, and action requests. It must
|
||||
|
||||
@@ -80,6 +80,14 @@
|
||||
// While mock/demo fixture mode is enabled, outbound pings are suppressed
|
||||
// entirely: a mock-mode boot (e2e, CI, qual runs, demo containers) would
|
||||
// otherwise report the synthetic fixture fleet as a real installation.
|
||||
//
|
||||
// # Test binaries
|
||||
//
|
||||
// Pings to the production endpoint are suppressed inside a Go test binary for
|
||||
// the same reason: a test that boots the real server is not an installation,
|
||||
// and its throwaway data directory mints a new install ID on every run. Tests
|
||||
// that need to assert on ping content redirect pingEndpoint to a local server,
|
||||
// which the guard deliberately allows.
|
||||
package telemetry
|
||||
|
||||
import (
|
||||
@@ -96,6 +104,7 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -104,12 +113,19 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// productionPingEndpoint is the live receiver for outbound usage telemetry.
|
||||
const productionPingEndpoint = "https://license.pulserelay.pro/v1/telemetry/ping"
|
||||
|
||||
// pingEndpoint is the URL that receives outbound usage telemetry pings.
|
||||
// It is a var (not const) so that tests can redirect it to a local server.
|
||||
var pingEndpoint = "https://license.pulserelay.pro/v1/telemetry/ping"
|
||||
var pingEndpoint = productionPingEndpoint
|
||||
|
||||
var errInstallIDUnavailable = errors.New("telemetry install id unavailable")
|
||||
|
||||
// errProductionEndpointUnderTest reports a ping suppressed because a test
|
||||
// binary tried to reach the live receiver.
|
||||
var errProductionEndpointUnderTest = errors.New("telemetry: refusing to post to the production endpoint from a test binary")
|
||||
|
||||
const (
|
||||
// heartbeatInterval is the base interval between daily pings.
|
||||
// Each cycle adds random jitter of ±maxHeartbeatJitter to prevent
|
||||
@@ -1731,6 +1747,15 @@ func buildPingAt(cfg Config, event string, now time.Time) (Ping, error) {
|
||||
// send posts a ping to the telemetry endpoint. Errors are observable in debug
|
||||
// logs but never affect normal Pulse operation.
|
||||
func send(ctx context.Context, ping Ping) error {
|
||||
// A test binary is not a real installation. Any test that boots the real
|
||||
// server (pkg/server.Run and anything like it) runs against a throwaway
|
||||
// data directory, so it mints a fresh install ID per run and would be
|
||||
// counted as a distinct live install. Telemetry's own tests redirect
|
||||
// pingEndpoint at a local server and are unaffected by this guard.
|
||||
if testing.Testing() && pingEndpoint == productionPingEndpoint {
|
||||
return errProductionEndpointUnderTest
|
||||
}
|
||||
|
||||
body, err := json.Marshal(ping)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -2,6 +2,7 @@ package telemetry
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
@@ -1366,3 +1367,61 @@ func TestTelemetryPrivacyDocsDiscloseSetupChoiceAndPayloadChanges(t *testing.T)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The production telemetry receiver must be unreachable from a Go test binary.
|
||||
// A test that boots the real server runs against a throwaway data directory,
|
||||
// so it mints a fresh install ID on every run and lands at the receiver as a
|
||||
// distinct live installation. Regression guard for the 317 single-ping
|
||||
// 0.0.0-test-version installs that pkg/server tests reported between
|
||||
// 2026-08-29 and 2026-09-03.
|
||||
func TestSendRefusesProductionEndpointUnderTest(t *testing.T) {
|
||||
if pingEndpoint != productionPingEndpoint {
|
||||
t.Fatalf("pingEndpoint = %q, want the production endpoint by default", pingEndpoint)
|
||||
}
|
||||
|
||||
err := send(context.Background(), Ping{Event: "startup"})
|
||||
if !errors.Is(err, errProductionEndpointUnderTest) {
|
||||
t.Fatalf("send() to the production endpoint = %v, want errProductionEndpointUnderTest", err)
|
||||
}
|
||||
}
|
||||
|
||||
// SendServiceHealthEvent is the path pkg/server.Run takes when startup fails,
|
||||
// and it sends synchronously rather than after the two-minute startup delay,
|
||||
// which is why the failing server tests reported and the passing ones did not.
|
||||
func TestSendServiceHealthEventRefusesProductionEndpointUnderTest(t *testing.T) {
|
||||
cfg := Config{
|
||||
Version: "test-version",
|
||||
DataDir: t.TempDir(),
|
||||
Enabled: true,
|
||||
}
|
||||
|
||||
err := SendServiceHealthEvent(context.Background(), cfg, "startup", ServiceHealthObservation{
|
||||
Observed: true,
|
||||
FailureCategory: ServiceHealthFailureListener,
|
||||
})
|
||||
if !errors.Is(err, errProductionEndpointUnderTest) {
|
||||
t.Fatalf("SendServiceHealthEvent() = %v, want errProductionEndpointUnderTest", err)
|
||||
}
|
||||
}
|
||||
|
||||
// A redirected endpoint is how telemetry's own tests assert on ping content,
|
||||
// so the guard must not block it.
|
||||
func TestSendAllowsRedirectedEndpointUnderTest(t *testing.T) {
|
||||
var received atomic.Int32
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
received.Add(1)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
origEndpoint := pingEndpoint
|
||||
pingEndpoint = ts.URL
|
||||
defer func() { pingEndpoint = origEndpoint }()
|
||||
|
||||
if err := send(context.Background(), Ping{Event: "startup"}); err != nil {
|
||||
t.Fatalf("send() to a redirected endpoint: %v", err)
|
||||
}
|
||||
if got := received.Load(); got != 1 {
|
||||
t.Fatalf("redirected endpoint received %d pings, want 1", got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,6 +294,9 @@ func waitForHTTPStatus(t *testing.T, url string, want int) {
|
||||
func TestServerRun_Shutdown(t *testing.T) {
|
||||
// Setup minimal environment
|
||||
tmpDir := t.TempDir()
|
||||
// Run() reports startup failures over outbound telemetry; a test boot is
|
||||
// not an installation, so opt this process out at the config layer too.
|
||||
t.Setenv("PULSE_TELEMETRY", "false")
|
||||
t.Setenv("PULSE_DATA_DIR", tmpDir)
|
||||
t.Setenv("PULSE_CONFIG_PATH", tmpDir)
|
||||
t.Setenv("BIND_ADDRESS", "127.0.0.1")
|
||||
@@ -325,6 +328,9 @@ func TestServerRun_Shutdown(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServerRunFailsFastWhenFrontendPortIsAlreadyBound(t *testing.T) {
|
||||
// Run() reports startup failures over outbound telemetry; a test boot is
|
||||
// not an installation, so opt this process out at the config layer too.
|
||||
t.Setenv("PULSE_TELEMETRY", "false")
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("listen: %v", err)
|
||||
@@ -347,6 +353,9 @@ func TestServerRunFailsFastWhenFrontendPortIsAlreadyBound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServerRunKeepsFrontendWhenMetricsPortConflicts(t *testing.T) {
|
||||
// Run() reports startup failures over outbound telemetry; a test boot is
|
||||
// not an installation, so opt this process out at the config layer too.
|
||||
t.Setenv("PULSE_TELEMETRY", "false")
|
||||
port := availableTCPPort(t)
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
@@ -381,6 +390,9 @@ func TestServerRunKeepsFrontendWhenMetricsPortConflicts(t *testing.T) {
|
||||
|
||||
func TestServerRun_RejectsWildcardTrustedProxyCIDR(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
// Run() reports startup failures over outbound telemetry; a test boot is
|
||||
// not an installation, so opt this process out at the config layer too.
|
||||
t.Setenv("PULSE_TELEMETRY", "false")
|
||||
t.Setenv("PULSE_DATA_DIR", tmpDir)
|
||||
t.Setenv("PULSE_CONFIG_PATH", tmpDir)
|
||||
t.Setenv("PULSE_TRUSTED_PROXY_CIDRS", "0.0.0.0/0")
|
||||
|
||||
Reference in New Issue
Block a user