mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 04:32:32 +00:00
Agent: read uninstall-registry strings up to the first NUL (D-338)
Some installers pad REG_SZ values with NULs. .NET Framework drops only one
terminator, so the rest rode into the normalized software identity and,
server-side, into a table key whose point read the HTTP layer rejects
("400 Invalid URL"). DisplayName, DisplayVersion, Publisher, InstallDate,
InstallLocation, UninstallString and ParentKeyName are now read the way
Windows shows them; a DisplayName of NULs only counts as empty.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+31
@@ -0,0 +1,31 @@
|
||||
#nullable enable
|
||||
using AutopilotMonitor.Agent.V2.Core.Monitoring.Telemetry.Analyzers;
|
||||
using Xunit;
|
||||
|
||||
namespace AutopilotMonitor.Agent.V2.Core.Tests.Monitoring.Analyzers
|
||||
{
|
||||
/// <summary>
|
||||
/// Uninstall-key strings are read the way Windows shows them: up to the first NUL. Installers
|
||||
/// that pad REG_SZ data with NULs otherwise leak them into the normalized vendor/name, which
|
||||
/// the backend turns into a table key.
|
||||
/// </summary>
|
||||
public sealed class SoftwareInventoryRegistryTextTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("Contoso Widget", "Contoso Widget")]
|
||||
[InlineData("Contoso Widget\0\0\0\0\0", "Contoso Widget")]
|
||||
[InlineData("Contoso\0stale buffer", "Contoso")]
|
||||
[InlineData("\0\0\0", "")]
|
||||
[InlineData("", "")]
|
||||
public void CutAtNul_KeepsTheTextBeforeTheFirstNul(string raw, string expected)
|
||||
{
|
||||
Assert.Equal(expected, SoftwareInventoryAnalyzer.CutAtNul(raw));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CutAtNul_Null_StaysNull()
|
||||
{
|
||||
Assert.Null(SoftwareInventoryAnalyzer.CutAtNul(null!));
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
-7
@@ -254,9 +254,9 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Telemetry.Analyzers
|
||||
if (subKey == null)
|
||||
continue;
|
||||
|
||||
var displayName = subKey.GetValue("DisplayName")?.ToString();
|
||||
var displayName = ReadRegistryText(subKey, "DisplayName");
|
||||
var systemComponent = subKey.GetValue("SystemComponent");
|
||||
var parentKeyName = subKey.GetValue("ParentKeyName")?.ToString();
|
||||
var parentKeyName = ReadRegistryText(subKey, "ParentKeyName");
|
||||
|
||||
if (ShouldExclude(displayName, systemComponent, parentKeyName))
|
||||
continue;
|
||||
@@ -264,11 +264,11 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Telemetry.Analyzers
|
||||
results.Add(new SoftwareEntry
|
||||
{
|
||||
DisplayName = displayName,
|
||||
DisplayVersion = subKey.GetValue("DisplayVersion")?.ToString(),
|
||||
Publisher = subKey.GetValue("Publisher")?.ToString(),
|
||||
InstallDate = subKey.GetValue("InstallDate")?.ToString(),
|
||||
InstallLocation = subKey.GetValue("InstallLocation")?.ToString(),
|
||||
UninstallString = subKey.GetValue("UninstallString")?.ToString(),
|
||||
DisplayVersion = ReadRegistryText(subKey, "DisplayVersion"),
|
||||
Publisher = ReadRegistryText(subKey, "Publisher"),
|
||||
InstallDate = ReadRegistryText(subKey, "InstallDate"),
|
||||
InstallLocation = ReadRegistryText(subKey, "InstallLocation"),
|
||||
UninstallString = ReadRegistryText(subKey, "UninstallString"),
|
||||
IsWindowsInstaller = Convert.ToInt32(subKey.GetValue("WindowsInstaller") ?? 0) == 1,
|
||||
RegistrySource = source
|
||||
});
|
||||
@@ -287,6 +287,22 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Telemetry.Analyzers
|
||||
}
|
||||
}
|
||||
|
||||
private static string ReadRegistryText(RegistryKey key, string name)
|
||||
=> CutAtNul(key.GetValue(name)?.ToString());
|
||||
|
||||
/// <summary>
|
||||
/// A registry string the way Windows shows it: up to the first NUL. Some installers write
|
||||
/// REG_SZ data padded with NULs, and .NET Framework drops only one terminator — the rest
|
||||
/// would ride into the normalized identity and, server-side, into a table key.
|
||||
/// </summary>
|
||||
internal static string CutAtNul(string value)
|
||||
{
|
||||
if (value == null)
|
||||
return null;
|
||||
var nul = value.IndexOf('\0');
|
||||
return nul < 0 ? value : value.Substring(0, nul);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// HKU per-user profile enumeration
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user