Resolve all 46 low-severity architecture review findings

This commit is contained in:
Joseph Doherty
2026-09-09 22:56:36 -04:00
parent fef3e7675f
commit 9ee402bbcd
139 changed files with 2621 additions and 1082 deletions
@@ -1,4 +1,5 @@
using System.Net;
using System.Text.RegularExpressions;
using Microsoft.Extensions.DependencyInjection;
using ZB.MOM.WW.ScadaBridge.CentralUI.Auth;
using ZB.MOM.WW.ScadaBridge.Security;
@@ -26,11 +27,7 @@ public class AuthFlowTests : IClassFixture<ScadaBridgeWebApplicationFactory>
AllowAutoRedirect = false
});
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("username", ""),
new KeyValuePair<string, string>("password", "")
});
using var content = await CreateLoginFormAsync(client, "", "");
var response = await client.PostAsync("/auth/login", content);
@@ -121,11 +118,7 @@ public class AuthFlowTests : IClassFixture<ScadaBridgeWebApplicationFactory>
AllowAutoRedirect = false
});
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("username", "admin"),
new KeyValuePair<string, string>("password", "password")
});
using var content = await CreateLoginFormAsync(client, "admin", "password");
var response = await client.PostAsync("/auth/login", content);
@@ -139,6 +132,42 @@ public class AuthFlowTests : IClassFixture<ScadaBridgeWebApplicationFactory>
Assert.Contains("ZB.MOM.WW.ScadaBridge.Auth", setCookieHeader);
}
[Fact]
public async Task LoginEndpoint_WithoutAntiforgeryToken_RejectsWithoutAuthCookie()
{
using var client = _factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
using var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["username"] = "admin", ["password"] = "password"
});
using var response = await client.PostAsync("/auth/login", content);
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
if (response.Headers.TryGetValues("Set-Cookie", out var cookies))
Assert.DoesNotContain(cookies, cookie => cookie.StartsWith(SecurityOptions.DefaultCookieName + "=", StringComparison.Ordinal));
}
private static async Task<FormUrlEncodedContent> CreateLoginFormAsync(HttpClient client, string username, string password)
{
// Fetch the actual rendered form. WebApplicationFactory's cookie handler retains
// its antiforgery cookie, so the POST exercises the same pair as a browser login.
using var page = await client.GetAsync("/login");
Assert.Equal(HttpStatusCode.OK, page.StatusCode);
var html = await page.Content.ReadAsStringAsync();
var input = Regex.Match(html, """<input\b[^>]*name="__RequestVerificationToken"[^>]*>""", RegexOptions.IgnoreCase);
Assert.True(input.Success, "The login form must render an antiforgery input.");
var value = Regex.Match(input.Value, "\\bvalue=\"([^\"]+)\"", RegexOptions.IgnoreCase);
Assert.True(value.Success, "The login form must provide a nonempty antiforgery token.");
return new FormUrlEncodedContent(new Dictionary<string, string>
{
["username"] = username,
["password"] = password,
["__RequestVerificationToken"] = WebUtility.HtmlDecode(value.Groups[1].Value)
});
}
private static async Task<bool> IsLdapAvailableAsync()
{
try