Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/test-csharp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,11 @@ jobs:
path: ./bindings/csharp/regorus-nuget/

- name: Restore Regorus.Tests
run: dotnet restore /p:RestoreAdditionalProjectSources=../regorus-nuget
run: dotnet restore /p:RestoreAdditionalProjectSources=../regorus-nuget /p:UseLocalRegorus=false
working-directory: ./bindings/csharp/Regorus.Tests

- name: Run Regorus.Tests
run: dotnet test --no-restore
run: dotnet test --no-restore -p:UseLocalRegorus=false
working-directory: ./bindings/csharp/Regorus.Tests

- name: Restore TestApp
Expand Down
84 changes: 84 additions & 0 deletions bindings/csharp/Regorus.Tests/PanicGuardTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#if REGORUS_FFI_TEST_HOOKS
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Regorus.Internal;

namespace Regorus.Tests;

[TestClass]
public sealed class PanicGuardTests
{
[TestInitialize]
public void Initialize()
{
API.regorus_engine_test_reset_poison();
}

[TestCleanup]
public void Cleanup()
{
API.regorus_engine_test_reset_poison();
}

[TestMethod]
public void Panic_produces_invalid_operation_exception()
{
var panic = Assert.ThrowsException<InvalidOperationException>(TriggerPanic);
StringAssert.Contains(panic.Message, "panicked", "panic message should capture payload");
}

[TestMethod]
public void Poison_flag_blocks_subsequent_calls()
{
_ = Assert.ThrowsException<InvalidOperationException>(TriggerPanic);
var poisoned = Assert.ThrowsException<InvalidOperationException>(TriggerPanic);
StringAssert.Contains(poisoned.Message, "poisoned", "poisoned message should explain guard state");
}

private static unsafe void TriggerPanic()
{
var result = API.regorus_engine_test_trigger_panic();
try
{
if (result.status == RegorusStatus.Ok)
{
return;
}

var message = PtrToStringUtf8((IntPtr)result.error_message);
throw result.status.CreateException(message);
}
finally
{
API.regorus_result_drop(result);
}
}

private static string? PtrToStringUtf8(IntPtr ptr)
{
#if NETSTANDARD2_1
return Marshal.PtrToStringUTF8(ptr);
#else
if (ptr == IntPtr.Zero)
{
return null;
}

var len = 0;
while (Marshal.ReadByte(ptr, len) != 0)
{
len++;
}

var buffer = new byte[len];
Marshal.Copy(ptr, buffer, 0, buffer.Length);
return System.Text.Encoding.UTF8.GetString(buffer);
#endif
}
}

#endif
16 changes: 14 additions & 2 deletions bindings/csharp/Regorus.Tests/Regorus.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@
<!-- More info about dotnet test integration https://learn.microsoft.com/dotnet/core/testing/unit-testing-platform-integration-dotnet-test -->
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
<TestingPlatformShowTestsFailure>true</TestingPlatformShowTestsFailure>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<PropertyGroup>
<!-- If the environment variable is set (such as in a Github Action run), append the suffix to the version number -->
<RegorusPackageVersionSuffix Condition="'$(VersionSuffix)' != ''">-$(VersionSuffix)</RegorusPackageVersionSuffix>
<UseLocalRegorus Condition="'$(UseLocalRegorus)' == ''">true</UseLocalRegorus>
</PropertyGroup>

<PropertyGroup Condition="'$(UseLocalRegorus)' == 'true'">
<DefineConstants>$(DefineConstants);REGORUS_FFI_TEST_HOOKS</DefineConstants>
</PropertyGroup>

<ItemGroup>
Expand All @@ -21,7 +27,13 @@
<PackageReference Include="MSTest" Version="3.8.2" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Regorus" Version="0.8.0$(RegorusPackageVersionSuffix)"/>
<ItemGroup Condition="'$(UseLocalRegorus)' == 'true'">
<ProjectReference Include="../Regorus/Regorus.csproj">
<AdditionalProperties>EnableRegorusTestHooks=true</AdditionalProperties>
</ProjectReference>
</ItemGroup>

<ItemGroup Condition="'$(UseLocalRegorus)' != 'true'">
<PackageReference Include="Regorus" Version="0.8.0$(RegorusPackageVersionSuffix)" />
</ItemGroup>
</Project>
3 changes: 3 additions & 0 deletions bindings/csharp/Regorus/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Regorus.Tests")]
3 changes: 2 additions & 1 deletion bindings/csharp/Regorus/CompiledPolicy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Text;
using System.Text.Json;
using System.Threading;
using Regorus.Internal;

#nullable enable
namespace Regorus
Expand Down Expand Up @@ -165,7 +166,7 @@ private void ThrowIfDisposed()
if (result.status != Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
throw new Exception(message ?? "Unknown error occurred");
throw result.status.CreateException(message);
}

return result.data_type switch
Expand Down
2 changes: 1 addition & 1 deletion bindings/csharp/Regorus/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ private static CompiledPolicy GetCompiledPolicyResult(Internal.RegorusResult res
if (result.status != Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
throw new Exception(message ?? "Unknown compilation error occurred");
throw result.status.CreateException(message);
}

if (result.data_type != Internal.RegorusDataType.Pointer || result.pointer_value == null)
Expand Down
22 changes: 11 additions & 11 deletions bindings/csharp/Regorus/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -373,21 +373,21 @@ public void SetGatherPrints(bool enable)

string? CheckAndDropResult(Regorus.Internal.RegorusResult result)
{
if (result.status != Regorus.Internal.RegorusStatus.Ok)
try
{
var message = StringFromUTF8((IntPtr)result.error_message);
var ex = new Exception(message);
Regorus.Internal.API.regorus_result_drop(result);
throw ex;
}
if (result.status != Regorus.Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
throw result.status.CreateException(message);
}

var resultString = "";
if (result.output is not null)
var output = result.output is not null ? StringFromUTF8((IntPtr)result.output) : null;
return output ?? string.Empty;
}
finally
{
resultString = StringFromUTF8((IntPtr)result.output);
Regorus.Internal.API.regorus_result_drop(result);
}
Regorus.Internal.API.regorus_result_drop(result);
return resultString;
}

private void ThrowIfDisposed()
Expand Down
22 changes: 22 additions & 0 deletions bindings/csharp/Regorus/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,20 @@ internal static unsafe partial class API
[DllImport(LibraryName, EntryPoint = "regorus_engine_compile_with_entrypoint", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_compile_with_entrypoint(RegorusEngine* engine, byte* rule);

#if REGORUS_FFI_TEST_HOOKS
/// <summary>
/// Trigger a panic inside the engine for testing purposes.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_test_trigger_panic", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_test_trigger_panic();

/// <summary>
/// Reset the engine poison flag for testing.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_test_reset_poison", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern void regorus_engine_test_reset_poison();
#endif

#endregion

#region Compilation Methods
Expand Down Expand Up @@ -472,6 +486,14 @@ internal enum RegorusStatus : uint
/// Invalid policy content.
/// </summary>
InvalidPolicy,
/// <summary>
/// The engine panicked and cannot be reused until reset.
/// </summary>
Panic,
/// <summary>
/// The engine remains poisoned because a previous panic was detected.
/// </summary>
Poisoned,
}

/// <summary>
Expand Down
4 changes: 4 additions & 0 deletions bindings/csharp/Regorus/Regorus.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
<PackageReference Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>

<PropertyGroup Condition="'$(EnableRegorusTestHooks)' == 'true'">
<DefineConstants>$(DefineConstants);REGORUS_FFI_TEST_HOOKS</DefineConstants>
</PropertyGroup>

<!--
$(RegorusFFIArtifactsDir) is the location where regorus shared libraries have been
built for various platforms and copied to. RegorusFFIArtifactsDir is passed in
Expand Down
6 changes: 3 additions & 3 deletions bindings/csharp/Regorus/SchemaRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ public static void ClearEffects()
if (result.status != Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
throw new Exception(message ?? "Unknown error occurred");
throw result.status.CreateException(message);
}

return result.data_type switch
Expand All @@ -265,7 +265,7 @@ private static bool GetBoolResult(Internal.RegorusResult result)
if (result.status != Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
throw new Exception(message ?? "Unknown error occurred");
throw result.status.CreateException(message);
}

return result.data_type == Internal.RegorusDataType.Boolean ? result.bool_value : false;
Expand All @@ -283,7 +283,7 @@ private static long GetIntResult(Internal.RegorusResult result)
if (result.status != Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
throw new Exception(message ?? "Unknown error occurred");
throw result.status.CreateException(message);
}

return result.data_type == Internal.RegorusDataType.Integer ? result.int_value : 0;
Expand Down
24 changes: 24 additions & 0 deletions bindings/csharp/Regorus/StatusExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;

#nullable enable

namespace Regorus.Internal
{
internal static class StatusExtensions
{
internal static Exception CreateException(this RegorusStatus status, string? message)
{
var details = string.IsNullOrWhiteSpace(message) ? "Regorus call failed." : message;

return status switch
{
RegorusStatus.Panic => new InvalidOperationException($"Regorus engine panicked: {details}"),
RegorusStatus.Poisoned => new InvalidOperationException($"Regorus engine is poisoned: {details}"),
_ => new Exception(details),
};
}
}
}
6 changes: 3 additions & 3 deletions bindings/csharp/Regorus/TargetRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public static bool IsEmpty
if (result.status != Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
throw new Exception(message ?? "Unknown error occurred");
throw result.status.CreateException(message);
}

return result.data_type switch
Expand All @@ -160,7 +160,7 @@ private static bool GetBoolResult(Internal.RegorusResult result)
if (result.status != Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
throw new Exception(message ?? "Unknown error occurred");
throw result.status.CreateException(message);
}

return result.data_type == Internal.RegorusDataType.Boolean ? result.bool_value : false;
Expand All @@ -178,7 +178,7 @@ private static long GetIntResult(Internal.RegorusResult result)
if (result.status != Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
throw new Exception(message ?? "Unknown error occurred");
throw result.status.CreateException(message);
}

return result.data_type == Internal.RegorusDataType.Integer ? result.int_value : 0;
Expand Down
6 changes: 6 additions & 0 deletions bindings/ffi/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ pub enum RegorusStatus {

/// Invalid policy content.
InvalidPolicy,

/// The engine panicked and cannot be reused until reset.
Panic,

/// The engine remains poisoned because a previous panic was detected.
Poisoned,
}

/// Type of data contained in RegorusResult
Expand Down
Loading
Loading