Resolve all 46 low-severity architecture review findings
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user