Commit graph

1312 commits

Author SHA1 Message Date
rcourtman
faefe6edc8 Remove 198 unreachable Go functions
Dead-code sweep. Functions flagged unreachable by golang.org/x/tools/cmd/deadcode
and confirmed unused across pulse, pulse-enterprise, pulse-pro and pulse-mobile by
adversarial cross-repo verification. Cross-module reachability was checked
explicitly (only pkg/ exported symbols are importable by other modules; internal/
packages and _test.go files are not). go build, go vet and test-compile all pass.
2026-06-03 12:29:37 +01:00
rcourtman
e17916e3bc Surface v5 agent migration security guidance
Warn when update readiness includes v5 agents and document the trusted-transport first hop.
2026-06-03 09:18:07 +01:00
rcourtman
1afd708e5a Harden unified agent update preflight
Record the single pulse-agent product invariant and clarify Docker / Podman module terminology.
2026-06-02 23:54:48 +01:00
rcourtman
c7e50d5602 Harden provider-hosted MSP isolation
Broker provider control-plane Docker access through a socket proxy, remove broad host mounts, align audit and rate-limit proxy trust, harden tenant runtime containers, restrict workspace report logo paths, and update provider deploy guardrails.
2026-06-02 21:10:13 +01:00
rcourtman
d6964832a0 Add entitlement-gated report branding 2026-06-02 18:11:25 +01:00
rcourtman
d729461cad Harden provider MSP tenant isolation
- isolate provider MSP tenant runtimes onto per-client Docker networks
- add adversarial proofs for workspace cap races, handoff retargeting, and org-bound agent report retargeting
- pin provider deploy proof to start Traefik before tenant network creation
2026-06-02 17:34:10 +01:00
rcourtman
3f20c85e86 Add provider MSP proof command
Adds a provider-hosted MSP proof command that exercises provider bootstrap, workspace creation, hosted tenant install-token generation, handoff exchange, setup-facts visibility, and cross-tenant token isolation.
2026-06-02 12:45:48 +01:00
rcourtman
57486cee88 Harden MSP onboarding proof gaps 2026-06-02 10:15:35 +01:00
rcourtman
00f6ee9daa Make MSP workspace onboarding actionable 2026-06-01 22:41:10 +01:00
rcourtman
a3e36d787f Remove the /install.sh GitHub proxy fallback; serve bundled agent installer or fail closed (#1470)
Root fix for the recurring class behind issue #1470. The served /install.sh and
/install.ps1 endpoints existed to hand out the unified AGENT installer, but their
GitHub fallback fetched the top-level install.sh release asset, which since
49412357a is the SERVER installer. Prior commits made that fallback unreachable
in normal deployments (deploy the sidecars; serve the local script even when
unsigned), but the endpoint was still structurally capable of serving the wrong
script in the no-local-bundle case.

The agent installer is a per-build artifact bundled into every release tarball and
Docker image, not a release asset, so the endpoint has no business proxying a
release asset at all. Remove proxyInstallScriptFromGitHub and its
installScriptReleaseAssetURL wrapper. handleDownloadInstallScriptCommon now serves
the locally bundled agent installer (signed when sidecars are present, unsigned
otherwise) or fails closed with 503 when no bundled script exists. Serving the
SERVER installer at this endpoint is now structurally impossible, not merely
unreachable.

The shared version-pinning (releaseAssetTag/releaseAssetURL) and installScriptClient
remain for the agent-BINARY download proxy, which legitimately fetches published
release assets; its version-pinning stays covered by the agentBinaryReleaseAssetURL
contract tests.

Replace the obsolete install-script proxy tests with fail-closed assertions
(including a guard that the endpoint makes no outbound call), drop the four
installScriptReleaseAssetURL contract tests, and revise the four subsystem
contracts that pinned the install-script fallback transport (api-contracts items
8 and 27, agent-lifecycle item 14, storage-recovery item 14, plus the
deployment-installability note) to state that install scripts are served local or
fail closed with no GitHub fallback.
2026-05-29 14:51:18 +01:00
rcourtman
191ddfdfc8 Never serve the server installer at /install.sh; serve local agent installer (#1470)
The "Install on Linux/Windows" wizard does `curl -fsSL <server>/install.sh |
bash -s -- --url ...` and never verifies the response signature headers (curl|bash
discards them). But for published releases handleDownloadInstallScriptCommon
proxied the top-level GitHub install.sh asset whenever the local agent installer
lacked its .sig/.sshsig sidecars, and since 49412357a that asset is the SERVER
installer, which rejects --url. Every install missing the sidecars served the
wrong script. The companion deploy_agent_scripts fix deploys the sidecars for new
installs, but existing boxes stay broken until they redeploy.

Serve the locally bundled agent installer when its signatures are absent instead
of proxying. An unsigned-but-correct local script beats a signed-but-wrong proxied
one when nothing verifies the headers, and this retroactively fixes already-deployed
boxes the moment they get the new binary. The proxy now runs only when no local
installer is bundled at all, so the endpoint can no longer hand the agent wizard a
server installer in any reachable deployment state. New installs still ship the
sidecars and are served signed.

Revise the install-script signature/fallback contract this changes, across the
three subsystems that pin it (api-contracts item 8, agent-lifecycle item 14,
storage-recovery item 14) plus the deployment-installability note, to state that
the served endpoint serves the agent installer with correctness outranking
signature presence. Add a handler guard asserting a published-release server with
a present-but-unsigned local installer serves it locally and does not proxy.
2026-05-29 14:19:04 +01:00
rcourtman
2f0a5a818f Make alert-triggered Patrol investigate the specific breach
Previously an alert that triggered Patrol ran a broad health check that
explicitly ignored the threshold breach. Now an alert carries its real
payload (metric type, value, threshold, identifier, level, message) into
the patrol scope, and the alert_fired run is framed around root-causing
that specific breach instead of a general assessment.

Three coordinated changes:

- Carry the alert payload into PatrolScope.AlertContext through the alert
  bridge (PatrolTriggerEvent), so the patrol prompt sees the breach
  specifics rather than just an alert-type string.
- Frame alert_fired patrol runs around the breach: replace the
  "ignore threshold breaches" instruction with a root-cause directive
  targeting the alert's metric and threshold.
- Add per-rule control via AIConfig.AlertTriggersInvestigation: a master
  enable, a minimum-severity floor (patrol_alert_trigger_min_severity,
  default critical-only), and an optional alert-type allowlist
  (patrol_alert_trigger_types). The router's bridge callback consults the
  policy and drops non-qualifying alert_fired events before queuing a
  scoped patrol. A config-panel selector persists the severity floor.

Adds config, handler, and frontend proof tests, and updates the affected
subsystem contracts.
2026-05-28 22:42:32 +01:00
rcourtman
75d1a0477b Enforce upgrade readiness before v6 updates
Some checks are pending
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Frontend & Backend (push) Waiting to run
Refs #1476
2026-05-28 17:22:01 +01:00
rcourtman
e1d1ec1d7c Add update readiness checks for v6 upgrades
Surface server upgrade readiness on update plans so v5 agent continuity and agent token scope issues are checked before install.
2026-05-28 16:47:49 +01:00
rcourtman
aa4a5fa631 Add agent provisioning capabilities 2026-05-28 16:31:19 +01:00
rcourtman
f7a40f3a22 Add Proxmox setup doctor repair flow 2026-05-28 15:51:31 +01:00
rcourtman
79a501e092 Lighten AI intelligence handler test setup 2026-05-28 15:10:39 +01:00
rcourtman
7297aae4af Fix late issue triage regressions
Refs #1341

Refs #1429

Refs #1469

Refs #1472

Refs #1476

Refs #1481
2026-05-28 14:49:20 +01:00
rcourtman
bbf047f7c4 Reduce API chart contract seed volume 2026-05-28 14:32:19 +01:00
rcourtman
5a22b04c4c Harden audit log store failures
Refs #1464
2026-05-28 13:44:20 +01:00
rcourtman
80aab47da6 Stop swallowing alert config persistence errors
Forward-port of the release/5.1 fix (2c46c6c2d).

UpdateAlertConfig used to log SaveAlertConfig failures and still tell
the client "saved successfully", leaving the in-memory state with the
new override but the on-disk file untouched. On the next config reload
or process restart, the override silently vanished and the user saw
their threshold "revert" with no surfaced error. Return HTTP 500 with
the persistence error so the frontend can show a real save-failed
toast instead of false confidence.
2026-05-28 13:15:55 +01:00
rcourtman
cd278bf5e2 Surface alert override identities in diagnostics
Forward-port of the release/5.1 fix (9ac7df976). buildAlertsDiagnostic
previously emitted only cooldown/grouping flags, so triaging support
cases like #1341 where a user suspects an override key mismatch
required asking them to paste alerts config from inside their
container. Add an Overrides slice that names each persisted key with
its thresholds and disabled flags. Sanitize mode in the frontend
redacts the keys to override-N while keeping thresholds visible.
2026-05-28 10:56:21 +01:00
rcourtman
7cb5f74db8 Bump WebSocket read deadlines from 2s to 15s in router integration tests
The four ReadDeadline(time.Now().Add(2 * time.Second)) calls in
router_integration_test.go (lines 1496, 1543, 1573, 1682) were
producing 'read tcp: i/o timeout' failures in CI under -race while
passing locally. The 2-second window is enough to read the welcome
+ initialState messages on a quiet dev workstation but too tight
once the runner is loaded with cumulative test work and the race
detector overhead. rc.5 cleared the same tests in CI but recent
fixture-size growth (k8s clusters 1->3 in 7938f28de plus the SMART
disk-temperature mock data added in 23ea4e487) pushed the
end-to-end server-start-to-welcome-message latency past the 2s
budget. Bumping to 15s gives CI breathing room without affecting
local test duration (the deadline only takes effect when the read
is genuinely stuck).
2026-05-27 18:36:33 +01:00
rcourtman
8769f07eea Land SMART/SSH temperature feature, rc.6 finalization, and post-IA-revert governance reconciliation 2026-05-27 15:27:25 +01:00
rcourtman
d5589cc8ca Make agent machine onboarding seamless 2026-05-26 09:07:59 +01:00
rcourtman
7470b62a01 Fix Proxmox PBS backup artifact surface 2026-05-25 21:47:12 +01:00
rcourtman
bfafe559f6 Classify agentless machine availability targets 2026-05-25 21:38:43 +01:00
rcourtman
2b3295e6f0 Speed up workload charts with TTL cache and remove redundant clones
handleWorkloadCharts: add a 3s TTL response cache + per-key singleflight
so repeated sparkline polls from dashboards share work. TestSLO_WorkloadCharts
p95 drops from 188ms (over the 90ms target) to 838µs because most polls
hit cache or coalesce. Cold-call cost is unchanged; the win is dedup of
the steady-state polling pattern.

Cache + singleflight state live on the Router struct, not as globals, so
tenants don't cross-contaminate and tests don't pollute each other. Same
treatment for stateComputeGroup added in the previous commit.

monitor_metrics.mergeMetricHistory: drop the defensive cloneMetricPointMap
of base and cloneMetricSeries of candidate inside the merge. Every caller
already passes a freshly-owned map (from filterChartMetricMap,
GetAllGuestMetrics, GetAllStorageMetrics, queryStore*), so the internal
clones were pure waste. Caller-side cloneMetricPointMap is removed in
the three sites where the input is owned. ~6% allocation reduction on
BenchmarkHandleWorkloadCharts_StoreBacked.

Contract-neutral: no endpoint, response body, header, or wire-format
change.
2026-05-25 20:35:58 +01:00
rcourtman
0b57527b29 Dedup concurrent /api/state and /api/diagnostics with singleflight
Under load, 5x concurrent /api/state degraded from 276ms (single) to ~4s
each (linear), because every caller serialized on the monitor lock to
rebuild and JSON-encode the full 1.6MB state. /api/diagnostics had the
same dogpile shape on cache miss, even though its 45s TTL cache was
working as designed.

Wrap both handlers in a per-tenant singleflight.Group so concurrent
callers share the work: 20x concurrent /api/state now completes in 358ms
wall (~14-50x improvement). Diagnostics warm-cache responses are now
sub-3ms; cold compute coalesces.

Also drop websocket Upgrader buffers from 4MB read/write to 64KB. gorilla
streams larger payloads across the buffer transparently, so the 4MB
allocation per connection was overhead that scaled badly with concurrent
clients (100 clients * 8MB = 800MB just in buffers).

Contract-neutral: no endpoint, response body, header, or wire-format
change.
2026-05-25 20:07:02 +01:00
rcourtman
b6af1d233e Wire platform poller state into connections ledger
Refs #1469
2026-05-25 18:20:11 +01:00
rcourtman
052e344e1b Add Kubernetes RBAC inventory to the agent + canonical + UI
Some checks are pending
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Frontend & Backend (push) Waiting to run
Closes the only API-coverage gap from the Docker / Kubernetes IA
maturity review: Roles, ClusterRoles, RoleBindings, and
ClusterRoleBindings now flow from the Kubernetes agent through the
canonical resource registry into the Kubernetes platform-page
Configuration tab.

Agent: pkg/agents/kubernetes/report.go gains four new report struct
types that carry summary counts plus subject-kind sets; individual
subject names and full PolicyRule contents are deliberately omitted
so Pulse stays a "what permissions exist where" surface, not an RBAC
enumeration tool. internal/kubernetesagent/agent.go gains four
collectors that call rbacv1.RoleList/ClusterRoleList/etc. through the
existing runKubernetesCallWithRetry wrapper, matching the
ServiceAccount collector's RBAC-forbidden retry pattern.

Canonical: internal/models mirrors with NormalizeCollections coverage;
convert* funcs in internal/monitoring/kubernetes_agents.go translate
agent report -> model; ResourceTypeK8sRole / K8sClusterRole /
K8sRoleBinding / K8sClusterRoleBinding join the canonical type set;
registry ingest* + adapter resourceFrom* functions emit one Resource
per RBAC object with ruleCount / roleKind / roleName / subjectCount /
subjectKinds / aggregationLabels on the K8s meta; search mapping in
internal/api/resources.go and the privacy allow-list in
internal/api/org_handlers.go pick up the four new type tokens; the
K8s privacy category in unifiedresources/policy_metadata.go classifies
them like the rest of K8s.

Frontend: ResourceType union + ResourceKubernetesMeta carry the new
kinds and RBAC summary fields; KubernetesPageSurface query asks for
them; the page model buckets them into the Configuration group;
KubernetesConfigTable renders Role / ClusterRole rule counts and the
aggregated flag, plus RoleBinding / ClusterRoleBinding role refs and
"N subjects · Kind1, Kind2 +overflow" subject summaries.

Curated demo seeds per-namespace Roles + RoleBindings plus an
aggregated ClusterRole + ClusterRoleBinding for pulse-demo-monitoring
in each cluster so the Configuration tab renders 18 RBAC rows across
the three demo clusters.

Contracts updated for the canonical-shape guard: monitoring,
api-contracts, unified-resources, frontend-primitives,
organization-settings (canonical) plus agent-lifecycle and
storage-recovery (dependent via Extension Points). Verification
proofs extended: kubernetes_registry_test.go, kubernetes_agents_test.go,
agent_inventory_test.go (new TestCollectRBACInventoryReportsSummaryCountsOnly
that pins the subject-name-omission contract), demo_scenarios_test.go,
adapter_coverage_test.go, contract_test.go, org_handlers_test.go,
resourceIdentity.test.ts, reportingResourceTypes.test.ts,
KubernetesConfigTable.test.tsx, and the
subsystem_lookup_test.py line-anchor bumps that the contract edits
shifted (api-contracts 246 -> 253, organization-settings 92 -> 93).

Verified:
- go build ./internal/... ./cmd/... clean
- go test ./internal/unifiedresources/..., ./internal/mock/...,
  ./internal/kubernetesagent/..., ./internal/api/...,
  the K8s subset of ./internal/monitoring/... all clean (three
  pre-existing unrelated monitoring failures noted earlier remain
  unchanged by this commit)
- npm run type-check, lint:eslint, lint:theme,
  lint:canonical-platforms clean
- vitest: 70 K8s frontend tests pass including the new RBAC render
  coverage in KubernetesConfigTable.test.tsx
- browser proof on /kubernetes/configuration: 36 config rows
  including 18 RBAC rows across three clusters; ClusterRole
  "pulse-demo-monitoring" shows "12 rules · Aggregated";
  ClusterRoleBinding shows "3 subjects · Group, ServiceAccount +1"
2026-05-25 09:25:03 +01:00
rcourtman
120dd5353a Expand Docker Swarm metadata inventory 2026-05-24 12:07:10 +01:00
rcourtman
0d67ca1b4a Expand Kubernetes API-native inventory surfaces
Collect native Kubernetes config, policy, and autoscaling objects.
Project the new resource types through API filters, unified resources, mock fixtures, and Kubernetes tabs.
Keep Secret inventory metadata-only and route k8s-secret policy as restricted local-only.
2026-05-24 11:12:33 +01:00
rcourtman
89abed099c Expand Docker runtime inventory coverage 2026-05-24 10:24:42 +01:00
rcourtman
f18502fc24 Expand Kubernetes native inventory coverage 2026-05-24 09:40:58 +01:00
rcourtman
6346929328 Expand Docker and Kubernetes platform projections 2026-05-24 08:58:02 +01:00
rcourtman
5016cbc2ba Add vSphere network inventory
Project vCenter network inventory through canonical resources and add the vSphere Networks table backed by vCenter network topology. Align resource presentation coalescing so state and resource APIs share the same host contract.
2026-05-22 20:26:56 +01:00
rcourtman
3cd1517883 Surface vSphere activity timeline
Add a global resource timeline endpoint for provider activity and wire vSphere Activity to VMware timeline changes. Seed mock VMware activity through the same supplemental-change path and keep the relevant resource contract tests current.
2026-05-22 08:54:43 +01:00
rcourtman
1118e09fcd Clear server-side CSRF token on logout
Some checks failed
Build and Test / Secret Scan (push) Has been cancelled
Build and Test / Frontend & Backend (push) Has been cancelled
clearSession invalidated the session-store record and zeroed both
session/CSRF cookies on the client side, but never deleted the
matching entry from the CSRF store. The orphaned record stuck around
for up to 4 hours (CSRF TTL) after every logout. Not exploitable —
the session itself was gone, so the stale CSRF entry could not be
used — but the asymmetry was real: InvalidateUserSessions (password
change) and InvalidateOldSessionFromRequest (re-login) both already
called DeleteCSRFToken, and logout was the missing path.

Add the same call in clearSession alongside InvalidateSession.

Regression test TestClearSession_DeletesServerSideCSRFToken sets up a
real session + CSRF token pair, calls clearSession with a request
carrying the session cookie, and asserts the CSRF token no longer
validates against the session ID afterward.
2026-05-21 16:26:00 +01:00
rcourtman
77a6a8c695 Read RBAC identity from request context, not response header
RequirePermission was reading the authenticated username from
w.Header().Get("X-Authenticated-User"). The header is set by checkAuth
upstream, but response headers are mutable across the handler chain:
any middleware sitting between checkAuth and RequirePermission that
wrote that header would substitute an arbitrary identity, and the
RBAC authorizer would make its access decision against the
substituted value. Identity belongs on the request context, not in a
mutable response surface.

Read internalauth.GetUser(r.Context()) first — checkAuth already
calls attachUserContext on every auth path that produces a real user,
so the context is the authoritative source. Keep a defensive,
warn-logged fallback to the response header for the one upstream path
that sets the header but skips attachUserContext (the anonymous-user
flow), so the existing test contract is preserved while the warning
flags the source path to plug.

Regression test TestRequirePermissionUsesContextUsername constructs
the attack: a request whose context says "real-authenticated-user"
and whose response header says "evil-spoofed-user". The RBAC
authorizer must observe the context value.
2026-05-21 16:19:25 +01:00
rcourtman
0ac04c8d39 Validate SAML LogoutResponse before clearing session
handleSAMLSLO was an unauthenticated force-logout DoS: any cross-origin
GET or POST to /api/saml/{id}/slo cleared the user's session and
returned a 302 to /?logout=success. No SAML payload was required, no
signature validation, no IdP-identity check.

Add ValidateLogoutResponse to SAMLService wrapping crewjam/saml's
ServiceProvider.ValidateLogoutResponseRequest, which validates the
XML-DSig against the configured IdP certificate and the standard
LogoutResponse temporal and target invariants.

In handleSAMLSLO:
  - extract and validate the provider ID
  - require either a SAMLResponse (response to our SLO request) or a
    SAMLRequest (IdP-initiated SLO) parameter — reject bare requests
    with 400 and do not clear session
  - reject IdP-initiated LogoutRequest with 400: we don't track
    in-flight request IDs to bind one to yet, and silently treating it
    as a force-logout signal is the same bug we are closing
  - on SAMLResponse, call service.ValidateLogoutResponse. On failure
    return 403 and do not clear session; audit-log the failure
  - only when validation passes do we clear the session and redirect

Regression tests:
  - TestHandleSAMLSLO_RejectsEmptyPayload (400, no redirect, no session
    state mutation)
  - TestHandleSAMLSLO_RejectsInvalidLogoutResponse (403 on garbage
    SAMLResponse value)
  - TestHandleSAMLSLO_RejectsIDPInitiatedLogoutRequest (400 on
    SAMLRequest-only payload)

The prior TestHandleSAMLSLO_Redirects test, which asserted the unsafe
"clear session and 302 on any GET" behaviour, is replaced by the
three above.
2026-05-21 16:15:53 +01:00
rcourtman
d890904ce6 Stop auto-escalating SameSite to None on proxied requests
getCookieSettings auto-set SameSite=None on the session and CSRF
cookies whenever a request arrived via a trusted proxy with
X-Forwarded-Proto: https. The original intent was "be more permissive
for proxied deployments," but in practice that disabled the
browser-side CSRF defence for every Pulse deployment behind a reverse
proxy — which is nearly all of them.

SameSite=None tells the browser to attach the cookie on arbitrary
cross-site requests. That is only required for cross-origin iframe
embedding scenarios Pulse does not document or support. The
top-level-navigation cases that actually matter (OIDC/SAML callback
landing, Cloudflare-tunnel access from a bookmark) all work under Lax,
which still attaches cookies on top-level navigations and only blocks
cross-site sub-resource requests.

Always return SameSiteLaxMode. The Secure flag still tracks the
actual connection state via isConnectionSecure(r). Together with the
earlier CSRF bypass fix (require token regardless of Authorization
header), the browser-side and server-side defences are now both in
place.

Test expectations updated: three cases that previously asserted
SameSiteNoneMode for the proxied-HTTPS / Cloudflare-tunnel / direct-
TLS-with-Forwarded paths now assert SameSiteLaxMode, with comments
flagging them as regression coverage.
2026-05-21 16:11:30 +01:00
rcourtman
bc2adac8b5 Stabilize dev runtime and complete PMG read-state migration
Refs architecture post-RC canonicalization follow-up.
2026-05-21 15:15:23 +01:00
rcourtman
0f4c831fab Reject expired trials in hosted subscription gate
isHostedSubscriptionValid was treating any tenant with a non-nil
TrialEndsAt as a valid bounded trial, never comparing the timestamp to
the current time. A tenant whose trial ended in the past but whose
stored subscription state was still "trial" (e.g. lease refresh failed
or never ran) retained full hosted Cloud access indefinitely.

The validity rule now requires both that a trial end timestamp exists
AND that time.Now() is before it. Refactored the switch to make the
trial branch read explicitly: nil evaluator -> deny, nil end -> deny,
past end -> deny, otherwise allow.

Added TestTenantMiddleware_HostedMode_ExpiredTrial_Blocked as the
regression test (mirrors the existing BoundedTrial_Allowed +
UnboundedTrial_Blocked pair with a past-dated trial end).
2026-05-21 12:33:10 +01:00
rcourtman
bb3514e316 Require CSRF token regardless of Authorization header
CheckCSRF was skipping the CSRF check whenever the request carried
Authorization: Bearer, Authorization: Basic, or X-API-Token, without
validating the credential. An attacker on a cross-origin page could
fetch() any state-changing endpoint with credentials: 'include' plus an
arbitrary Authorization header — the browser would auto-attach the
victim's pulse_session cookie, the server would skip CSRF, and the
request would execute as the logged-in user. Full CSRF bypass for every
session-authenticated user.

CSRF protection exists because the browser auto-attaches the session
cookie. That cookie is the only auto-attached credential we issue, so
it is the only correct signal for whether CSRF applies. Header-based
auth is set explicitly per request and is not CSRF-vulnerable, but its
presence does not make a session-cookie-bearing request safe. Skip CSRF
only when no session cookie is present; otherwise require the token
regardless of any Authorization or X-API-Token header.

Tests: the three "header bypasses CSRF" cases in security_test.go were
passing only because they sent no session cookie (so the no-cookie path
returned true). Renamed those to make the no-cookie precondition
explicit. Added TestCheckCSRF_HeaderDoesNotBypassWhenSessionCookiePresent
in security_regression_test.go covering X-API-Token, Authorization:
Basic, Authorization: Bearer, and mixed-case Bearer with a session
cookie present — each must require a valid CSRF token.
2026-05-21 12:29:32 +01:00
rcourtman
9ef4e588b7 Add TrueNAS services and stabilize dev startup 2026-05-21 12:08:25 +01:00
rcourtman
497390ff95 Scope TrueNAS resources by appliance
Scope TrueNAS child source IDs under the system source key so duplicate pool, dataset, app, VM, share, and disk names from different appliances do not merge.

Use the resulting hierarchy for per-system TrueNAS overview counts and scoped mock metric targets.
2026-05-21 00:59:00 +01:00
rcourtman
0801ede8c3 Add native TrueNAS network share inventory 2026-05-20 23:50:37 +01:00
rcourtman
3fa46aa836 Add native TrueNAS VM inventory to overview
Project TrueNAS vm.query inventory into canonical VM resources and render it on the TrueNAS overview.
2026-05-20 22:32:46 +01:00
rcourtman
142236d797 Seed mock Discovery fixtures 2026-05-20 14:54:19 +01:00