diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..90dac795 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,65 @@ +name: Publish NuGet Packages + +on: + release: + types: [created] + +jobs: + publish: + runs-on: windows-2022 + timeout-minutes: 30 + + env: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_CLI_TELEMETRY_OPTOUT: true + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Cache NuGet packages + uses: actions/cache@v3 + with: + path: | + ~/.nuget/packages + ~/.local/share/NuGet/v3-cache + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }} + restore-keys: | + ${{ runner.os }}-nuget- + + - name: Install .NET versions that we need for building the library + uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 6.0 + 7.0 + 8.0 + + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v2 + with: + vs-version: "[17.2,19.0)" + + - name: Install dependencies + run: | + dotnet tool restore + dotnet restore + + - name: Install Cake Tool + run: dotnet tool install --global Cake.Tool + + - name: Run Cake Script to Build and Pack + run: dotnet cake build.cake --target=CreatePackages --libVersion=${{ github.ref_name }} + + - name: Publish NuGet Packages + run: | + $ErrorActionPreference = "Stop" + Get-ChildItem -Path artifacts\*.nupkg | ForEach-Object { + try { + dotnet nuget push $_.FullName --api-key ${{ secrets.NUGETAPIKEY }} --source https://api.nuget.org/v3/index.json + Write-Host "Pushed $($_.FullName)" + } catch { + Write-Host "Failed to push $($_.FullName)" + exit 1 + } + } diff --git a/Aikido.Zen.Core/Aikido.Zen.Core.csproj b/Aikido.Zen.Core/Aikido.Zen.Core.csproj index 9cf940ba..cbacc671 100644 --- a/Aikido.Zen.Core/Aikido.Zen.Core.csproj +++ b/Aikido.Zen.Core/Aikido.Zen.Core.csproj @@ -39,6 +39,6 @@ - + diff --git a/Aikido.Zen.Core/Helpers/AgentInfoHelper.cs b/Aikido.Zen.Core/Helpers/AgentInfoHelper.cs index dd5ee1de..3e2b2bc7 100644 --- a/Aikido.Zen.Core/Helpers/AgentInfoHelper.cs +++ b/Aikido.Zen.Core/Helpers/AgentInfoHelper.cs @@ -41,7 +41,6 @@ public static AgentInfo GetInfo() _cachedAgentInfo.DryMode = EnvironmentHelper.DryMode; _cachedAgentInfo.Serverless = Environment.GetEnvironmentVariable("AWS_LAMBDA_FUNCTION_NAME") != null || Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID") != null; - return _cachedAgentInfo; } } diff --git a/Aikido.Zen.Core/Helpers/ReflectionHelper.cs b/Aikido.Zen.Core/Helpers/ReflectionHelper.cs index 763f1a80..9de36e6d 100644 --- a/Aikido.Zen.Core/Helpers/ReflectionHelper.cs +++ b/Aikido.Zen.Core/Helpers/ReflectionHelper.cs @@ -32,11 +32,9 @@ static ReflectionHelper() public static MethodInfo GetMethodFromAssembly(string assemblyName, string typeName, string methodName, params string[] parameterTypeNames) { // Attempt to load the assembly - // Attempt to get the assembly from the cache, if not found, load it if (!_assemblies.TryGetValue(assemblyName, out var assembly)) { assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == assemblyName); - // If the assembly is not loaded, and the assembly path exists, load it if (File.Exists($"{assemblyName}.dll") && assembly == null) { assembly = Assembly.LoadFrom($"{assemblyName}.dll"); @@ -50,12 +48,16 @@ public static MethodInfo GetMethodFromAssembly(string assemblyName, string typeN if (!_types.TryGetValue(typeKey, out var type)) { type = assembly.ExportedTypes.FirstOrDefault(t => t.Name == typeName || t.FullName == typeName); + if (type == null) return null; _types[typeKey] = type; } - // Use reflection to get the method - var method = type.GetMethods().FirstOrDefault(m => m.Name == methodName && m.GetParameters().All(p => parameterTypeNames.Any(ptn => ptn == p.ParameterType.FullName))); + // Use reflection to get the method, make sure to check for public, internal and private methods + var method = type + .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static) + .FirstOrDefault(m => m.Name == methodName && + m.GetParameters().Select(p => p.ParameterType.FullName).SequenceEqual(parameterTypeNames)); return method; } diff --git a/Aikido.Zen.Core/Patches/SqlClientPatcher.cs b/Aikido.Zen.Core/Patches/SqlClientPatcher.cs index 690238f4..fd6299a3 100644 --- a/Aikido.Zen.Core/Patches/SqlClientPatcher.cs +++ b/Aikido.Zen.Core/Patches/SqlClientPatcher.cs @@ -10,7 +10,8 @@ public static class SqlClientPatcher { public static bool OnCommandExecuting(object[] __args, MethodBase __originalMethod, DbCommand __instance, string assembly, Context context) { - var command = __instance; + var command = __instance + ?? __args[0] as DbCommand; var methodInfo = __originalMethod as MethodInfo; if (context == null) @@ -37,6 +38,7 @@ public static SQLDialect GetDialect(string assembly) { case "System.Data.SqlClient": case "Microsoft.Data.SqlClient": + case "System.Data.SqlServerCe": return SQLDialect.MicrosoftSQL; case "MySql.Data": case "MySqlConnector": diff --git a/Aikido.Zen.DotNetCore/Aikido.Zen.DotNetCore.nuspec b/Aikido.Zen.DotNetCore/Aikido.Zen.DotNetCore.nuspec new file mode 100644 index 00000000..9d3556f7 --- /dev/null +++ b/Aikido.Zen.DotNetCore/Aikido.Zen.DotNetCore.nuspec @@ -0,0 +1,64 @@ + + + + Aikido.Zen.DotNetCore + * + $title$ + Aikido Security + Aikido Security + Aikido Security Zen .NET Core Firewall + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Aikido.Zen.DotNetCore/Aikido.Zen.DotNetCore.targets b/Aikido.Zen.DotNetCore/Aikido.Zen.DotNetCore.targets new file mode 100644 index 00000000..f84a993f --- /dev/null +++ b/Aikido.Zen.DotNetCore/Aikido.Zen.DotNetCore.targets @@ -0,0 +1,16 @@ + + + + + PreserveNewest + + + + + + + + + + + \ No newline at end of file diff --git a/Aikido.Zen.DotNetCore/Middleware/ContextMiddleware.cs b/Aikido.Zen.DotNetCore/Middleware/ContextMiddleware.cs index 1e5fb9a4..92a3c0a5 100644 --- a/Aikido.Zen.DotNetCore/Middleware/ContextMiddleware.cs +++ b/Aikido.Zen.DotNetCore/Middleware/ContextMiddleware.cs @@ -2,6 +2,7 @@ using Aikido.Zen.Core.Helpers; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; +using System.Collections.Concurrent; namespace Aikido.Zen.DotNetCore.Middleware { @@ -18,9 +19,9 @@ public ContextMiddleware(IEnumerable endpointSources) public async Task InvokeAsync(HttpContext httpContext, RequestDelegate next) { - // Convert headers and query parameters to dictionaries once - var queryDictionary = httpContext.Request.Query.ToDictionary(q => q.Key, q => q.Value.ToArray()); - var headersDictionary = httpContext.Request.Headers.ToDictionary(h => h.Key, h => h.Value.ToArray()); + // Convert headers and query parameters to thread-safe dictionaries + var queryDictionary = new ConcurrentDictionary(httpContext.Request.Query.ToDictionary(q => q.Key, q => q.Value.ToArray())); + var headersDictionary = new ConcurrentDictionary(httpContext.Request.Headers.ToDictionary(h => h.Key, h => h.Value.ToArray())); var context = new Context { diff --git a/Aikido.Zen.DotNetCore/Patches/SqlClientPatches.cs b/Aikido.Zen.DotNetCore/Patches/SqlClientPatches.cs index 81e86345..a4e2f0db 100644 --- a/Aikido.Zen.DotNetCore/Patches/SqlClientPatches.cs +++ b/Aikido.Zen.DotNetCore/Patches/SqlClientPatches.cs @@ -46,6 +46,11 @@ public static void ApplyPatches(Harmony harmony) PatchMethod(harmony, "MySqlX", "XDevAPI.Relational.Table", "Insert"); PatchMethod(harmony, "MySqlX", "XDevAPI.Relational.Table", "Update"); PatchMethod(harmony, "MySqlX", "XDevAPI.Relational.Table", "Delete"); + + // NPoco + PatchMethod(harmony, "NPoco", "Database", "ExecuteReaderHelper", "System.Data.Common.DbCommand"); + PatchMethod(harmony, "NPoco", "Database", "ExecuteNonQueryHelper", "System.Data.Common.DbCommand"); + PatchMethod(harmony, "NPoco", "Database", "ExecuteScalarHelper", "System.Data.Common.DbCommand"); } private static void PatchMethod(Harmony harmony, string assemblyName, string typeName, string methodName, params string[] parameterTypeNames) @@ -59,7 +64,8 @@ private static void PatchMethod(Harmony harmony, string assemblyName, string typ private static bool OnCommandExecuting(object[] __args, MethodBase __originalMethod, object __instance) { - var dbCommand = __instance as System.Data.Common.DbCommand; + var dbCommand = __instance as System.Data.Common.DbCommand + ?? __args[0] as System.Data.Common.DbCommand; if (dbCommand == null) return true; var assembly = __instance.GetType().Assembly.FullName?.Split(", Culture=")[0]; return Aikido.Zen.Core.Patches.SqlClientPatcher.OnCommandExecuting(__args, __originalMethod, dbCommand, assembly, Zen.GetContext()); diff --git a/Aikido.Zen.DotNetFramework/Aikido.Zen.DotNetFramework.nuspec b/Aikido.Zen.DotNetFramework/Aikido.Zen.DotNetFramework.nuspec index 1e6a9d8b..87a208ab 100644 --- a/Aikido.Zen.DotNetFramework/Aikido.Zen.DotNetFramework.nuspec +++ b/Aikido.Zen.DotNetFramework/Aikido.Zen.DotNetFramework.nuspec @@ -1,17 +1,29 @@ - $id$ - $version$ + Aikido.Zen.DotNetFramework + * $title$ - $author$ + Aikido Security + Aikido Security + Aikido Security Zen .NET Framework Firewall false MIT - - http://project_url_here_or_delete_this_line/ - Aikido Zen, An in-app firewall for .NET - Summary of changes made in this release of the package. Copyright 2024 Aikido firewall .NET + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Aikido.Zen.DotNetFramework/Aikido.Zen.DotNetFramework.targets b/Aikido.Zen.DotNetFramework/Aikido.Zen.DotNetFramework.targets new file mode 100644 index 00000000..b44589a7 --- /dev/null +++ b/Aikido.Zen.DotNetFramework/Aikido.Zen.DotNetFramework.targets @@ -0,0 +1,21 @@ + + + + + libraries\libzen_internals_x86_64-pc-windows-gnu.dll + PreserveNewest + + + libraries\libzen_internals_x86_64-pc-windows-gnu.dll.sha256sum + PreserveNewest + + + + + + + + + + + \ No newline at end of file diff --git a/Aikido.Zen.DotNetFramework/Patches/SqlClientPatches.cs b/Aikido.Zen.DotNetFramework/Patches/SqlClientPatches.cs index d9865ab5..fbcb992e 100644 --- a/Aikido.Zen.DotNetFramework/Patches/SqlClientPatches.cs +++ b/Aikido.Zen.DotNetFramework/Patches/SqlClientPatches.cs @@ -3,6 +3,7 @@ using HarmonyLib; using Aikido.Zen.Core.Models; using Aikido.Zen.Core.Helpers; +using Aikido.Zen.Core; namespace Aikido.Zen.DotNetFramework.Patches { @@ -27,6 +28,14 @@ public static void ApplyPatches(Harmony harmony) PatchMethod(harmony, "System.Data.SqlClient", "SqlCommand", "ExecuteScalar"); PatchMethod(harmony, "System.Data.SqlClient", "SqlCommand", "ExecuteReader", "System.Data.CommandBehavior"); + //SQL ServerCE + PatchMethod(harmony, "System.Data.SqlServerCe", "SqlCeCommand", "ExecuteNonQuery"); + PatchMethod(harmony, "System.Data.SqlServerCe", "SqlCeCommand", "ExecuteScalar"); + PatchMethod(harmony, "System.Data.SqlServerCe", "SqlCeCommand", "ExecuteReader", "System.Data.CommandBehavior"); + PatchMethod(harmony, "System.Data.SqlServerCe", "SqlCeCommand", "ExecuteNonQuery"); + PatchMethod(harmony, "System.Data.SqlServerCe", "SqlCeCommand", "ExecuteScalar"); + PatchMethod(harmony, "System.Data.SqlServerCe", "SqlCeCommand", "ExecuteReader", "System.Data.CommandBehavior"); + // SQLite PatchMethod(harmony, "Microsoft.Data.Sqlite", "SqliteCommand", "ExecuteNonQuery"); PatchMethod(harmony, "Microsoft.Data.Sqlite", "SqliteCommand", "ExecuteScalar"); @@ -50,6 +59,11 @@ public static void ApplyPatches(Harmony harmony) PatchMethod(harmony, "MySqlX", "XDevAPI.Relational.Table", "Insert"); PatchMethod(harmony, "MySqlX", "XDevAPI.Relational.Table", "Update"); PatchMethod(harmony, "MySqlX", "XDevAPI.Relational.Table", "Delete"); + + // NPoco + PatchMethod(harmony, "NPoco", "Database", "ExecuteReaderHelper", "System.Data.Common.DbCommand"); + PatchMethod(harmony, "NPoco", "Database", "ExecuteNonQueryHelper", "System.Data.Common.DbCommand"); + PatchMethod(harmony, "NPoco", "Database", "ExecuteScalarHelper", "System.Data.Common.DbCommand"); } /// @@ -78,7 +92,8 @@ private static void PatchMethod(Harmony harmony, string assemblyName, string typ /// True if the original method should continue execution; otherwise, false. private static bool OnCommandExecuting(object[] __args, MethodBase __originalMethod, object __instance) { - var dbCommand = __instance as System.Data.Common.DbCommand; + var dbCommand = __instance as System.Data.Common.DbCommand + ?? __args[0] as System.Data.Common.DbCommand; if (dbCommand == null) return true; var assembly = __instance.GetType().Assembly.FullName?.Split(new[] { ", Culture=" }, StringSplitOptions.RemoveEmptyEntries)[0]; return Aikido.Zen.Core.Patches.SqlClientPatcher.OnCommandExecuting(__args, __originalMethod, dbCommand, assembly, Zen.GetContext()); diff --git a/build.cake b/build.cake index 30676e98..2c787326 100644 --- a/build.cake +++ b/build.cake @@ -1,4 +1,6 @@ #addin nuget:?package=Cake.FileHelpers&version=6.0.0 +#load "nuget:https://www.nuget.org/api/v2?package=Cake.NuGet&version=5.0.0" + var target = Argument("target", "Default"); @@ -6,9 +8,10 @@ var configuration = Argument("configuration", "Release"); var framework = Argument("framework", ""); var solution = "./Aikido.Zen.sln"; var projectName = "Aikido.Zen.Core"; -var version = "0.1.35"; +var zenInternalsVersion = "0.1.35"; +var libVersion = Argument("libVersion", "0.0.1-alpha5"); -var baseUrl = $"https://github.com/AikidoSec/zen-internals/releases/download/v{version}/"; +var baseUrl = $"https://github.com/AikidoSec/zen-internals/releases/download/v{zenInternalsVersion}/"; var librariesDir = $"./{projectName}/libraries"; var filesToDownload = new string[] { @@ -43,7 +46,7 @@ Task("DownloadLibraries") { FilePath file = files.First(); var currVersion = FileReadText(file).Split('-')[1]; - if (currVersion == version) + if (currVersion == zenInternalsVersion) { Information("Libraries already downloaded. skipping download."); return; @@ -81,7 +84,8 @@ Task("Build") DetailedSummary = false, NodeReuse = true } - .WithTarget("Build"); + .WithTarget("Build") + .WithProperty("version", libVersion); var projects = GetFiles("./**/*.csproj") .Where(p => !p.FullPath.Contains("sample-apps") && !p.FullPath.Contains("Aikido.Zen.Benchmarks")); @@ -168,19 +172,19 @@ Task("Pack") if (configuration == "Release") { var projects = new[] { - "./Aikido.Zen.Core/Aikido.Zen.Core.csproj", "./Aikido.Zen.DotNetFramework/Aikido.Zen.DotNetFramework.csproj", "./Aikido.Zen.DotNetCore/Aikido.Zen.DotNetCore.csproj" }; foreach (var project in projects) { - DotNetPack(project, new DotNetPackSettings + var specFile = project.Replace(".csproj", ".nuspec"); + var nugetPackSettings = new NuGetPackSettings { - Configuration = configuration, - NoBuild = true, OutputDirectory = "./artifacts", - }); + Version = libVersion, + }; + NuGetPack(specFile, nugetPackSettings); } Information("Pack task completed successfully."); } @@ -191,8 +195,7 @@ Task("Pack") }); Task("CreatePackages") - .IsDependentOn("Test") - .IsDependentOn("TestE2E") + .IsDependentOn("Build") .IsDependentOn("Pack"); Task("Default") diff --git a/dotnet-tools.json b/dotnet-tools.json index 7b346950..99a3eb1c 100644 --- a/dotnet-tools.json +++ b/dotnet-tools.json @@ -2,11 +2,9 @@ "version": 1, "isRoot": true, "tools": { - "cake.tool": { - "version": "4.0.0", - "commands": [ - "dotnet-cake" - ] - } + "cake.tool": { + "version": "5.0.0", + "commands": ["dotnet-cake"] + } } - } \ No newline at end of file +}