build: clear all remaining .NET build warnings to zero

- CmTraceLogParser: annotate TryParseLine (string? line, [NotNullWhen(true)]
  out entry), init string properties (CS8618/CS8625/CS8604)
- Shared: internal NotNullWhen polyfill for netstandard2.0; multi-target
  netstandard2.0;net10.0 so net10 consumers use the BCL attribute and the
  polyfill cannot collide via InternalsVisibleTo (CS0433)
- AgentEndpointMigrationRules.TryNormalizeTarget: now carries [NotNullWhen(true)]
- Functions.csproj: drop DataProtection/Cryptography.Xml security pins made
  redundant by .NET 10 framework-package pruning (NU1510)
- Functions.Tests.csproj: add Microsoft.AspNetCore.App FrameworkReference so
  pruning applies there too and the pins stay unnecessary (NU1903/NU1904)
- Agent tests: nullable test-helper param, Assert.NotNull before dereference

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Oliver Kieselbach
2026-08-13 09:38:41 +02:00
parent ade6c4948f
commit 35cea4fe64
8 changed files with 39 additions and 17 deletions
@@ -107,6 +107,7 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Monitoring.Ime
var ok = CmTraceLogParser.TryParseLine(line, out var entry);
Assert.True(ok);
Assert.NotNull(entry);
Assert.Equal(
new DateTime(2026, 7, 29, 11, 46, 19, DateTimeKind.Utc).AddTicks(226610).AddMinutes(expectedOffsetMinutes),
entry.Timestamp);
@@ -16,7 +16,7 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Program
/// </summary>
public sealed class TenantIdWaitResolutionTests
{
private static AgentConfiguration Build(BootstrapConfigFile bootstrapConfig, params string[] args) =>
private static AgentConfiguration Build(BootstrapConfigFile? bootstrapConfig, params string[] args) =>
AutopilotMonitor.Agent.V2.Program.BuildAgentConfiguration(
args: args,
tenantId: "tenant-t",
@@ -8,6 +8,11 @@
</PropertyGroup>
<ItemGroup>
<!-- Same compile context as the product project: with the shared framework referenced,
NuGet prunes framework-provided transitive packages (e.g. the vulnerable
DataProtection/Cryptography.Xml 10.0.0 pulled in via Microsoft.Identity.Web),
which would otherwise surface NU1903/NU1904 advisories here. -->
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.9.2" />
@@ -55,12 +55,6 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Microsoft.Identity.Web" Version="4.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.10" />
<!-- Security pins: Microsoft.Identity.Web 4.3.0 pulls these transitively at 10.0.0, which carries
known advisories (GHSA-9mv3-2cwr-p262 DataProtection; GHSA-37gx-xxp4-5rgx and siblings on
Cryptography.Xml). Force the patched 10.0.10 servicing build. Remove once Identity.Web ships
a dependency floor past the fix. -->
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="10.0.10" />
<PackageReference Include="System.Security.Cryptography.Xml" Version="10.0.10" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.25" />
<PackageReference Include="Resend" Version="0.2.1" />
</ItemGroup>
@@ -1,12 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<!-- netstandard2.0 for the net48 agent; net10.0 so nullable-enabled backend consumers
compile against the real BCL nullability attributes instead of the internal
netstandard2.0 polyfill (NullableAttributes.cs), which would otherwise collide
with System.Runtime's public copy through InternalsVisibleTo (CS0433). -->
<TargetFrameworks>netstandard2.0;net10.0</TargetFrameworks>
<LangVersion>9.0</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Text.Json" Version="9.0.1" />
</ItemGroup>
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text.RegularExpressions;
@@ -10,10 +11,10 @@ namespace AutopilotMonitor.Shared.Logging
public class CmTraceLogEntry
{
public DateTime Timestamp { get; set; }
public string Message { get; set; }
public string Component { get; set; }
public string Message { get; set; } = string.Empty;
public string Component { get; set; } = string.Empty;
public int Type { get; set; } // 1=Info, 2=Warning, 3=Error
public string Thread { get; set; }
public string Thread { get; set; } = string.Empty;
}
/// <summary>
@@ -42,11 +43,12 @@ namespace AutopilotMonitor.Shared.Logging
/// Attempts to parse a single line of CMTrace-formatted log.
/// Returns true if parsing succeeded, false if the line doesn't match the format.
/// </summary>
public static bool TryParseLine(string line, out CmTraceLogEntry entry)
public static bool TryParseLine(string? line, [NotNullWhen(true)] out CmTraceLogEntry? entry)
{
entry = null;
if (string.IsNullOrEmpty(line) || !line.StartsWith("<![LOG["))
// netstandard2.0's IsNullOrEmpty lacks [NotNullWhen(false)] — assert non-null after it.
if (string.IsNullOrEmpty(line) || !line!.StartsWith("<![LOG["))
return false;
var match = CmTraceRegex.Match(line);
@@ -0,0 +1,16 @@
// netstandard2.0 ships none of the nullable-flow attributes, so shared Try-pattern APIs
// could not tell nullable-enabled consumers (backend net10, agent test projects) that an
// out param is non-null on success. Internal polyfill — the consuming compiler matches
// these attributes by full name from metadata, so accessibility does not matter.
#if NETSTANDARD2_0
namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
internal sealed class NotNullWhenAttribute : Attribute
{
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
public bool ReturnValue { get; }
}
}
#endif
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
namespace AutopilotMonitor.Shared.Services
{
@@ -28,10 +29,9 @@ namespace AutopilotMonitor.Shared.Services
/// normalized base URL (<c>https://{lowercase-host}</c>, no trailing slash) on success.
/// Rules: absolute https URI, default port, no userinfo/path/query/fragment, host
/// matching <see cref="AllowedHostSuffixes"/>. <paramref name="normalized"/> is non-null
/// exactly when this returns <c>true</c> (netstandard2.0 has no usable
/// <c>[NotNullWhen]</c>, so callers must check the bool themselves).
/// exactly when this returns <c>true</c>.
/// </summary>
public static bool TryNormalizeTarget(string candidate, out string? normalized)
public static bool TryNormalizeTarget(string candidate, [NotNullWhen(true)] out string? normalized)
{
normalized = null;