diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt
index dbe169530..224f1502e 100644
--- a/.github/actions/spelling/allow.txt
+++ b/.github/actions/spelling/allow.txt
@@ -13,6 +13,7 @@ Overconstrained
parallelizable
qrcode
retriable
+Runtimes
sanitizers
scrollbars
shortcutting
diff --git a/.github/actions/spelling/excludes.txt b/.github/actions/spelling/excludes.txt
index 0b74a71f6..dba700b74 100644
--- a/.github/actions/spelling/excludes.txt
+++ b/.github/actions/spelling/excludes.txt
@@ -1,6 +1,9 @@
Assets/Vendors/
\QLombiq.VueJs.Tests.UI/Assets/Media/\E.*\.mjpeg$
^\Qtest/Lombiq.UITestingToolbox/Lombiq.Tests.UI/Constants/CommonDisplayResolutions.cs\E$
+^\Qtest/Lombiq.UITestingToolbox/Lombiq.Tests.UI/SecurityScanning/AutomationFrameworkPlanFragments/\E.*.yml$
+^\Qtest/Lombiq.UITestingToolbox/Lombiq.Tests.UI/SecurityScanning/AutomationFrameworkPlans/\E.*.yml$
+^\Qtest/Lombiq.UITestingToolbox/Lombiq.Tests.UI.Samples/Tests/CustomZapAutomationFrameworkPlan.yml\E$
^\Qtools/Lombiq.GitHub.Actions/\E
\QUnmanagedNodeModules/\E
\QUploadingTestFileDOCX.docx\E$
diff --git a/.github/workflows/build-and-test-windows.yml b/.github/workflows/build-and-test-windows.yml
index f37cd3e4a..f698b6f5f 100644
--- a/.github/workflows/build-and-test-windows.yml
+++ b/.github/workflows/build-and-test-windows.yml
@@ -42,6 +42,9 @@ jobs:
ui-test-parallelism: 0
build-create-binary-log: "true"
blame-hang-timeout: "5m"
+ # Running ZAP for security scans in Docker under GHA Windows runners won't work since such virtualization is not
+ # supported by GHA.
+ test-filter: "FullyQualifiedName!~SecurityScanningTests"
build-and-test-standard-runners:
# Since dev builds are not awaited by anyone, they can run on the slower free runners.
@@ -55,6 +58,9 @@ jobs:
set-up-azurite: "true"
build-create-binary-log: "true"
blame-hang-timeout: "5m"
+ # Running ZAP for security scans in Docker under GHA Windows runners won't work since such virtualization is not
+ # supported by GHA.
+ test-filter: "FullyQualifiedName!~SecurityScanningTests"
build-and-test-nuget-test:
if: github.ref_name == github.event.repository.default_branch ||
@@ -67,6 +73,9 @@ jobs:
build-directory: NuGetTest
timeout-minutes: 25
blame-hang-timeout: "5m"
+ # Running ZAP for security scans in Docker under GHA Windows runners won't work since such virtualization is not
+ # supported by GHA.
+ test-filter: "FullyQualifiedName!~SecurityScanningTests"
powershell-static-code-analysis:
if: github.ref_name == github.event.repository.default_branch ||
diff --git a/NuGetTest/src/Lombiq.OSOCE.NuGet.Web/Lombiq.OSOCE.NuGet.Web.csproj b/NuGetTest/src/Lombiq.OSOCE.NuGet.Web/Lombiq.OSOCE.NuGet.Web.csproj
index cbbe2a718..7f103d42f 100644
--- a/NuGetTest/src/Lombiq.OSOCE.NuGet.Web/Lombiq.OSOCE.NuGet.Web.csproj
+++ b/NuGetTest/src/Lombiq.OSOCE.NuGet.Web/Lombiq.OSOCE.NuGet.Web.csproj
@@ -47,8 +47,8 @@
-
-
+
+
diff --git a/NuGetTest/src/Modules/Lombiq.OSOCE.NuGet.TestModule/Lombiq.OSOCE.NuGet.TestModule.csproj b/NuGetTest/src/Modules/Lombiq.OSOCE.NuGet.TestModule/Lombiq.OSOCE.NuGet.TestModule.csproj
index 97869c0bf..7cc730af0 100644
--- a/NuGetTest/src/Modules/Lombiq.OSOCE.NuGet.TestModule/Lombiq.OSOCE.NuGet.TestModule.csproj
+++ b/NuGetTest/src/Modules/Lombiq.OSOCE.NuGet.TestModule/Lombiq.OSOCE.NuGet.TestModule.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/NuGetTest/test/Lombiq.OSOCE.NuGet.Tests.UI/Lombiq.OSOCE.NuGet.Tests.UI.csproj b/NuGetTest/test/Lombiq.OSOCE.NuGet.Tests.UI/Lombiq.OSOCE.NuGet.Tests.UI.csproj
index c5d1ce1bd..6bea20471 100644
--- a/NuGetTest/test/Lombiq.OSOCE.NuGet.Tests.UI/Lombiq.OSOCE.NuGet.Tests.UI.csproj
+++ b/NuGetTest/test/Lombiq.OSOCE.NuGet.Tests.UI/Lombiq.OSOCE.NuGet.Tests.UI.csproj
@@ -34,7 +34,7 @@
-
+
diff --git a/NuGetTest/test/Lombiq.OSOCE.NuGet.Tests.UI/Tests/SecurityScanningTests.cs b/NuGetTest/test/Lombiq.OSOCE.NuGet.Tests.UI/Tests/SecurityScanningTests.cs
new file mode 100644
index 000000000..1bb45480c
--- /dev/null
+++ b/NuGetTest/test/Lombiq.OSOCE.NuGet.Tests.UI/Tests/SecurityScanningTests.cs
@@ -0,0 +1,27 @@
+using Lombiq.Tests.UI.SecurityScanning;
+using Shouldly;
+using System.Threading.Tasks;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Lombiq.OSOCE.NuGet.Tests.UI.Tests;
+
+public class SecurityScanningTests : UITestBase
+{
+ public SecurityScanningTests(ITestOutputHelper testOutputHelper)
+ : base(testOutputHelper)
+ {
+ }
+
+ // Only scanning the homepage, since this is just to make sure that ZAP still works from NuGet.
+ [Fact]
+ public Task BasicSecurityScanShouldPass() =>
+ ExecuteTestAfterSetupAsync(
+ context => context.RunAndAssertBaselineSecurityScanAsync(
+ configuration => configuration.ExcludeUrlWithRegex(".*:[0-9]+\\/.+"),
+ // We expect 5 alerts from ZAP. This is using "less than" not to fail the test, should ZAP be a bit
+ // inconsistent, which it can be (see https://www.zaproxy.org/faq/why-can-zap-scans-be-inconsistent/).
+ // If this starts failing after some update, then inspect the scan report in the failure dump to see if
+ // the alerts can be simply expected and this number should be increased.
+ sarifLog => sarifLog.Runs[0].Results.Count.ShouldBeLessThan(6)));
+}
diff --git a/NuGetTest/test/Lombiq.OSOCE.NuGet.Tests/Lombiq.OSOCE.NuGet.Tests.csproj b/NuGetTest/test/Lombiq.OSOCE.NuGet.Tests/Lombiq.OSOCE.NuGet.Tests.csproj
index ba38657b8..c08ab31f9 100644
--- a/NuGetTest/test/Lombiq.OSOCE.NuGet.Tests/Lombiq.OSOCE.NuGet.Tests.csproj
+++ b/NuGetTest/test/Lombiq.OSOCE.NuGet.Tests/Lombiq.OSOCE.NuGet.Tests.csproj
@@ -5,7 +5,7 @@
-
+
diff --git a/src/Libraries/Lombiq.HelpfulLibraries b/src/Libraries/Lombiq.HelpfulLibraries
index 56beebf74..9346c1c5d 160000
--- a/src/Libraries/Lombiq.HelpfulLibraries
+++ b/src/Libraries/Lombiq.HelpfulLibraries
@@ -1 +1 @@
-Subproject commit 56beebf7470e049b400aefb383a9f92debe43d1a
+Subproject commit 9346c1c5daa632afb6f1127ad5547d578680a246
diff --git a/src/Modules/Lombiq.TrainingDemo b/src/Modules/Lombiq.TrainingDemo
index a961b39eb..b5611dfeb 160000
--- a/src/Modules/Lombiq.TrainingDemo
+++ b/src/Modules/Lombiq.TrainingDemo
@@ -1 +1 @@
-Subproject commit a961b39ebab34730eb56c3c7108d2c83b4b02a3e
+Subproject commit b5611dfeb1245a5b260dc2f879507319c027e62e
diff --git a/test/Lombiq.UITestingToolbox b/test/Lombiq.UITestingToolbox
index 88ec8caf6..659d8a3e4 160000
--- a/test/Lombiq.UITestingToolbox
+++ b/test/Lombiq.UITestingToolbox
@@ -1 +1 @@
-Subproject commit 88ec8caf6940e7f1445fe4993701fc0ce5ce5e48
+Subproject commit 659d8a3e4bafc41fb09ee8242c8f14dbec1074ed