mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
feat(ffi): unwind safety: shield FFI entrypoints with panic guard (#546)
This PR implements widely accepted Rust programming practices for dealing with panics across ABI (programming language) boundaries. - Add panic_guard.rs to wrap FFI calls and prevent panic across FFI/ABI boundary (undefined behavior). - Capture per-thread backtraces via a temporary panic hook - After a panic, subsequent invocations are poisoned. - Integrate with_unwind_guard across the engine, schema registry, and target registry exportis Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
9426b2ec02
commit
80686d6ed1
@@ -144,11 +144,11 @@ jobs:
|
|||||||
path: ./bindings/csharp/regorus-nuget/
|
path: ./bindings/csharp/regorus-nuget/
|
||||||
|
|
||||||
- name: Restore Regorus.Tests
|
- 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
|
working-directory: ./bindings/csharp/Regorus.Tests
|
||||||
|
|
||||||
- name: Run 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
|
working-directory: ./bindings/csharp/Regorus.Tests
|
||||||
|
|
||||||
- name: Restore TestApp
|
- name: Restore TestApp
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -6,11 +6,17 @@
|
|||||||
<!-- More info about dotnet test integration https://learn.microsoft.com/dotnet/core/testing/unit-testing-platform-integration-dotnet-test -->
|
<!-- More info about dotnet test integration https://learn.microsoft.com/dotnet/core/testing/unit-testing-platform-integration-dotnet-test -->
|
||||||
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
|
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
|
||||||
<TestingPlatformShowTestsFailure>true</TestingPlatformShowTestsFailure>
|
<TestingPlatformShowTestsFailure>true</TestingPlatformShowTestsFailure>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- If the environment variable is set (such as in a Github Action run), append the suffix to the version number -->
|
<!-- 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>
|
<RegorusPackageVersionSuffix Condition="'$(VersionSuffix)' != ''">-$(VersionSuffix)</RegorusPackageVersionSuffix>
|
||||||
|
<UseLocalRegorus Condition="'$(UseLocalRegorus)' == ''">true</UseLocalRegorus>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(UseLocalRegorus)' == 'true'">
|
||||||
|
<DefineConstants>$(DefineConstants);REGORUS_FFI_TEST_HOOKS</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -21,7 +27,13 @@
|
|||||||
<PackageReference Include="MSTest" Version="3.8.2" />
|
<PackageReference Include="MSTest" Version="3.8.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup Condition="'$(UseLocalRegorus)' == 'true'">
|
||||||
<PackageReference Include="Regorus" Version="0.8.0$(RegorusPackageVersionSuffix)"/>
|
<ProjectReference Include="../Regorus/Regorus.csproj">
|
||||||
|
<AdditionalProperties>EnableRegorusTestHooks=true</AdditionalProperties>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup Condition="'$(UseLocalRegorus)' != 'true'">
|
||||||
|
<PackageReference Include="Regorus" Version="0.8.0$(RegorusPackageVersionSuffix)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("Regorus.Tests")]
|
||||||
@@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using Regorus.Internal;
|
||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
namespace Regorus
|
namespace Regorus
|
||||||
@@ -165,7 +166,7 @@ namespace Regorus
|
|||||||
if (result.status != Internal.RegorusStatus.Ok)
|
if (result.status != Internal.RegorusStatus.Ok)
|
||||||
{
|
{
|
||||||
var message = StringFromUTF8((IntPtr)result.error_message);
|
var message = StringFromUTF8((IntPtr)result.error_message);
|
||||||
throw new Exception(message ?? "Unknown error occurred");
|
throw result.status.CreateException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.data_type switch
|
return result.data_type switch
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ namespace Regorus
|
|||||||
if (result.status != Internal.RegorusStatus.Ok)
|
if (result.status != Internal.RegorusStatus.Ok)
|
||||||
{
|
{
|
||||||
var message = StringFromUTF8((IntPtr)result.error_message);
|
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)
|
if (result.data_type != Internal.RegorusDataType.Pointer || result.pointer_value == null)
|
||||||
|
|||||||
@@ -373,21 +373,21 @@ namespace Regorus
|
|||||||
|
|
||||||
string? CheckAndDropResult(Regorus.Internal.RegorusResult result)
|
string? CheckAndDropResult(Regorus.Internal.RegorusResult result)
|
||||||
{
|
{
|
||||||
if (result.status != Regorus.Internal.RegorusStatus.Ok)
|
try
|
||||||
{
|
{
|
||||||
var message = StringFromUTF8((IntPtr)result.error_message);
|
if (result.status != Regorus.Internal.RegorusStatus.Ok)
|
||||||
var ex = new Exception(message);
|
{
|
||||||
Regorus.Internal.API.regorus_result_drop(result);
|
var message = StringFromUTF8((IntPtr)result.error_message);
|
||||||
throw ex;
|
throw result.status.CreateException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
var resultString = "";
|
var output = result.output is not null ? StringFromUTF8((IntPtr)result.output) : null;
|
||||||
if (result.output is not null)
|
return output ?? string.Empty;
|
||||||
{
|
}
|
||||||
resultString = StringFromUTF8((IntPtr)result.output);
|
finally
|
||||||
|
{
|
||||||
|
Regorus.Internal.API.regorus_result_drop(result);
|
||||||
}
|
}
|
||||||
Regorus.Internal.API.regorus_result_drop(result);
|
|
||||||
return resultString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThrowIfDisposed()
|
private void ThrowIfDisposed()
|
||||||
|
|||||||
@@ -217,6 +217,20 @@ namespace Regorus.Internal
|
|||||||
[DllImport(LibraryName, EntryPoint = "regorus_engine_compile_with_entrypoint", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
[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);
|
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
|
#endregion
|
||||||
|
|
||||||
#region Compilation Methods
|
#region Compilation Methods
|
||||||
@@ -472,6 +486,14 @@ namespace Regorus.Internal
|
|||||||
/// Invalid policy content.
|
/// Invalid policy content.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
InvalidPolicy,
|
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>
|
/// <summary>
|
||||||
|
|||||||
@@ -17,6 +17,10 @@
|
|||||||
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(EnableRegorusTestHooks)' == 'true'">
|
||||||
|
<DefineConstants>$(DefineConstants);REGORUS_FFI_TEST_HOOKS</DefineConstants>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
$(RegorusFFIArtifactsDir) is the location where regorus shared libraries have been
|
$(RegorusFFIArtifactsDir) is the location where regorus shared libraries have been
|
||||||
built for various platforms and copied to. RegorusFFIArtifactsDir is passed in
|
built for various platforms and copied to. RegorusFFIArtifactsDir is passed in
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ namespace Regorus
|
|||||||
if (result.status != Internal.RegorusStatus.Ok)
|
if (result.status != Internal.RegorusStatus.Ok)
|
||||||
{
|
{
|
||||||
var message = StringFromUTF8((IntPtr)result.error_message);
|
var message = StringFromUTF8((IntPtr)result.error_message);
|
||||||
throw new Exception(message ?? "Unknown error occurred");
|
throw result.status.CreateException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.data_type switch
|
return result.data_type switch
|
||||||
@@ -265,7 +265,7 @@ namespace Regorus
|
|||||||
if (result.status != Internal.RegorusStatus.Ok)
|
if (result.status != Internal.RegorusStatus.Ok)
|
||||||
{
|
{
|
||||||
var message = StringFromUTF8((IntPtr)result.error_message);
|
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;
|
return result.data_type == Internal.RegorusDataType.Boolean ? result.bool_value : false;
|
||||||
@@ -283,7 +283,7 @@ namespace Regorus
|
|||||||
if (result.status != Internal.RegorusStatus.Ok)
|
if (result.status != Internal.RegorusStatus.Ok)
|
||||||
{
|
{
|
||||||
var message = StringFromUTF8((IntPtr)result.error_message);
|
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;
|
return result.data_type == Internal.RegorusDataType.Integer ? result.int_value : 0;
|
||||||
|
|||||||
@@ -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),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -135,7 +135,7 @@ namespace Regorus
|
|||||||
if (result.status != Internal.RegorusStatus.Ok)
|
if (result.status != Internal.RegorusStatus.Ok)
|
||||||
{
|
{
|
||||||
var message = StringFromUTF8((IntPtr)result.error_message);
|
var message = StringFromUTF8((IntPtr)result.error_message);
|
||||||
throw new Exception(message ?? "Unknown error occurred");
|
throw result.status.CreateException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.data_type switch
|
return result.data_type switch
|
||||||
@@ -160,7 +160,7 @@ namespace Regorus
|
|||||||
if (result.status != Internal.RegorusStatus.Ok)
|
if (result.status != Internal.RegorusStatus.Ok)
|
||||||
{
|
{
|
||||||
var message = StringFromUTF8((IntPtr)result.error_message);
|
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;
|
return result.data_type == Internal.RegorusDataType.Boolean ? result.bool_value : false;
|
||||||
@@ -178,7 +178,7 @@ namespace Regorus
|
|||||||
if (result.status != Internal.RegorusStatus.Ok)
|
if (result.status != Internal.RegorusStatus.Ok)
|
||||||
{
|
{
|
||||||
var message = StringFromUTF8((IntPtr)result.error_message);
|
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;
|
return result.data_type == Internal.RegorusDataType.Integer ? result.int_value : 0;
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ pub enum RegorusStatus {
|
|||||||
|
|
||||||
/// Invalid policy content.
|
/// Invalid policy content.
|
||||||
InvalidPolicy,
|
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
|
/// Type of data contained in RegorusResult
|
||||||
|
|||||||
+78
-79
@@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT License.
|
// Licensed under the MIT License.
|
||||||
use crate::common::{from_c_str, RegorusResult, RegorusStatus};
|
use crate::common::{from_c_str, RegorusResult, RegorusStatus};
|
||||||
use crate::compiled_policy::RegorusCompiledPolicy;
|
use crate::compiled_policy::RegorusCompiledPolicy;
|
||||||
|
use crate::panic_guard::with_unwind_guard;
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::format;
|
use alloc::format;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
@@ -43,55 +44,54 @@ pub extern "C" fn regorus_compile_policy_with_entrypoint(
|
|||||||
modules_len: usize,
|
modules_len: usize,
|
||||||
entry_point_rule: *const c_char,
|
entry_point_rule: *const c_char,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
let data_str = match from_c_str(data_json) {
|
with_unwind_guard(|| {
|
||||||
Ok(s) => s,
|
let data_str = match from_c_str(data_json) {
|
||||||
Err(e) => {
|
Ok(s) => s,
|
||||||
return RegorusResult::err_with_message(
|
Err(e) => {
|
||||||
RegorusStatus::InvalidDataFormat,
|
return RegorusResult::err_with_message(
|
||||||
format!("Invalid data JSON string: {e}"),
|
RegorusStatus::InvalidDataFormat,
|
||||||
)
|
format!("Invalid data JSON string: {e}"),
|
||||||
}
|
)
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let entry_rule = match from_c_str(entry_point_rule) {
|
let entry_rule = match from_c_str(entry_point_rule) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return RegorusResult::err_with_message(
|
return RegorusResult::err_with_message(
|
||||||
RegorusStatus::InvalidEntrypoint,
|
RegorusStatus::InvalidEntrypoint,
|
||||||
format!("Invalid entry point rule string: {e}"),
|
format!("Invalid entry point rule string: {e}"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse data JSON
|
let data = match Value::from_json_str(&data_str) {
|
||||||
let data = match Value::from_json_str(&data_str) {
|
Ok(data) => data,
|
||||||
Ok(data) => data,
|
Err(e) => {
|
||||||
Err(e) => {
|
return RegorusResult::err_with_message(
|
||||||
return RegorusResult::err_with_message(
|
RegorusStatus::InvalidDataFormat,
|
||||||
RegorusStatus::InvalidDataFormat,
|
format!("Failed to parse data JSON: {e}"),
|
||||||
format!("Failed to parse data JSON: {e}"),
|
)
|
||||||
)
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
// Convert C modules array to Rust Vec
|
let policy_modules = match convert_c_modules_to_rust(modules, modules_len) {
|
||||||
let policy_modules = match convert_c_modules_to_rust(modules, modules_len) {
|
Ok(modules) => modules,
|
||||||
Ok(modules) => modules,
|
Err(status) => return RegorusResult::err(status),
|
||||||
Err(status) => return RegorusResult::err(status),
|
};
|
||||||
};
|
|
||||||
|
|
||||||
// Call the convenience function
|
match compile_policy_with_entrypoint(data, &policy_modules, entry_rule.into()) {
|
||||||
match compile_policy_with_entrypoint(data, &policy_modules, entry_rule.into()) {
|
Ok(compiled_policy) => {
|
||||||
Ok(compiled_policy) => {
|
let wrapped_policy = RegorusCompiledPolicy { compiled_policy };
|
||||||
let wrapped_policy = RegorusCompiledPolicy { compiled_policy };
|
let boxed_policy = Box::new(wrapped_policy);
|
||||||
let boxed_policy = Box::new(wrapped_policy);
|
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut c_void)
|
||||||
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut c_void)
|
}
|
||||||
|
Err(e) => RegorusResult::err_with_message(
|
||||||
|
RegorusStatus::CompilationFailed,
|
||||||
|
format!("Policy compilation failed: {e}"),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
Err(e) => RegorusResult::err_with_message(
|
})
|
||||||
RegorusStatus::CompilationFailed,
|
|
||||||
format!("Policy compilation failed: {e}"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compiles a target-aware policy from data and modules.
|
/// Compiles a target-aware policy from data and modules.
|
||||||
@@ -122,45 +122,44 @@ pub extern "C" fn regorus_compile_policy_for_target(
|
|||||||
modules: *const RegorusPolicyModule,
|
modules: *const RegorusPolicyModule,
|
||||||
modules_len: usize,
|
modules_len: usize,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
let data_str = match from_c_str(data_json) {
|
with_unwind_guard(|| {
|
||||||
Ok(s) => s,
|
let data_str = match from_c_str(data_json) {
|
||||||
Err(e) => {
|
Ok(s) => s,
|
||||||
return RegorusResult::err_with_message(
|
Err(e) => {
|
||||||
RegorusStatus::InvalidDataFormat,
|
return RegorusResult::err_with_message(
|
||||||
format!("Invalid data JSON string: {e}"),
|
RegorusStatus::InvalidDataFormat,
|
||||||
)
|
format!("Invalid data JSON string: {e}"),
|
||||||
}
|
)
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Parse data JSON
|
let data = match Value::from_json_str(&data_str) {
|
||||||
let data = match Value::from_json_str(&data_str) {
|
Ok(data) => data,
|
||||||
Ok(data) => data,
|
Err(e) => {
|
||||||
Err(e) => {
|
return RegorusResult::err_with_message(
|
||||||
return RegorusResult::err_with_message(
|
RegorusStatus::InvalidDataFormat,
|
||||||
RegorusStatus::InvalidDataFormat,
|
format!("Failed to parse data JSON: {e}"),
|
||||||
format!("Failed to parse data JSON: {e}"),
|
)
|
||||||
)
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
// Convert C modules array to Rust Vec
|
let policy_modules = match convert_c_modules_to_rust(modules, modules_len) {
|
||||||
let policy_modules = match convert_c_modules_to_rust(modules, modules_len) {
|
Ok(modules) => modules,
|
||||||
Ok(modules) => modules,
|
Err(status) => return RegorusResult::err(status),
|
||||||
Err(status) => return RegorusResult::err(status),
|
};
|
||||||
};
|
|
||||||
|
|
||||||
// Call the convenience function
|
match compile_policy_for_target(data, &policy_modules) {
|
||||||
match compile_policy_for_target(data, &policy_modules) {
|
Ok(compiled_policy) => {
|
||||||
Ok(compiled_policy) => {
|
let wrapped_policy = RegorusCompiledPolicy { compiled_policy };
|
||||||
let wrapped_policy = RegorusCompiledPolicy { compiled_policy };
|
let boxed_policy = Box::new(wrapped_policy);
|
||||||
let boxed_policy = Box::new(wrapped_policy);
|
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut c_void)
|
||||||
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut c_void)
|
}
|
||||||
|
Err(e) => RegorusResult::err_with_message(
|
||||||
|
RegorusStatus::CompilationFailed,
|
||||||
|
format!("Target-aware policy compilation failed: {e}"),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
Err(e) => RegorusResult::err_with_message(
|
})
|
||||||
RegorusStatus::CompilationFailed,
|
|
||||||
format!("Target-aware policy compilation failed: {e}"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to convert C module array to Rust Vec<PolicyModule>.
|
/// Helper function to convert C module array to Rust Vec<PolicyModule>.
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT License.
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
use crate::panic_guard::with_unwind_guard;
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
@@ -35,18 +36,20 @@ pub extern "C" fn regorus_compiled_policy_eval_with_input(
|
|||||||
compiled_policy: *mut RegorusCompiledPolicy,
|
compiled_policy: *mut RegorusCompiledPolicy,
|
||||||
input: *const c_char,
|
input: *const c_char,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
let output = || -> Result<String> {
|
with_unwind_guard(|| {
|
||||||
let input_value = regorus::Value::from_json_str(&from_c_str(input)?)?;
|
let output = || -> Result<String> {
|
||||||
let result = to_ref(compiled_policy)?
|
let input_value = regorus::Value::from_json_str(&from_c_str(input)?)?;
|
||||||
.compiled_policy
|
let result = to_ref(compiled_policy)?
|
||||||
.eval_with_input(input_value)?;
|
.compiled_policy
|
||||||
result.to_json_str()
|
.eval_with_input(input_value)?;
|
||||||
}();
|
result.to_json_str()
|
||||||
|
}();
|
||||||
|
|
||||||
match output {
|
match output {
|
||||||
Ok(out) => RegorusResult::ok_string(out),
|
Ok(out) => RegorusResult::ok_string(out),
|
||||||
Err(e) => to_regorus_result(Err(e)),
|
Err(e) => to_regorus_result(Err(e)),
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get information about the compiled policy including metadata about modules,
|
/// Get information about the compiled policy including metadata about modules,
|
||||||
@@ -59,14 +62,16 @@ pub extern "C" fn regorus_compiled_policy_eval_with_input(
|
|||||||
pub extern "C" fn regorus_compiled_policy_get_policy_info(
|
pub extern "C" fn regorus_compiled_policy_get_policy_info(
|
||||||
compiled_policy: *mut RegorusCompiledPolicy,
|
compiled_policy: *mut RegorusCompiledPolicy,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
let output = || -> Result<String> {
|
with_unwind_guard(|| {
|
||||||
let info = to_ref(compiled_policy)?.compiled_policy.get_policy_info()?;
|
let output = || -> Result<String> {
|
||||||
serde_json::to_string(&info)
|
let info = to_ref(compiled_policy)?.compiled_policy.get_policy_info()?;
|
||||||
.map_err(|e| anyhow::anyhow!("Failed to serialize policy info: {}", e))
|
serde_json::to_string(&info)
|
||||||
}();
|
.map_err(|e| anyhow::anyhow!("Failed to serialize policy info: {}", e))
|
||||||
|
}();
|
||||||
|
|
||||||
match output {
|
match output {
|
||||||
Ok(out) => RegorusResult::ok_string(out),
|
Ok(out) => RegorusResult::ok_string(out),
|
||||||
Err(e) => to_regorus_result(Err(e)),
|
Err(e) => to_regorus_result(Err(e)),
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#![cfg(feature = "azure_policy")]
|
#![cfg(feature = "azure_policy")]
|
||||||
use crate::common::{from_c_str, RegorusResult, RegorusStatus};
|
use crate::common::{from_c_str, RegorusResult, RegorusStatus};
|
||||||
|
use crate::panic_guard::with_unwind_guard;
|
||||||
use regorus::{registry::schemas, Schema};
|
use regorus::{registry::schemas, Schema};
|
||||||
|
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
@@ -29,45 +30,45 @@ pub extern "C" fn regorus_effect_schema_register(
|
|||||||
name: *const c_char,
|
name: *const c_char,
|
||||||
schema_json: *const c_char,
|
schema_json: *const c_char,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
let schema_name = match from_c_str(name) {
|
with_unwind_guard(|| {
|
||||||
Ok(s) => s,
|
let schema_name = match from_c_str(name) {
|
||||||
Err(e) => {
|
Ok(s) => s,
|
||||||
return RegorusResult::err_with_message(
|
Err(e) => {
|
||||||
RegorusStatus::InvalidArgument,
|
return RegorusResult::err_with_message(
|
||||||
format!("Invalid effect schema name string: {e}"),
|
RegorusStatus::InvalidArgument,
|
||||||
)
|
format!("Invalid effect schema name string: {e}"),
|
||||||
}
|
)
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let schema_str = match from_c_str(schema_json) {
|
let schema_str = match from_c_str(schema_json) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return RegorusResult::err_with_message(
|
return RegorusResult::err_with_message(
|
||||||
RegorusStatus::InvalidDataFormat,
|
RegorusStatus::InvalidDataFormat,
|
||||||
format!("Invalid effect schema JSON string: {e}"),
|
format!("Invalid effect schema JSON string: {e}"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse schema from JSON
|
let schema = match Schema::from_json_str(&schema_str) {
|
||||||
let schema = match Schema::from_json_str(&schema_str) {
|
Ok(schema) => schema,
|
||||||
Ok(schema) => schema,
|
Err(e) => {
|
||||||
Err(e) => {
|
return RegorusResult::err_with_message(
|
||||||
return RegorusResult::err_with_message(
|
RegorusStatus::InvalidDataFormat,
|
||||||
RegorusStatus::InvalidDataFormat,
|
format!("Failed to parse effect schema JSON: {e}"),
|
||||||
format!("Failed to parse effect schema JSON: {e}"),
|
)
|
||||||
)
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
// Register the schema
|
match schemas::effect::register(schema_name, schema.into()) {
|
||||||
match schemas::effect::register(schema_name, schema.into()) {
|
Ok(()) => RegorusResult::ok_pointer(std::ptr::null_mut()),
|
||||||
Ok(()) => RegorusResult::ok_pointer(std::ptr::null_mut()),
|
Err(e) => RegorusResult::err_with_message(
|
||||||
Err(e) => RegorusResult::err_with_message(
|
RegorusStatus::Error,
|
||||||
RegorusStatus::Error,
|
format!("Failed to register effect schema: {e}"),
|
||||||
format!("Failed to register effect schema: {e}"),
|
),
|
||||||
),
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if an effect schema with the given name exists.
|
/// Check if an effect schema with the given name exists.
|
||||||
@@ -83,18 +84,20 @@ pub extern "C" fn regorus_effect_schema_register(
|
|||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn regorus_effect_schema_contains(name: *const c_char) -> RegorusResult {
|
pub extern "C" fn regorus_effect_schema_contains(name: *const c_char) -> RegorusResult {
|
||||||
let schema_name = match from_c_str(name) {
|
with_unwind_guard(|| {
|
||||||
Ok(s) => s,
|
let schema_name = match from_c_str(name) {
|
||||||
Err(e) => {
|
Ok(s) => s,
|
||||||
return RegorusResult::err_with_message(
|
Err(e) => {
|
||||||
RegorusStatus::InvalidArgument,
|
return RegorusResult::err_with_message(
|
||||||
format!("Invalid effect schema name string: {e}"),
|
RegorusStatus::InvalidArgument,
|
||||||
)
|
format!("Invalid effect schema name string: {e}"),
|
||||||
}
|
)
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let contains = schemas::effect::contains(&schema_name);
|
let contains = schemas::effect::contains(&schema_name);
|
||||||
RegorusResult::ok_bool(contains)
|
RegorusResult::ok_bool(contains)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the number of registered effect schemas.
|
/// Get the number of registered effect schemas.
|
||||||
@@ -104,8 +107,10 @@ pub extern "C" fn regorus_effect_schema_contains(name: *const c_char) -> Regorus
|
|||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn regorus_effect_schema_len() -> RegorusResult {
|
pub extern "C" fn regorus_effect_schema_len() -> RegorusResult {
|
||||||
let count = schemas::effect::len();
|
with_unwind_guard(|| {
|
||||||
RegorusResult::ok_int(count as i64)
|
let count = schemas::effect::len();
|
||||||
|
RegorusResult::ok_int(count as i64)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the effect schema registry is empty.
|
/// Check if the effect schema registry is empty.
|
||||||
@@ -115,8 +120,10 @@ pub extern "C" fn regorus_effect_schema_len() -> RegorusResult {
|
|||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn regorus_effect_schema_is_empty() -> RegorusResult {
|
pub extern "C" fn regorus_effect_schema_is_empty() -> RegorusResult {
|
||||||
let is_empty = schemas::effect::is_empty();
|
with_unwind_guard(|| {
|
||||||
RegorusResult::ok_bool(is_empty)
|
let is_empty = schemas::effect::is_empty();
|
||||||
|
RegorusResult::ok_bool(is_empty)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List all registered effect schema names as a JSON array.
|
/// List all registered effect schema names as a JSON array.
|
||||||
@@ -126,14 +133,16 @@ pub extern "C" fn regorus_effect_schema_is_empty() -> RegorusResult {
|
|||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn regorus_effect_schema_list_names() -> RegorusResult {
|
pub extern "C" fn regorus_effect_schema_list_names() -> RegorusResult {
|
||||||
let names = schemas::effect::list_names();
|
with_unwind_guard(|| {
|
||||||
match serde_json::to_string(&names) {
|
let names = schemas::effect::list_names();
|
||||||
Ok(json_str) => RegorusResult::ok_string(json_str),
|
match serde_json::to_string(&names) {
|
||||||
Err(e) => RegorusResult::err_with_message(
|
Ok(json_str) => RegorusResult::ok_string(json_str),
|
||||||
RegorusStatus::Error,
|
Err(e) => RegorusResult::err_with_message(
|
||||||
format!("Failed to serialize effect schema names to JSON: {e}"),
|
RegorusStatus::Error,
|
||||||
),
|
format!("Failed to serialize effect schema names to JSON: {e}"),
|
||||||
}
|
),
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove an effect schema by name.
|
/// Remove an effect schema by name.
|
||||||
@@ -149,18 +158,20 @@ pub extern "C" fn regorus_effect_schema_list_names() -> RegorusResult {
|
|||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn regorus_effect_schema_remove(name: *const c_char) -> RegorusResult {
|
pub extern "C" fn regorus_effect_schema_remove(name: *const c_char) -> RegorusResult {
|
||||||
let schema_name = match from_c_str(name) {
|
with_unwind_guard(|| {
|
||||||
Ok(s) => s,
|
let schema_name = match from_c_str(name) {
|
||||||
Err(e) => {
|
Ok(s) => s,
|
||||||
return RegorusResult::err_with_message(
|
Err(e) => {
|
||||||
RegorusStatus::InvalidArgument,
|
return RegorusResult::err_with_message(
|
||||||
format!("Invalid effect schema name string: {e}"),
|
RegorusStatus::InvalidArgument,
|
||||||
)
|
format!("Invalid effect schema name string: {e}"),
|
||||||
}
|
)
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let removed = schemas::effect::remove(&schema_name).is_some();
|
let removed = schemas::effect::remove(&schema_name).is_some();
|
||||||
RegorusResult::ok_bool(removed)
|
RegorusResult::ok_bool(removed)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear all effect schemas from the registry.
|
/// Clear all effect schemas from the registry.
|
||||||
@@ -170,6 +181,8 @@ pub extern "C" fn regorus_effect_schema_remove(name: *const c_char) -> RegorusRe
|
|||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn regorus_effect_schema_clear() -> RegorusResult {
|
pub extern "C" fn regorus_effect_schema_clear() -> RegorusResult {
|
||||||
schemas::effect::clear();
|
with_unwind_guard(|| {
|
||||||
RegorusResult::ok_pointer(std::ptr::null_mut())
|
schemas::effect::clear();
|
||||||
|
RegorusResult::ok_pointer(std::ptr::null_mut())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+346
-200
@@ -6,6 +6,7 @@ use crate::common::{
|
|||||||
};
|
};
|
||||||
use crate::compiled_policy::RegorusCompiledPolicy;
|
use crate::compiled_policy::RegorusCompiledPolicy;
|
||||||
use crate::lock::{new_handle, read, try_read, try_write, Handle, ReadGuard, WriteGuard};
|
use crate::lock::{new_handle, read, try_read, try_write, Handle, ReadGuard, WriteGuard};
|
||||||
|
use crate::panic_guard::with_unwind_guard;
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::format;
|
use alloc::format;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
@@ -67,6 +68,101 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(test, feature = "std"))]
|
||||||
|
mod panic_tests {
|
||||||
|
use super::{
|
||||||
|
regorus_engine_drop, regorus_engine_eval_query, regorus_engine_get_policies,
|
||||||
|
regorus_engine_new,
|
||||||
|
};
|
||||||
|
use crate::common::{regorus_result_drop, RegorusStatus};
|
||||||
|
use crate::panic_guard::{is_poisoned, reset_poison};
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use regorus::Value;
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn catches_extension_panics_and_marks_poison() {
|
||||||
|
reset_poison();
|
||||||
|
|
||||||
|
let engine_ptr = regorus_engine_new();
|
||||||
|
assert!(!engine_ptr.is_null(), "engine allocation must succeed");
|
||||||
|
assert!(!is_poisoned(), "guard must start unpoisoned");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let engine = &mut *engine_ptr;
|
||||||
|
{
|
||||||
|
let mut guard = engine
|
||||||
|
.try_write()
|
||||||
|
.expect("exclusive access to configure engine");
|
||||||
|
guard
|
||||||
|
.add_extension(
|
||||||
|
"panic_extension".to_string(),
|
||||||
|
0,
|
||||||
|
Box::new(|_| -> anyhow::Result<Value> { panic!("ffi extension panic") }),
|
||||||
|
)
|
||||||
|
.expect("extension registration must succeed");
|
||||||
|
guard
|
||||||
|
.add_policy(
|
||||||
|
"panic.rego".to_string(),
|
||||||
|
"package panic\n\ndefault allow = false\n\nallow if {\n panic_extension()\n}"
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
.expect("policy registration must succeed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let query = CString::new("data.panic.allow").expect("valid query string");
|
||||||
|
let panic_result = regorus_engine_eval_query(engine_ptr, query.as_ptr());
|
||||||
|
assert!(matches!(panic_result.status, RegorusStatus::Panic));
|
||||||
|
unsafe {
|
||||||
|
assert!(
|
||||||
|
!panic_result.error_message.is_null(),
|
||||||
|
"panic details must be present"
|
||||||
|
);
|
||||||
|
let message = CStr::from_ptr(panic_result.error_message)
|
||||||
|
.to_str()
|
||||||
|
.expect("error message utf8");
|
||||||
|
assert!(
|
||||||
|
message.contains("ffi extension panic"),
|
||||||
|
"panic payload must bubble across guard"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
regorus_result_drop(panic_result);
|
||||||
|
assert!(is_poisoned(), "engine must be marked poisoned after panic");
|
||||||
|
|
||||||
|
let poisoned_result = regorus_engine_get_policies(engine_ptr);
|
||||||
|
assert!(matches!(poisoned_result.status, RegorusStatus::Poisoned));
|
||||||
|
unsafe {
|
||||||
|
assert!(
|
||||||
|
!poisoned_result.error_message.is_null(),
|
||||||
|
"poison message must be present"
|
||||||
|
);
|
||||||
|
let message = CStr::from_ptr(poisoned_result.error_message)
|
||||||
|
.to_str()
|
||||||
|
.expect("poison message utf8");
|
||||||
|
assert!(
|
||||||
|
message.contains("regorus is poisoned"),
|
||||||
|
"poison message must inform callers"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
regorus_result_drop(poisoned_result);
|
||||||
|
|
||||||
|
regorus_engine_drop(engine_ptr);
|
||||||
|
reset_poison();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub extern "C" fn regorus_engine_test_trigger_panic() -> RegorusResult {
|
||||||
|
with_unwind_guard(|| panic!("regorus ffi test panic"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn regorus_engine_test_reset_poison() {
|
||||||
|
crate::panic_guard::reset_poison();
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
/// Construct a new Engine
|
/// Construct a new Engine
|
||||||
///
|
///
|
||||||
@@ -116,11 +212,13 @@ pub extern "C" fn regorus_engine_add_policy(
|
|||||||
path: *const c_char,
|
path: *const c_char,
|
||||||
rego: *const c_char,
|
rego: *const c_char,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
to_regorus_string_result(|| -> Result<String> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
to_regorus_string_result(|| -> Result<String> {
|
||||||
let mut guard = engine.try_write()?;
|
let engine = to_ref(engine)?;
|
||||||
guard.add_policy(from_c_str(path)?, from_c_str(rego)?)
|
let mut guard = engine.try_write()?;
|
||||||
}())
|
guard.add_policy(from_c_str(path)?, from_c_str(rego)?)
|
||||||
|
}())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@@ -129,11 +227,13 @@ pub extern "C" fn regorus_engine_add_policy_from_file(
|
|||||||
engine: *mut RegorusEngine,
|
engine: *mut RegorusEngine,
|
||||||
path: *const c_char,
|
path: *const c_char,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
to_regorus_string_result(|| -> Result<String> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
to_regorus_string_result(|| -> Result<String> {
|
||||||
let mut guard = engine.try_write()?;
|
let engine = to_ref(engine)?;
|
||||||
guard.add_policy_from_file(from_c_str(path)?)
|
let mut guard = engine.try_write()?;
|
||||||
}())
|
guard.add_policy_from_file(from_c_str(path)?)
|
||||||
|
}())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add policy data.
|
/// Add policy data.
|
||||||
@@ -145,11 +245,13 @@ pub extern "C" fn regorus_engine_add_data_json(
|
|||||||
engine: *mut RegorusEngine,
|
engine: *mut RegorusEngine,
|
||||||
data: *const c_char,
|
data: *const c_char,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
to_regorus_result(|| -> Result<()> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
to_regorus_result(|| -> Result<()> {
|
||||||
let mut guard = engine.try_write()?;
|
let engine = to_ref(engine)?;
|
||||||
guard.add_data(regorus::Value::from_json_str(&from_c_str(data)?)?)
|
let mut guard = engine.try_write()?;
|
||||||
}())
|
guard.add_data(regorus::Value::from_json_str(&from_c_str(data)?)?)
|
||||||
|
}())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get list of loaded Rego packages as JSON.
|
/// Get list of loaded Rego packages as JSON.
|
||||||
@@ -157,11 +259,13 @@ pub extern "C" fn regorus_engine_add_data_json(
|
|||||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_packages
|
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_packages
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn regorus_engine_get_packages(engine: *mut RegorusEngine) -> RegorusResult {
|
pub extern "C" fn regorus_engine_get_packages(engine: *mut RegorusEngine) -> RegorusResult {
|
||||||
to_regorus_string_result(|| -> Result<String> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
to_regorus_string_result(|| -> Result<String> {
|
||||||
let guard = engine.try_read()?;
|
let engine = to_ref(engine)?;
|
||||||
serde_json::to_string_pretty(&guard.get_packages()?).map_err(anyhow::Error::msg)
|
let guard = engine.try_read()?;
|
||||||
}())
|
serde_json::to_string_pretty(&guard.get_packages()?).map_err(anyhow::Error::msg)
|
||||||
|
}())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get list of policies as JSON.
|
/// Get list of policies as JSON.
|
||||||
@@ -169,11 +273,13 @@ pub extern "C" fn regorus_engine_get_packages(engine: *mut RegorusEngine) -> Reg
|
|||||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_policies
|
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_policies
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn regorus_engine_get_policies(engine: *mut RegorusEngine) -> RegorusResult {
|
pub extern "C" fn regorus_engine_get_policies(engine: *mut RegorusEngine) -> RegorusResult {
|
||||||
to_regorus_string_result(|| -> Result<String> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
to_regorus_string_result(|| -> Result<String> {
|
||||||
let guard = engine.try_read()?;
|
let engine = to_ref(engine)?;
|
||||||
guard.get_policies_as_json()
|
let guard = engine.try_read()?;
|
||||||
}())
|
guard.get_policies_as_json()
|
||||||
|
}())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@@ -182,11 +288,13 @@ pub extern "C" fn regorus_engine_add_data_from_json_file(
|
|||||||
engine: *mut RegorusEngine,
|
engine: *mut RegorusEngine,
|
||||||
path: *const c_char,
|
path: *const c_char,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
to_regorus_result(|| -> Result<()> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
to_regorus_result(|| -> Result<()> {
|
||||||
let mut guard = engine.try_write()?;
|
let engine = to_ref(engine)?;
|
||||||
guard.add_data(regorus::Value::from_json_file(from_c_str(path)?)?)
|
let mut guard = engine.try_write()?;
|
||||||
}())
|
guard.add_data(regorus::Value::from_json_file(from_c_str(path)?)?)
|
||||||
|
}())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear policy data.
|
/// Clear policy data.
|
||||||
@@ -194,12 +302,14 @@ pub extern "C" fn regorus_engine_add_data_from_json_file(
|
|||||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.clear_data
|
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.clear_data
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn regorus_engine_clear_data(engine: *mut RegorusEngine) -> RegorusResult {
|
pub extern "C" fn regorus_engine_clear_data(engine: *mut RegorusEngine) -> RegorusResult {
|
||||||
to_regorus_result(|| -> Result<()> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
to_regorus_result(|| -> Result<()> {
|
||||||
let mut guard = engine.try_write()?;
|
let engine = to_ref(engine)?;
|
||||||
guard.clear_data();
|
let mut guard = engine.try_write()?;
|
||||||
Ok(())
|
guard.clear_data();
|
||||||
}())
|
Ok(())
|
||||||
|
}())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set input.
|
/// Set input.
|
||||||
@@ -211,12 +321,14 @@ pub extern "C" fn regorus_engine_set_input_json(
|
|||||||
engine: *mut RegorusEngine,
|
engine: *mut RegorusEngine,
|
||||||
input: *const c_char,
|
input: *const c_char,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
to_regorus_result(|| -> Result<()> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
to_regorus_result(|| -> Result<()> {
|
||||||
let mut guard = engine.try_write()?;
|
let engine = to_ref(engine)?;
|
||||||
guard.set_input(regorus::Value::from_json_str(&from_c_str(input)?)?);
|
let mut guard = engine.try_write()?;
|
||||||
Ok(())
|
guard.set_input(regorus::Value::from_json_str(&from_c_str(input)?)?);
|
||||||
}())
|
Ok(())
|
||||||
|
}())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@@ -225,12 +337,14 @@ pub extern "C" fn regorus_engine_set_input_from_json_file(
|
|||||||
engine: *mut RegorusEngine,
|
engine: *mut RegorusEngine,
|
||||||
path: *const c_char,
|
path: *const c_char,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
to_regorus_result(|| -> Result<()> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
to_regorus_result(|| -> Result<()> {
|
||||||
let mut guard = engine.try_write()?;
|
let engine = to_ref(engine)?;
|
||||||
guard.set_input(regorus::Value::from_json_file(from_c_str(path)?)?);
|
let mut guard = engine.try_write()?;
|
||||||
Ok(())
|
guard.set_input(regorus::Value::from_json_file(from_c_str(path)?)?);
|
||||||
}())
|
Ok(())
|
||||||
|
}())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate query.
|
/// Evaluate query.
|
||||||
@@ -242,16 +356,18 @@ pub extern "C" fn regorus_engine_eval_query(
|
|||||||
engine: *mut RegorusEngine,
|
engine: *mut RegorusEngine,
|
||||||
query: *const c_char,
|
query: *const c_char,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
let output = || -> Result<String> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
let output = || -> Result<String> {
|
||||||
let mut guard = engine.try_write()?;
|
let engine = to_ref(engine)?;
|
||||||
let results = guard.eval_query(from_c_str(query)?, false)?;
|
let mut guard = engine.try_write()?;
|
||||||
Ok(serde_json::to_string_pretty(&results)?)
|
let results = guard.eval_query(from_c_str(query)?, false)?;
|
||||||
}();
|
Ok(serde_json::to_string_pretty(&results)?)
|
||||||
match output {
|
}();
|
||||||
Ok(out) => RegorusResult::ok_string(out),
|
match output {
|
||||||
Err(e) => to_regorus_result(Err(e)),
|
Ok(out) => RegorusResult::ok_string(out),
|
||||||
}
|
Err(e) => to_regorus_result(Err(e)),
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate specified rule.
|
/// Evaluate specified rule.
|
||||||
@@ -263,15 +379,17 @@ pub extern "C" fn regorus_engine_eval_rule(
|
|||||||
engine: *mut RegorusEngine,
|
engine: *mut RegorusEngine,
|
||||||
rule: *const c_char,
|
rule: *const c_char,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
let output = || -> Result<String> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
let output = || -> Result<String> {
|
||||||
let mut guard = engine.try_write()?;
|
let engine = to_ref(engine)?;
|
||||||
guard.eval_rule(from_c_str(rule)?)?.to_json_str()
|
let mut guard = engine.try_write()?;
|
||||||
}();
|
guard.eval_rule(from_c_str(rule)?)?.to_json_str()
|
||||||
match output {
|
}();
|
||||||
Ok(out) => RegorusResult::ok_string(out),
|
match output {
|
||||||
Err(e) => to_regorus_result(Err(e)),
|
Ok(out) => RegorusResult::ok_string(out),
|
||||||
}
|
Err(e) => to_regorus_result(Err(e)),
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enable/disable coverage.
|
/// Enable/disable coverage.
|
||||||
@@ -284,12 +402,14 @@ pub extern "C" fn regorus_engine_set_enable_coverage(
|
|||||||
engine: *mut RegorusEngine,
|
engine: *mut RegorusEngine,
|
||||||
enable: bool,
|
enable: bool,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
to_regorus_result(|| -> Result<()> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
to_regorus_result(|| -> Result<()> {
|
||||||
let mut guard = engine.try_write()?;
|
let engine = to_ref(engine)?;
|
||||||
guard.set_enable_coverage(enable);
|
let mut guard = engine.try_write()?;
|
||||||
Ok(())
|
guard.set_enable_coverage(enable);
|
||||||
}())
|
Ok(())
|
||||||
|
}())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get coverage report.
|
/// Get coverage report.
|
||||||
@@ -298,15 +418,17 @@ pub extern "C" fn regorus_engine_set_enable_coverage(
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[cfg(feature = "coverage")]
|
#[cfg(feature = "coverage")]
|
||||||
pub extern "C" fn regorus_engine_get_coverage_report(engine: *mut RegorusEngine) -> RegorusResult {
|
pub extern "C" fn regorus_engine_get_coverage_report(engine: *mut RegorusEngine) -> RegorusResult {
|
||||||
let output = || -> Result<String> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
let output = || -> Result<String> {
|
||||||
let guard = engine.try_read()?;
|
let engine = to_ref(engine)?;
|
||||||
Ok(serde_json::to_string_pretty(&guard.get_coverage_report()?)?)
|
let guard = engine.try_read()?;
|
||||||
}();
|
Ok(serde_json::to_string_pretty(&guard.get_coverage_report()?)?)
|
||||||
match output {
|
}();
|
||||||
Ok(out) => RegorusResult::ok_string(out),
|
match output {
|
||||||
Err(e) => to_regorus_result(Err(e)),
|
Ok(out) => RegorusResult::ok_string(out),
|
||||||
}
|
Err(e) => to_regorus_result(Err(e)),
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enable/disable strict builtin errors.
|
/// Enable/disable strict builtin errors.
|
||||||
@@ -318,12 +440,14 @@ pub extern "C" fn regorus_engine_set_strict_builtin_errors(
|
|||||||
engine: *mut RegorusEngine,
|
engine: *mut RegorusEngine,
|
||||||
strict: bool,
|
strict: bool,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
to_regorus_result(|| -> Result<()> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
to_regorus_result(|| -> Result<()> {
|
||||||
let mut guard = engine.try_write()?;
|
let engine = to_ref(engine)?;
|
||||||
guard.set_strict_builtin_errors(strict);
|
let mut guard = engine.try_write()?;
|
||||||
Ok(())
|
guard.set_strict_builtin_errors(strict);
|
||||||
}())
|
Ok(())
|
||||||
|
}())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get pretty printed coverage report.
|
/// Get pretty printed coverage report.
|
||||||
@@ -334,15 +458,17 @@ pub extern "C" fn regorus_engine_set_strict_builtin_errors(
|
|||||||
pub extern "C" fn regorus_engine_get_coverage_report_pretty(
|
pub extern "C" fn regorus_engine_get_coverage_report_pretty(
|
||||||
engine: *mut RegorusEngine,
|
engine: *mut RegorusEngine,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
let output = || -> Result<String> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
let output = || -> Result<String> {
|
||||||
let guard = engine.try_read()?;
|
let engine = to_ref(engine)?;
|
||||||
guard.get_coverage_report()?.to_string_pretty()
|
let guard = engine.try_read()?;
|
||||||
}();
|
guard.get_coverage_report()?.to_string_pretty()
|
||||||
match output {
|
}();
|
||||||
Ok(out) => RegorusResult::ok_string(out),
|
match output {
|
||||||
Err(e) => to_regorus_result(Err(e)),
|
Ok(out) => RegorusResult::ok_string(out),
|
||||||
}
|
Err(e) => to_regorus_result(Err(e)),
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear coverage data.
|
/// Clear coverage data.
|
||||||
@@ -351,12 +477,14 @@ pub extern "C" fn regorus_engine_get_coverage_report_pretty(
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[cfg(feature = "coverage")]
|
#[cfg(feature = "coverage")]
|
||||||
pub extern "C" fn regorus_engine_clear_coverage_data(engine: *mut RegorusEngine) -> RegorusResult {
|
pub extern "C" fn regorus_engine_clear_coverage_data(engine: *mut RegorusEngine) -> RegorusResult {
|
||||||
to_regorus_result(|| -> Result<()> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
to_regorus_result(|| -> Result<()> {
|
||||||
let mut guard = engine.try_write()?;
|
let engine = to_ref(engine)?;
|
||||||
guard.clear_coverage_data();
|
let mut guard = engine.try_write()?;
|
||||||
Ok(())
|
guard.clear_coverage_data();
|
||||||
}())
|
Ok(())
|
||||||
|
}())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether to gather output of print statements.
|
/// Whether to gather output of print statements.
|
||||||
@@ -368,12 +496,14 @@ pub extern "C" fn regorus_engine_set_gather_prints(
|
|||||||
engine: *mut RegorusEngine,
|
engine: *mut RegorusEngine,
|
||||||
enable: bool,
|
enable: bool,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
to_regorus_result(|| -> Result<()> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
to_regorus_result(|| -> Result<()> {
|
||||||
let mut guard = engine.try_write()?;
|
let engine = to_ref(engine)?;
|
||||||
guard.set_gather_prints(enable);
|
let mut guard = engine.try_write()?;
|
||||||
Ok(())
|
guard.set_gather_prints(enable);
|
||||||
}())
|
Ok(())
|
||||||
|
}())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take all the gathered print statements.
|
/// Take all the gathered print statements.
|
||||||
@@ -381,15 +511,17 @@ pub extern "C" fn regorus_engine_set_gather_prints(
|
|||||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.take_prints
|
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.take_prints
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn regorus_engine_take_prints(engine: *mut RegorusEngine) -> RegorusResult {
|
pub extern "C" fn regorus_engine_take_prints(engine: *mut RegorusEngine) -> RegorusResult {
|
||||||
let output = || -> Result<String> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
let output = || -> Result<String> {
|
||||||
let mut guard = engine.try_write()?;
|
let engine = to_ref(engine)?;
|
||||||
Ok(serde_json::to_string_pretty(&guard.take_prints()?)?)
|
let mut guard = engine.try_write()?;
|
||||||
}();
|
Ok(serde_json::to_string_pretty(&guard.take_prints()?)?)
|
||||||
match output {
|
}();
|
||||||
Ok(out) => RegorusResult::ok_string(out),
|
match output {
|
||||||
Err(e) => to_regorus_result(Err(e)),
|
Ok(out) => RegorusResult::ok_string(out),
|
||||||
}
|
Err(e) => to_regorus_result(Err(e)),
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get AST of policies.
|
/// Get AST of policies.
|
||||||
@@ -398,15 +530,17 @@ pub extern "C" fn regorus_engine_take_prints(engine: *mut RegorusEngine) -> Rego
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[cfg(feature = "ast")]
|
#[cfg(feature = "ast")]
|
||||||
pub extern "C" fn regorus_engine_get_ast_as_json(engine: *mut RegorusEngine) -> RegorusResult {
|
pub extern "C" fn regorus_engine_get_ast_as_json(engine: *mut RegorusEngine) -> RegorusResult {
|
||||||
let output = || -> Result<String> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
let output = || -> Result<String> {
|
||||||
let guard = engine.try_read()?;
|
let engine = to_ref(engine)?;
|
||||||
guard.get_ast_as_json()
|
let guard = engine.try_read()?;
|
||||||
}();
|
guard.get_ast_as_json()
|
||||||
match output {
|
}();
|
||||||
Ok(out) => RegorusResult::ok_string(out),
|
match output {
|
||||||
Err(e) => to_regorus_result(Err(e)),
|
Ok(out) => RegorusResult::ok_string(out),
|
||||||
}
|
Err(e) => to_regorus_result(Err(e)),
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the package names defined in each policy added to the engine.
|
/// Gets the package names defined in each policy added to the engine.
|
||||||
@@ -417,15 +551,18 @@ pub extern "C" fn regorus_engine_get_ast_as_json(engine: *mut RegorusEngine) ->
|
|||||||
pub extern "C" fn regorus_engine_get_policy_package_names(
|
pub extern "C" fn regorus_engine_get_policy_package_names(
|
||||||
engine: *mut RegorusEngine,
|
engine: *mut RegorusEngine,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
let output = || -> Result<String> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
let output = || -> Result<String> {
|
||||||
let guard = engine.try_read()?;
|
let engine = to_ref(engine)?;
|
||||||
serde_json::to_string_pretty(&guard.get_policy_package_names()?).map_err(anyhow::Error::msg)
|
let guard = engine.try_read()?;
|
||||||
}();
|
serde_json::to_string_pretty(&guard.get_policy_package_names()?)
|
||||||
match output {
|
.map_err(anyhow::Error::msg)
|
||||||
Ok(out) => RegorusResult::ok_string(out),
|
}();
|
||||||
Err(e) => to_regorus_result(Err(e)),
|
match output {
|
||||||
}
|
Ok(out) => RegorusResult::ok_string(out),
|
||||||
|
Err(e) => to_regorus_result(Err(e)),
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the parameters defined in each policy added to the engine.
|
/// Gets the parameters defined in each policy added to the engine.
|
||||||
@@ -436,15 +573,18 @@ pub extern "C" fn regorus_engine_get_policy_package_names(
|
|||||||
pub extern "C" fn regorus_engine_get_policy_parameters(
|
pub extern "C" fn regorus_engine_get_policy_parameters(
|
||||||
engine: *mut RegorusEngine,
|
engine: *mut RegorusEngine,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
let output = || -> Result<String> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
let output = || -> Result<String> {
|
||||||
let guard = engine.try_read()?;
|
let engine = to_ref(engine)?;
|
||||||
serde_json::to_string_pretty(&guard.get_policy_parameters()?).map_err(anyhow::Error::msg)
|
let guard = engine.try_read()?;
|
||||||
}();
|
serde_json::to_string_pretty(&guard.get_policy_parameters()?)
|
||||||
match output {
|
.map_err(anyhow::Error::msg)
|
||||||
Ok(out) => RegorusResult::ok_string(out),
|
}();
|
||||||
Err(e) => to_regorus_result(Err(e)),
|
match output {
|
||||||
}
|
Ok(out) => RegorusResult::ok_string(out),
|
||||||
|
Err(e) => to_regorus_result(Err(e)),
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enable/disable rego v1.
|
/// Enable/disable rego v1.
|
||||||
@@ -455,16 +595,18 @@ pub extern "C" fn regorus_engine_set_rego_v0(
|
|||||||
engine: *mut RegorusEngine,
|
engine: *mut RegorusEngine,
|
||||||
enable: bool,
|
enable: bool,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
let output = || -> Result<()> {
|
with_unwind_guard(|| {
|
||||||
let engine = to_ref(engine)?;
|
let output = || -> Result<()> {
|
||||||
let mut guard = engine.try_write()?;
|
let engine = to_ref(engine)?;
|
||||||
guard.set_rego_v0(enable);
|
let mut guard = engine.try_write()?;
|
||||||
Ok(())
|
guard.set_rego_v0(enable);
|
||||||
}();
|
Ok(())
|
||||||
match output {
|
}();
|
||||||
Ok(()) => RegorusResult::ok_void(),
|
match output {
|
||||||
Err(e) => to_regorus_result(Err(e)),
|
Ok(()) => RegorusResult::ok_void(),
|
||||||
}
|
Err(e) => to_regorus_result(Err(e)),
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compile a target-aware policy from the current engine state.
|
/// Compile a target-aware policy from the current engine state.
|
||||||
@@ -476,37 +618,39 @@ pub extern "C" fn regorus_engine_set_rego_v0(
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
pub extern "C" fn regorus_engine_compile_for_target(engine: *mut RegorusEngine) -> RegorusResult {
|
pub extern "C" fn regorus_engine_compile_for_target(engine: *mut RegorusEngine) -> RegorusResult {
|
||||||
let engine = match to_ref(engine) {
|
with_unwind_guard(|| {
|
||||||
Ok(engine) => engine,
|
let engine = match to_ref(engine) {
|
||||||
Err(e) => {
|
Ok(engine) => engine,
|
||||||
return RegorusResult::err_with_message(
|
Err(e) => {
|
||||||
RegorusStatus::InvalidArgument,
|
return RegorusResult::err_with_message(
|
||||||
format!("Failed to get engine reference: {e}"),
|
RegorusStatus::InvalidArgument,
|
||||||
)
|
format!("Failed to get engine reference: {e}"),
|
||||||
}
|
)
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let mut guard = match engine.try_write() {
|
let mut guard = match engine.try_write() {
|
||||||
Ok(guard) => guard,
|
Ok(guard) => guard,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return RegorusResult::err_with_message(
|
return RegorusResult::err_with_message(
|
||||||
RegorusStatus::Error,
|
RegorusStatus::Error,
|
||||||
format!("Failed to lock engine: {e}"),
|
format!("Failed to lock engine: {e}"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match guard.compile_for_target() {
|
match guard.compile_for_target() {
|
||||||
Ok(compiled_policy) => {
|
Ok(compiled_policy) => {
|
||||||
let wrapped_policy = RegorusCompiledPolicy { compiled_policy };
|
let wrapped_policy = RegorusCompiledPolicy { compiled_policy };
|
||||||
let boxed_policy = Box::new(wrapped_policy);
|
let boxed_policy = Box::new(wrapped_policy);
|
||||||
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut c_void)
|
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut c_void)
|
||||||
|
}
|
||||||
|
Err(e) => RegorusResult::err_with_message(
|
||||||
|
RegorusStatus::CompilationFailed,
|
||||||
|
format!("Failed to compile for target: {e}"),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
Err(e) => RegorusResult::err_with_message(
|
})
|
||||||
RegorusStatus::CompilationFailed,
|
|
||||||
format!("Failed to compile for target: {e}"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compile a policy with a specific entry point rule.
|
/// Compile a policy with a specific entry point rule.
|
||||||
@@ -520,23 +664,25 @@ pub extern "C" fn regorus_engine_compile_with_entrypoint(
|
|||||||
engine: *mut RegorusEngine,
|
engine: *mut RegorusEngine,
|
||||||
rule: *const c_char,
|
rule: *const c_char,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
let result = || -> Result<RegorusCompiledPolicy> {
|
with_unwind_guard(|| {
|
||||||
let rule_str = from_c_str(rule)?;
|
let result = || -> Result<RegorusCompiledPolicy> {
|
||||||
let rule_rc: regorus::Rc<str> = rule_str.into();
|
let rule_str = from_c_str(rule)?;
|
||||||
let engine = to_ref(engine)?;
|
let rule_rc: regorus::Rc<str> = rule_str.into();
|
||||||
let mut guard = engine.try_write()?;
|
let engine = to_ref(engine)?;
|
||||||
let compiled_policy = guard.compile_with_entrypoint(&rule_rc)?;
|
let mut guard = engine.try_write()?;
|
||||||
Ok(RegorusCompiledPolicy { compiled_policy })
|
let compiled_policy = guard.compile_with_entrypoint(&rule_rc)?;
|
||||||
}();
|
Ok(RegorusCompiledPolicy { compiled_policy })
|
||||||
|
}();
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(wrapped_policy) => {
|
Ok(wrapped_policy) => {
|
||||||
let boxed_policy = Box::new(wrapped_policy);
|
let boxed_policy = Box::new(wrapped_policy);
|
||||||
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut c_void)
|
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut c_void)
|
||||||
|
}
|
||||||
|
Err(e) => RegorusResult::err_with_message(
|
||||||
|
RegorusStatus::CompilationFailed,
|
||||||
|
format!("Failed to compile with entrypoint: {e}"),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
Err(e) => RegorusResult::err_with_message(
|
})
|
||||||
RegorusStatus::CompilationFailed,
|
|
||||||
format!("Failed to compile with entrypoint: {e}"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,5 +12,6 @@ mod compiled_policy;
|
|||||||
mod effect_registry;
|
mod effect_registry;
|
||||||
mod engine;
|
mod engine;
|
||||||
mod lock;
|
mod lock;
|
||||||
|
mod panic_guard;
|
||||||
mod schema_registry;
|
mod schema_registry;
|
||||||
mod target_registry;
|
mod target_registry;
|
||||||
|
|||||||
@@ -0,0 +1,159 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
//! Minimal helpers for catching panics inside the FFI layer.
|
||||||
|
//!
|
||||||
|
//! These are not yet wired into the exported functions; they will
|
||||||
|
//! be used once the integration work is complete.
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use crate::common::{RegorusResult, RegorusStatus};
|
||||||
|
|
||||||
|
use alloc::string::String;
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::{
|
||||||
|
backtrace::Backtrace,
|
||||||
|
cell::RefCell,
|
||||||
|
panic::{self, AssertUnwindSafe},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
thread_local! {
|
||||||
|
// Stashes the formatted panic + backtrace for whichever call last panicked on this thread.
|
||||||
|
static PANIC_BACKTRACE: RefCell<Option<String>> = const { RefCell::new(None) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
type PanicHook = dyn Fn(&panic::PanicHookInfo<'_>) + Sync + Send + 'static;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
/// RAII helper that installs a per-call panic hook and restores the prior hook on drop.
|
||||||
|
struct PanicHookGuard {
|
||||||
|
previous: Option<Box<PanicHook>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl PanicHookGuard {
|
||||||
|
fn install() -> Self {
|
||||||
|
// Remember whatever hook the embedding application already registered.
|
||||||
|
let previous = panic::take_hook();
|
||||||
|
PANIC_BACKTRACE.with(|slot| {
|
||||||
|
slot.replace(None);
|
||||||
|
});
|
||||||
|
// Install our temporary hook so we can capture a backtrace for this invocation.
|
||||||
|
panic::set_hook(Box::new(|info| {
|
||||||
|
let backtrace = Backtrace::force_capture();
|
||||||
|
PANIC_BACKTRACE.with(|slot| {
|
||||||
|
slot.replace(Some(format!(
|
||||||
|
"panic hook observed: {}\nbacktrace:\n{:#?}",
|
||||||
|
info, backtrace
|
||||||
|
)));
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
Self {
|
||||||
|
previous: Some(previous),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl Drop for PanicHookGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(previous) = self.previous.take() {
|
||||||
|
// Restore the original panic hook before we return control to the host.
|
||||||
|
panic::set_hook(previous);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static POISONED: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
/// Result of attempting to run `f` while guarding against unwinding.
|
||||||
|
pub(crate) enum GuardResult<T> {
|
||||||
|
/// Closure completed successfully.
|
||||||
|
Success(T),
|
||||||
|
/// Closure panicked; contains a best-effort string payload.
|
||||||
|
Panic(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn with_unwind_guard<F>(f: F) -> RegorusResult
|
||||||
|
where
|
||||||
|
F: FnOnce() -> RegorusResult,
|
||||||
|
{
|
||||||
|
if is_poisoned() {
|
||||||
|
return poisoned_result();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The closure passed across this boundary closes over raw pointers and lock guards.
|
||||||
|
// These types are not unwind safe by default and may become poisoned if a panic occurs.
|
||||||
|
// We therefore use AssertUnwindSafe to get the compiler to accept the closure.
|
||||||
|
// Upon unwind, we mark regorus as poisoned and disallow further use.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
{
|
||||||
|
let outcome = {
|
||||||
|
let _hook_guard = PanicHookGuard::install();
|
||||||
|
match panic::catch_unwind(AssertUnwindSafe(f)) {
|
||||||
|
Ok(value) => GuardResult::Success(value),
|
||||||
|
Err(payload) => GuardResult::Panic(panic_message_to_string(payload)),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
finalize(outcome)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
return finalize(GuardResult::Success(f()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finalize(outcome: GuardResult<RegorusResult>) -> RegorusResult {
|
||||||
|
match outcome {
|
||||||
|
GuardResult::Success(result) => result,
|
||||||
|
GuardResult::Panic(message) => {
|
||||||
|
trip_poison();
|
||||||
|
RegorusResult::err_with_message(RegorusStatus::Panic, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
fn panic_message_to_string(payload: Box<dyn core::any::Any + Send + 'static>) -> String {
|
||||||
|
let mut message = if let Some(s) = payload.downcast_ref::<&str>() {
|
||||||
|
(*s).into()
|
||||||
|
} else if let Some(s) = payload.downcast_ref::<String>() {
|
||||||
|
s.clone()
|
||||||
|
} else {
|
||||||
|
String::from("regorus encountered panic")
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(backtrace) = take_panic_backtrace() {
|
||||||
|
message.push('\n');
|
||||||
|
message.push_str(&backtrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
message
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
fn take_panic_backtrace() -> Option<String> {
|
||||||
|
PANIC_BACKTRACE.with(|slot| slot.borrow_mut().take())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poisoned_result() -> RegorusResult {
|
||||||
|
RegorusResult::err_with_message(
|
||||||
|
RegorusStatus::Poisoned,
|
||||||
|
String::from("regorus is poisoned after a previous panic"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trip_poison() {
|
||||||
|
POISONED.store(true, Ordering::Release);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_poisoned() -> bool {
|
||||||
|
POISONED.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn reset_poison() {
|
||||||
|
POISONED.store(false, Ordering::Release);
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
#![cfg(feature = "azure_policy")]
|
#![cfg(feature = "azure_policy")]
|
||||||
|
|
||||||
use crate::common::{from_c_str, RegorusResult, RegorusStatus};
|
use crate::common::{from_c_str, RegorusResult, RegorusStatus};
|
||||||
|
use crate::panic_guard::with_unwind_guard;
|
||||||
use regorus::{registry::schemas, Schema};
|
use regorus::{registry::schemas, Schema};
|
||||||
|
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
@@ -32,45 +33,45 @@ pub extern "C" fn regorus_resource_schema_register(
|
|||||||
name: *const c_char,
|
name: *const c_char,
|
||||||
schema_json: *const c_char,
|
schema_json: *const c_char,
|
||||||
) -> RegorusResult {
|
) -> RegorusResult {
|
||||||
let schema_name = match from_c_str(name) {
|
with_unwind_guard(|| {
|
||||||
Ok(s) => s,
|
let schema_name = match from_c_str(name) {
|
||||||
Err(e) => {
|
Ok(s) => s,
|
||||||
return RegorusResult::err_with_message(
|
Err(e) => {
|
||||||
RegorusStatus::InvalidArgument,
|
return RegorusResult::err_with_message(
|
||||||
format!("Invalid schema name string: {e}"),
|
RegorusStatus::InvalidArgument,
|
||||||
)
|
format!("Invalid schema name string: {e}"),
|
||||||
}
|
)
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let schema_str = match from_c_str(schema_json) {
|
let schema_str = match from_c_str(schema_json) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return RegorusResult::err_with_message(
|
return RegorusResult::err_with_message(
|
||||||
RegorusStatus::InvalidDataFormat,
|
RegorusStatus::InvalidDataFormat,
|
||||||
format!("Invalid schema JSON string: {e}"),
|
format!("Invalid schema JSON string: {e}"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse schema from JSON
|
let schema = match Schema::from_json_str(&schema_str) {
|
||||||
let schema = match Schema::from_json_str(&schema_str) {
|
Ok(schema) => schema,
|
||||||
Ok(schema) => schema,
|
Err(e) => {
|
||||||
Err(e) => {
|
return RegorusResult::err_with_message(
|
||||||
return RegorusResult::err_with_message(
|
RegorusStatus::InvalidDataFormat,
|
||||||
RegorusStatus::InvalidDataFormat,
|
format!("Failed to parse schema JSON: {e}"),
|
||||||
format!("Failed to parse schema JSON: {e}"),
|
)
|
||||||
)
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
// Register the schema
|
match schemas::resource::register(schema_name, schema.into()) {
|
||||||
match schemas::resource::register(schema_name, schema.into()) {
|
Ok(()) => RegorusResult::ok_pointer(std::ptr::null_mut()),
|
||||||
Ok(()) => RegorusResult::ok_pointer(std::ptr::null_mut()),
|
Err(e) => RegorusResult::err_with_message(
|
||||||
Err(e) => RegorusResult::err_with_message(
|
RegorusStatus::Error,
|
||||||
RegorusStatus::Error,
|
format!("Failed to register schema: {e}"),
|
||||||
format!("Failed to register schema: {e}"),
|
),
|
||||||
),
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a resource schema with the given name exists.
|
/// Check if a resource schema with the given name exists.
|
||||||
@@ -86,18 +87,20 @@ pub extern "C" fn regorus_resource_schema_register(
|
|||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn regorus_resource_schema_contains(name: *const c_char) -> RegorusResult {
|
pub extern "C" fn regorus_resource_schema_contains(name: *const c_char) -> RegorusResult {
|
||||||
let schema_name = match from_c_str(name) {
|
with_unwind_guard(|| {
|
||||||
Ok(s) => s,
|
let schema_name = match from_c_str(name) {
|
||||||
Err(e) => {
|
Ok(s) => s,
|
||||||
return RegorusResult::err_with_message(
|
Err(e) => {
|
||||||
RegorusStatus::InvalidArgument,
|
return RegorusResult::err_with_message(
|
||||||
format!("Invalid schema name string: {e}"),
|
RegorusStatus::InvalidArgument,
|
||||||
)
|
format!("Invalid schema name string: {e}"),
|
||||||
}
|
)
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let contains = schemas::resource::contains(&schema_name);
|
let contains = schemas::resource::contains(&schema_name);
|
||||||
RegorusResult::ok_bool(contains)
|
RegorusResult::ok_bool(contains)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the number of registered resource schemas.
|
/// Get the number of registered resource schemas.
|
||||||
@@ -107,8 +110,10 @@ pub extern "C" fn regorus_resource_schema_contains(name: *const c_char) -> Regor
|
|||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn regorus_resource_schema_len() -> RegorusResult {
|
pub extern "C" fn regorus_resource_schema_len() -> RegorusResult {
|
||||||
let count = schemas::resource::len();
|
with_unwind_guard(|| {
|
||||||
RegorusResult::ok_int(count as i64)
|
let count = schemas::resource::len();
|
||||||
|
RegorusResult::ok_int(count as i64)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the resource schema registry is empty.
|
/// Check if the resource schema registry is empty.
|
||||||
@@ -118,8 +123,10 @@ pub extern "C" fn regorus_resource_schema_len() -> RegorusResult {
|
|||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn regorus_resource_schema_is_empty() -> RegorusResult {
|
pub extern "C" fn regorus_resource_schema_is_empty() -> RegorusResult {
|
||||||
let is_empty = schemas::resource::is_empty();
|
with_unwind_guard(|| {
|
||||||
RegorusResult::ok_bool(is_empty)
|
let is_empty = schemas::resource::is_empty();
|
||||||
|
RegorusResult::ok_bool(is_empty)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List all registered resource schema names as a JSON array.
|
/// List all registered resource schema names as a JSON array.
|
||||||
@@ -129,14 +136,16 @@ pub extern "C" fn regorus_resource_schema_is_empty() -> RegorusResult {
|
|||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn regorus_resource_schema_list_names() -> RegorusResult {
|
pub extern "C" fn regorus_resource_schema_list_names() -> RegorusResult {
|
||||||
let names = schemas::resource::list_names();
|
with_unwind_guard(|| {
|
||||||
match serde_json::to_string(&names) {
|
let names = schemas::resource::list_names();
|
||||||
Ok(json_str) => RegorusResult::ok_string(json_str),
|
match serde_json::to_string(&names) {
|
||||||
Err(e) => RegorusResult::err_with_message(
|
Ok(json_str) => RegorusResult::ok_string(json_str),
|
||||||
RegorusStatus::Error,
|
Err(e) => RegorusResult::err_with_message(
|
||||||
format!("Failed to serialize schema names to JSON: {e}"),
|
RegorusStatus::Error,
|
||||||
),
|
format!("Failed to serialize schema names to JSON: {e}"),
|
||||||
}
|
),
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove a resource schema by name.
|
/// Remove a resource schema by name.
|
||||||
@@ -152,18 +161,20 @@ pub extern "C" fn regorus_resource_schema_list_names() -> RegorusResult {
|
|||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn regorus_resource_schema_remove(name: *const c_char) -> RegorusResult {
|
pub extern "C" fn regorus_resource_schema_remove(name: *const c_char) -> RegorusResult {
|
||||||
let schema_name = match from_c_str(name) {
|
with_unwind_guard(|| {
|
||||||
Ok(s) => s,
|
let schema_name = match from_c_str(name) {
|
||||||
Err(e) => {
|
Ok(s) => s,
|
||||||
return RegorusResult::err_with_message(
|
Err(e) => {
|
||||||
RegorusStatus::InvalidArgument,
|
return RegorusResult::err_with_message(
|
||||||
format!("Invalid schema name string: {e}"),
|
RegorusStatus::InvalidArgument,
|
||||||
)
|
format!("Invalid schema name string: {e}"),
|
||||||
}
|
)
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let removed = schemas::resource::remove(&schema_name).is_some();
|
let removed = schemas::resource::remove(&schema_name).is_some();
|
||||||
RegorusResult::ok_bool(removed)
|
RegorusResult::ok_bool(removed)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear all resource schemas from the registry.
|
/// Clear all resource schemas from the registry.
|
||||||
@@ -173,6 +184,8 @@ pub extern "C" fn regorus_resource_schema_remove(name: *const c_char) -> Regorus
|
|||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn regorus_resource_schema_clear() -> RegorusResult {
|
pub extern "C" fn regorus_resource_schema_clear() -> RegorusResult {
|
||||||
schemas::resource::clear();
|
with_unwind_guard(|| {
|
||||||
RegorusResult::ok_pointer(std::ptr::null_mut())
|
schemas::resource::clear();
|
||||||
|
RegorusResult::ok_pointer(std::ptr::null_mut())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#![cfg(feature = "azure_policy")]
|
#![cfg(feature = "azure_policy")]
|
||||||
|
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
use crate::panic_guard::with_unwind_guard;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
@@ -16,12 +17,14 @@ use std::os::raw::c_char;
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
pub extern "C" fn regorus_register_target_from_json(target_json: *const c_char) -> RegorusResult {
|
pub extern "C" fn regorus_register_target_from_json(target_json: *const c_char) -> RegorusResult {
|
||||||
to_regorus_result(|| -> Result<()> {
|
with_unwind_guard(|| {
|
||||||
let target_str = from_c_str(target_json)?;
|
to_regorus_result(|| -> Result<()> {
|
||||||
let target = regorus::Target::from_json_str(&target_str)?;
|
let target_str = from_c_str(target_json)?;
|
||||||
regorus::registry::targets::register(regorus::Rc::new(target))?;
|
let target = regorus::Target::from_json_str(&target_str)?;
|
||||||
Ok(())
|
regorus::registry::targets::register(regorus::Rc::new(target))?;
|
||||||
}())
|
Ok(())
|
||||||
|
}())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a target is registered.
|
/// Check if a target is registered.
|
||||||
@@ -36,31 +39,35 @@ pub extern "C" fn regorus_register_target_from_json(target_json: *const c_char)
|
|||||||
/// The name parameter must be a valid null-terminated UTF-8 string.
|
/// The name parameter must be a valid null-terminated UTF-8 string.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn regorus_target_registry_contains(name: *const c_char) -> RegorusResult {
|
pub extern "C" fn regorus_target_registry_contains(name: *const c_char) -> RegorusResult {
|
||||||
let target_name = match from_c_str(name) {
|
with_unwind_guard(|| {
|
||||||
Ok(s) => s,
|
let target_name = match from_c_str(name) {
|
||||||
Err(e) => {
|
Ok(s) => s,
|
||||||
return RegorusResult::err_with_message(
|
Err(e) => {
|
||||||
RegorusStatus::InvalidArgument,
|
return RegorusResult::err_with_message(
|
||||||
format!("Invalid target name string: {e}"),
|
RegorusStatus::InvalidArgument,
|
||||||
)
|
format!("Invalid target name string: {e}"),
|
||||||
}
|
)
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let contains = regorus::registry::targets::contains(&target_name);
|
let contains = regorus::registry::targets::contains(&target_name);
|
||||||
RegorusResult::ok_bool(contains)
|
RegorusResult::ok_bool(contains)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a list of all registered target names as JSON array.
|
/// Get a list of all registered target names as JSON array.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
pub extern "C" fn regorus_target_registry_list_names() -> RegorusResult {
|
pub extern "C" fn regorus_target_registry_list_names() -> RegorusResult {
|
||||||
let names = regorus::registry::targets::list_names();
|
with_unwind_guard(|| {
|
||||||
let output = serde_json::to_string_pretty(&names).map_err(anyhow::Error::msg);
|
let names = regorus::registry::targets::list_names();
|
||||||
|
let output = serde_json::to_string_pretty(&names).map_err(anyhow::Error::msg);
|
||||||
|
|
||||||
match output {
|
match output {
|
||||||
Ok(out) => RegorusResult::ok_string(out),
|
Ok(out) => RegorusResult::ok_string(out),
|
||||||
Err(e) => to_regorus_result(Err(e)),
|
Err(e) => to_regorus_result(Err(e)),
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove a target from the registry by name.
|
/// Remove a target from the registry by name.
|
||||||
@@ -69,19 +76,23 @@ pub extern "C" fn regorus_target_registry_list_names() -> RegorusResult {
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
pub extern "C" fn regorus_target_registry_remove(name: *const c_char) -> RegorusResult {
|
pub extern "C" fn regorus_target_registry_remove(name: *const c_char) -> RegorusResult {
|
||||||
to_regorus_result(|| -> Result<()> {
|
with_unwind_guard(|| {
|
||||||
let name_str = from_c_str(name)?;
|
to_regorus_result(|| -> Result<()> {
|
||||||
regorus::registry::targets::remove(&name_str);
|
let name_str = from_c_str(name)?;
|
||||||
Ok(())
|
regorus::registry::targets::remove(&name_str);
|
||||||
}())
|
Ok(())
|
||||||
|
}())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear all targets from the registry.
|
/// Clear all targets from the registry.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
pub extern "C" fn regorus_target_registry_clear() -> RegorusResult {
|
pub extern "C" fn regorus_target_registry_clear() -> RegorusResult {
|
||||||
regorus::registry::targets::clear();
|
with_unwind_guard(|| {
|
||||||
RegorusResult::ok_void()
|
regorus::registry::targets::clear();
|
||||||
|
RegorusResult::ok_void()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the number of registered targets.
|
/// Get the number of registered targets.
|
||||||
@@ -91,8 +102,10 @@ pub extern "C" fn regorus_target_registry_clear() -> RegorusResult {
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
pub extern "C" fn regorus_target_registry_len() -> RegorusResult {
|
pub extern "C" fn regorus_target_registry_len() -> RegorusResult {
|
||||||
let count = regorus::registry::targets::len();
|
with_unwind_guard(|| {
|
||||||
RegorusResult::ok_int(count as i64)
|
let count = regorus::registry::targets::len();
|
||||||
|
RegorusResult::ok_int(count as i64)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the target registry is empty.
|
/// Check if the target registry is empty.
|
||||||
@@ -102,6 +115,8 @@ pub extern "C" fn regorus_target_registry_len() -> RegorusResult {
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
pub extern "C" fn regorus_target_registry_is_empty() -> RegorusResult {
|
pub extern "C" fn regorus_target_registry_is_empty() -> RegorusResult {
|
||||||
let is_empty = regorus::registry::targets::is_empty();
|
with_unwind_guard(|| {
|
||||||
RegorusResult::ok_bool(is_empty)
|
let is_empty = regorus::registry::targets::is_empty();
|
||||||
|
RegorusResult::ok_bool(is_empty)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user