Files
AutopilotMonitor/src/Backend/AutopilotMonitor.Functions.Tests/SessionReportZipEntryNameTests.cs
T
Oliver Kieselbach 89c5c7da03 feat: richer, self-describing session report content
The report ZIP now captures everything an operator needs even after the
session is gone, and states its own completeness:

- session.csv: append the 15 newer Sessions fields that had drifted out
  of the export (failureSource, reconcileReason, adminMarkedAction,
  validatedBy, rebootCount, isHybridJoin, isSelfDeployingProfile,
  connectionType, geo*, avgApiLatencyMs, apiRequestCount, stalledAt,
  failureSnapshotJson)
- completeness signal: exportedEventCount / sessionEventCount /
  eventStreamActive in the request and report-metadata.json; the modal
  warns while event pages are still streaming
- includedDiagnostics records sourceBlobName/sourceDestination so a
  failed copy still points at the original archive
- tenantContext snapshot (fail-soft): upload mode/destination, gather-
  rule debug flag, custom gather/analyze rule IDs + UpdatedAt, IME
  pattern IDs — no secrets, no definitions
- robustness: 8 MB client-side screenshot cap (20 MB body cap headroom),
  attachments status in metadata instead of silently dropping invalid
  base64, SanitizeZipEntryName strips path components from customer-
  supplied file names (both report types)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-29 14:13:33 +02:00

40 lines
1.4 KiB
C#

using AutopilotMonitor.Functions.Services;
using Xunit;
namespace AutopilotMonitor.Functions.Tests;
/// <summary>
/// Customer-supplied attachment file names go verbatim into ZIP entry names of the
/// session-report archive. The sanitizer must strip path components so a crafted name
/// cannot produce traversal-shaped entries that an extraction tool would write outside
/// the target directory.
/// </summary>
public class SessionReportZipEntryNameTests
{
[Theory]
[InlineData("agent.log", "agent.log")]
[InlineData("agent_20260729.log", "agent_20260729.log")]
[InlineData("logs/agent.log", "agent.log")]
[InlineData("..\\..\\evil.exe", "evil.exe")]
[InlineData("../../../etc/passwd", "passwd")]
[InlineData("C:\\Windows\\System32\\bad.dll", "bad.dll")]
[InlineData(" spaced.log ", "spaced.log")]
public void SanitizeZipEntryName_StripsPathComponents(string input, string expected)
{
Assert.Equal(expected, SessionReportService.SanitizeZipEntryName(input, "fallback.log"));
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
[InlineData("..")]
[InlineData(".")]
[InlineData("logs/")]
[InlineData("a/../")]
public void SanitizeZipEntryName_FallsBackWhenNothingUsable(string? input)
{
Assert.Equal("fallback.log", SessionReportService.SanitizeZipEntryName(input, "fallback.log"));
}
}