mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-19 02:28:59 +00:00
Stabilize release validation boundaries
Authenticate global API tokens before cold tenant runtime resolution, lock the cross-org behavior into integration coverage, and refresh schema-14 telemetry browser proof. Contract-Neutral: Restores bounded cross-org token authorization and records verification without changing the API contract Change-source: pulse-maintainer
This commit is contained in:
parent
0df9747bd7
commit
e3c0990e15
6 changed files with 73 additions and 16 deletions
|
|
@ -1,15 +1,18 @@
|
|||
{
|
||||
"version": 1,
|
||||
"base_sha": "db7e26deac2a77dd5eff1dfb5bf2f1546683d5d6",
|
||||
"verified_at": "2026-08-29T10:03:51Z",
|
||||
"base_sha": "0df9747bd7ef97d768d5320e1952116ad4e675ea",
|
||||
"verified_at": "2026-08-29T13:46:17Z",
|
||||
"result": "passed",
|
||||
"changed_paths": [
|
||||
"frontend-modern/src/api/settings.ts"
|
||||
],
|
||||
"content_sha256": {
|
||||
"frontend-modern/src/api/settings.ts": "e54e7adfb7c66c8626e2cb0abcf20c1c7903b3d26e2f0050750a97a89811eab2"
|
||||
"frontend-modern/src/api/settings.ts": "6b3b67e14054d6b93a0eeb92107426384b4d2badeec7c52798b7c00cf6f5e920"
|
||||
},
|
||||
"routes": ["/settings/general"],
|
||||
"routes": [
|
||||
"/settings/system-general",
|
||||
"/docs/PRIVACY"
|
||||
],
|
||||
"viewports": [
|
||||
{
|
||||
"width": 1280,
|
||||
|
|
@ -21,14 +24,17 @@
|
|||
}
|
||||
],
|
||||
"states": [
|
||||
"authenticated General settings with outbound telemetry locked off by PULSE_TELEMETRY",
|
||||
"current heartbeat preview rendered from the schema-v13 backend at desktop width",
|
||||
"current heartbeat preview rendered from the schema-v13 backend at narrow width"
|
||||
"authenticated General settings with outbound telemetry disabled by PULSE_TELEMETRY=false",
|
||||
"schema-14 heartbeat payload preview expanded at desktop width",
|
||||
"schema-14 heartbeat payload preview expanded without document overflow at narrow width",
|
||||
"rotating pseudonymous install ID refreshed after operator confirmation",
|
||||
"shipped privacy document opened from the telemetry disclosure"
|
||||
],
|
||||
"interactions": [
|
||||
"opened General settings and selected Preview payload",
|
||||
"confirmed the preview explains that telemetry is disabled while still showing the exact payload Pulse would send",
|
||||
"confirmed schema_version 13 plus service_health_observed, service_health_healthy, and service_health_cohort in the rendered payload at 1280x800",
|
||||
"confirmed the same service-health payload fields remained visible and readable at 390x844"
|
||||
"opened General settings at 1280x800 and selected Preview payload",
|
||||
"confirmed schema_version 14 and representative alert severity, resolution-time, and persistence-health aggregates in the rendered payload",
|
||||
"resized the current interaction to 390x844, confirmed the payload stayed visible, and verified no horizontal document overflow",
|
||||
"selected Reset ID at narrow width, accepted the confirmation dialog, and confirmed the rendered install ID rotated",
|
||||
"opened Full details and confirmed the shipped /docs/PRIVACY document rendered in a browser popup"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ export interface TelemetryPingPreview {
|
|||
alerts_fired_30d: number;
|
||||
alerts_acknowledged_30d: number;
|
||||
alerts_resolved_30d: number;
|
||||
// Coarse lifecycle aggregates only: no alert text, resource identity, or tenant identity.
|
||||
active_alerts_info: number;
|
||||
active_alerts_warning: number;
|
||||
active_alerts_critical: number;
|
||||
|
|
|
|||
|
|
@ -166,8 +166,19 @@ func TestOrgScopedAPITokenCannotReadOtherOrgMembersWithSessionCookiePresent(t *t
|
|||
"X-Org-ID": "org-a",
|
||||
}
|
||||
assertMembersStatus("/api/orgs/org-a/members", headers, http.StatusOK)
|
||||
body := assertMembersStatus("/api/orgs/org-b/members", headers, http.StatusForbidden)
|
||||
if _, initialized := router.mtMonitor.PeekMonitor("org-b"); initialized {
|
||||
t.Fatal("cross-org target monitor initialized before the denied request")
|
||||
}
|
||||
crossOrgHeaders := map[string]string{
|
||||
"Authorization": "Bearer " + created.Token,
|
||||
"X-Pulse-Org-ID": "org-b",
|
||||
"X-Org-ID": "org-b",
|
||||
}
|
||||
body := assertMembersStatus("/api/orgs/org-b/members", crossOrgHeaders, http.StatusForbidden)
|
||||
if !strings.Contains(body, "access_denied") {
|
||||
t.Fatalf("expected cross-org denial payload, got %q", body)
|
||||
}
|
||||
if _, initialized := router.mtMonitor.PeekMonitor("org-b"); initialized {
|
||||
t.Fatal("denied global token request initialized the cross-org target monitor")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1322,6 +1322,21 @@ func extractAndStoreAuthContext(cfg *config.Config, mtm *monitoring.MultiTenantM
|
|||
if cfg.HasAPITokens() || mtm != nil {
|
||||
// Determine which config to use for validation (Global vs Tenant)
|
||||
targetConfig := cfg
|
||||
providedToken, explicitTokenProvided := explicitAPITokenFromRequest(r)
|
||||
|
||||
// Global tokens are the common control-plane credential. Authenticate
|
||||
// them before resolving the header-selected tenant monitor: resolving a
|
||||
// cold tenant can initialize its complete monitoring runtime, even when
|
||||
// the already-known global token is bound to another organization and
|
||||
// tenant authorization will reject the request. Besides wasting work on
|
||||
// an unauthorized request, that initialization can exceed an API client
|
||||
// timeout and leave cleanup queued behind the same cold start.
|
||||
if explicitTokenProvided {
|
||||
if record, ok := validateGlobalAPITokenLocked(cfg, providedToken); ok {
|
||||
attachAPITokenRecord(r, record)
|
||||
return attachUserContext(r, apiTokenAuthenticatedUser(record))
|
||||
}
|
||||
}
|
||||
|
||||
if mtm != nil {
|
||||
// Check for Tenant ID in header or cookie
|
||||
|
|
@ -1351,7 +1366,7 @@ func extractAndStoreAuthContext(cfg *config.Config, mtm *monitoring.MultiTenantM
|
|||
return nil, false
|
||||
}
|
||||
|
||||
if providedToken, provided := explicitAPITokenFromRequest(r); provided {
|
||||
if explicitTokenProvided {
|
||||
if req, ok := validateToken(providedToken); ok {
|
||||
return req
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,7 +279,6 @@ func TestHostedTenantAgentInstallTokenCannotReportToOtherTenant(t *testing.T) {
|
|||
require.True(t, ok)
|
||||
require.Len(t, monitorA.GetLiveHostsSnapshot(), 1)
|
||||
|
||||
monitorB, ok := mtm.PeekMonitor("client-b")
|
||||
require.True(t, ok)
|
||||
require.Empty(t, monitorB.GetLiveHostsSnapshot(), "retargeted client-a token must not write into client-b")
|
||||
_, ok = mtm.PeekMonitor("client-b")
|
||||
require.False(t, ok, "retargeted client-a token must be denied before client-b runtime initialization")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,10 @@ async function readTelemetryPreview(page: Page) {
|
|||
return JSON.parse((await preview.textContent()) ?? "{}") as {
|
||||
install_id: string;
|
||||
event: string;
|
||||
schema_version: number;
|
||||
active_alerts_warning: number;
|
||||
alerts_resolution_under_15m_30d: number;
|
||||
alert_active_state_persistence_degraded_tenants: number;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -75,6 +79,7 @@ test.describe("Telemetry disclosure", () => {
|
|||
"Desktop-only telemetry disclosure coverage",
|
||||
);
|
||||
|
||||
await page.setViewportSize({ width: 1280, height: 800 });
|
||||
await page.goto("/settings/system-general", {
|
||||
waitUntil: "domcontentloaded",
|
||||
});
|
||||
|
|
@ -121,6 +126,26 @@ test.describe("Telemetry disclosure", () => {
|
|||
const initialPreview = await readTelemetryPreview(page);
|
||||
expect(initialPreview.event).toBe("heartbeat");
|
||||
expect(initialPreview.install_id).toBeTruthy();
|
||||
expect(initialPreview.schema_version).toBe(14);
|
||||
expect(initialPreview.active_alerts_warning).toBeGreaterThanOrEqual(0);
|
||||
expect(
|
||||
initialPreview.alerts_resolution_under_15m_30d,
|
||||
).toBeGreaterThanOrEqual(0);
|
||||
expect(
|
||||
initialPreview.alert_active_state_persistence_degraded_tenants,
|
||||
).toBeGreaterThanOrEqual(0);
|
||||
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await expect(
|
||||
page.locator('pre[aria-label="Telemetry payload preview"]'),
|
||||
).toBeVisible();
|
||||
expect(
|
||||
await page.evaluate(
|
||||
() =>
|
||||
document.documentElement.scrollWidth <=
|
||||
document.documentElement.clientWidth,
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
page.once("dialog", (dialog) => dialog.accept());
|
||||
await page.getByRole("button", { name: "Reset ID" }).click();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue