Files
AutopilotMonitor/src/Backend/AutopilotMonitor.Functions.Tests/NoStoreCacheMiddlewareTests.cs
T
Oliver Kieselbach 9f7b070e80 chore: remove legacy V1 agent and its backend ingest path
The V1 agent line is retired: no new V1 sessions since 2026-05-09, only
orphaned straggler traffic remained on /api/agent/ingest (last seen
2026-06-29, events for long-cleaned-up sessions). Bootstrap/Intune/CI
have deployed V2-only since the v2 cutover.

Agent:
- Delete AutopilotMonitor.Agent, .Agent.Core, .Agent.Core.Tests
  (projects + sln entries). V2 trio, SummaryDialog, DecisionCore.Tests
  and V2Isolation.targets stay.

Backend:
- Relocate the shared statics/DTOs V2 reused from the V1 partials
  (StampServerFields, SanitizeEventTimestamps, IsMaxLifetimeAgentShutdown,
  RecalculateAppDurations, EventClassification, AppInstallAggregationState)
  into EventIngestProcessor; IngestEventsOutput moves next to
  IngestTelemetryFunction (same namespace, zero ripple).
- Delete IngestEventsFunction (route agent/ingest), BootstrapIngestEvents
  (route bootstrap/ingest), NdjsonParser and their DI registration.
  EventDataNormalizer stays (used by TelemetryPayloadParser).
- Drop both routes from EndpointAccessPolicyCatalog and
  RequestTelemetryMiddleware.TenantHeaderTrustedPaths.
- Retire the V1 update-hash oracle: LatestAgentV1*/LatestBootstrapV1*
  fields + GetAgentLine case 1 (default arm returns empty hashes, so
  stragglers gracefully skip the integrity check; ParseAgentMajor keeps
  its default of 1 on purpose — defaulting to 2 would feed V2 hashes to
  V1 binaries). TableConfigRepository Store+Map updated in lockstep.
- Remove ApiEndpoints.IngestEvents/.BootstrapIngestEvents constants.

Tests: NdjsonParserTests deleted; timestamp/critical-path/max-lifetime
tests repointed to EventIngestProcessor; the two NDJSON pipeline tests
removed (coverage retained by direct-list siblings); catalog/no-store
InlineData rows updated.

Docs: architecture.md (endpoint tables, ingest flow diagram, solution
layout, test matrix), web SectionAgentInternals endpoint tables,
build.ps1 help text.

Azure: no config change needed — /api/agent is not in the client-cert
exclusion paths (agent/ingest was always cert-enforced) and the
/api/bootstrap prefix exclusion remains for the surviving bootstrap
routes (verified via ARM on autopilotmonitor-api).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 08:53:20 +02:00

79 lines
3.4 KiB
C#

using AutopilotMonitor.Functions.Middleware;
namespace AutopilotMonitor.Functions.Tests;
public class NoStoreCacheMiddlewareTests
{
[Theory]
// Exact-match credential / identity routes
[InlineData("/api/auth/me")]
[InlineData("/api/agent/config")]
[InlineData("/api/agent/upload-url")]
[InlineData("/api/bootstrap/config")]
// Prefix-match routes — Bearer token (bootstrap/validate), tenant config, sessions
[InlineData("/api/bootstrap/validate/ABC123")]
[InlineData("/api/config/contoso-tenant-id")]
[InlineData("/api/config/contoso-tenant-id/bootstrap-tokens")]
[InlineData("/api/sessions/session-guid-here")]
[InlineData("/api/sessions/session-guid-here/events")]
// Session list + diagnostics-download surfaces — PII session payloads / proxied ZIPs
[InlineData("/api/sessions")]
[InlineData("/api/diagnostics/download-url")]
[InlineData("/api/diagnostics/download-ticket")] // short-lived signed download credential
[InlineData("/api/diagnostics/download")] // ticket-gated anonymous ZIP stream
// /api/search/* — quick, sessions, sessions-by-event, sessions-by-cve (all return session PII)
[InlineData("/api/search/quick")]
[InlineData("/api/search/sessions")]
[InlineData("/api/search/sessions-by-event")]
[InlineData("/api/search/sessions-by-cve")]
// GA cross-tenant raw + global PII surfaces
[InlineData("/api/raw/sessions")]
[InlineData("/api/raw/events")]
[InlineData("/api/global/sessions")]
[InlineData("/api/global/raw/sessions")]
[InlineData("/api/global/raw/events")]
[InlineData("/api/global/raw/tables/foo")]
[InlineData("/api/global/search/sessions")]
[InlineData("/api/global/search/sessions-by-cve")]
[InlineData("/api/global/distress-reports")]
[InlineData("/api/global/session-reports")]
[InlineData("/api/global/session-reports/download-url")] // 15-min SAS URL in response
[InlineData("/api/global/session-reports/report-id/note")]
// Case-insensitive
[InlineData("/API/Auth/Me")]
[InlineData("/api/Bootstrap/Validate/abc")]
[InlineData("/API/Diagnostics/Download-Url")]
[InlineData("/api/Global/Session-Reports/Download-Url")]
public void IsSensitive_ReturnsTrue_ForCredentialBearingRoutes(string path)
{
Assert.True(NoStoreCacheMiddleware.IsSensitive(path),
$"Expected '{path}' to be classified as sensitive.");
}
[Theory]
[InlineData("/api/health")]
[InlineData("/api/stats/platform")]
[InlineData("/api/agent/telemetry")]
[InlineData("/api/agent/register-session")]
[InlineData("/api/auth/global-admins")] // returns admin list, but no Bearer/SAS
[InlineData("/api/auth/is-global-admin")]
[InlineData("/api/rules")]
[InlineData("/api/rules/gather")]
[InlineData("/api/rules/analyze")]
[InlineData("/api/notifications")] // tenant bell — no SAS / token
[InlineData("/api/audit/logs")] // not in scope: see middleware allowlist commentary
[InlineData("/")]
[InlineData("")]
public void IsSensitive_ReturnsFalse_ForNonCredentialRoutes(string path)
{
Assert.False(NoStoreCacheMiddleware.IsSensitive(path),
$"Expected '{path}' to NOT be classified as sensitive (would over-stamp no-store).");
}
[Fact]
public void IsSensitive_NullPath_ReturnsFalse()
{
Assert.False(NoStoreCacheMiddleware.IsSensitive(null!));
}
}