mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
feat!: add cooperative execution-time limits across engine, VM, and binding (#539)
- Introduce ExecutionTimer/ExecutionTimerConfig to allow limiting evaluating time. - To amortize time checking costs, checking interval can be configured via the notion of work units - A global fallback time limit can be set to universally limit all evaluation in addition to engine level limit setting. - Implement limnits in interpreter and RVM. In RVM, also handle suspend/resume so that time during pause is not counted. - Add engine-level APIs to set/clear per-engine timer configuration and apply global fallback defaults. - Surface execution-time limits through FFI and C# bindings - Add C# tests and example usage to validate engine overrides, global fallback behavior, and compiled policy enforcement. - Expand docs for execution-time limit - Add interpreter YAML cases and VM unit tests for time-limit behavior and deterministic time sources. Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
e68e852ee3
commit
394625d4bc
@@ -29,8 +29,11 @@ Once the workflow run completes, the generated Nuget can be downloaded by follow
|
||||
|
||||
## Local
|
||||
|
||||
<<<<<<< HEAD
|
||||
TODO
|
||||
The `cargo xtask` runner provides helpers for local builds:
|
||||
|
||||
1. `cargo xtask ffi` builds the `bindings/ffi` crate for the host platform in debug mode. Add `--target <triple>` (repeatable) to cross-compile, or `--release` to produce optimised artefacts. Results land under `bindings/ffi/target/<triple>/<profile>`.
|
||||
2. `cargo xtask nuget` reuses those artefacts to pack the C# library. It defaults to debug builds for the host but accepts `--target`, `--release`, `--artifacts-dir <path>` to reuse existing binaries, and `--enforce-artifacts` to require every officially supported platform.
|
||||
3. `cargo xtask test-csharp` ensures a NuGet is available (rebuilding when required or when `--force-nuget` is passed) and then runs `Regorus.Tests`, `TestApp`, and `TargetExampleApp` against it. The command accepts the same build flags as `cargo xtask nuget`.
|
||||
|
||||
## Memory Usage Safeguards
|
||||
|
||||
@@ -48,11 +51,11 @@ using var engine = new Regorus.Engine();
|
||||
var veryLargeJson = new string('x', 128 * 1024);
|
||||
try
|
||||
{
|
||||
engine.SetInputJson(veryLargeJson);
|
||||
engine.SetInputJson(veryLargeJson);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
Console.WriteLine($"Allocator reported: {ex.Message}");
|
||||
Console.WriteLine($"Allocator reported: {ex.Message}");
|
||||
}
|
||||
|
||||
// Restore defaults once done
|
||||
@@ -60,11 +63,4 @@ Regorus.MemoryLimits.SetGlobalMemoryLimit(null);
|
||||
Regorus.MemoryLimits.SetThreadFlushThresholdOverride(null);
|
||||
```
|
||||
|
||||
See `bindings/csharp/Regorus.Tests/RegorusTests.cs` for scenario coverage and `bindings/csharp/TargetExampleApp/Program.cs` for end-to-end usage.
|
||||
=======
|
||||
The `cargo xtask` runner provides helpers for local builds:
|
||||
|
||||
1. `cargo xtask ffi` builds the `bindings/ffi` crate for the host platform in debug mode. Add `--target <triple>` (repeatable) to cross-compile, or `--release` to produce optimised artefacts. Results land under `bindings/ffi/target/<triple>/<profile>`.
|
||||
2. `cargo xtask nuget` reuses those artefacts to pack the C# library. It defaults to debug builds for the host but accepts `--target`, `--release`, `--artifacts-dir <path>` to reuse existing binaries, and `--enforce-artifacts` to require every officially supported platform.
|
||||
3. `cargo xtask test-csharp` ensures a NuGet is available (rebuilding when required or when `--force-nuget` is passed) and then runs `Regorus.Tests`, `TestApp`, and `TargetExampleApp` against it. The command accepts the same build flags as `cargo xtask nuget`.
|
||||
>>>>>>> 380a27c (feat(xtask): consolidate CI workflows onto xtask helpers)
|
||||
See bindings/csharp/Regorus.Tests/RegorusTests.cs for scenario coverage and bindings/csharp/TargetExampleApp/Program.cs for end-to-end usage.
|
||||
|
||||
131
bindings/csharp/Regorus.Tests/ExecutionTimerTests.cs
Normal file
131
bindings/csharp/Regorus.Tests/ExecutionTimerTests.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Regorus;
|
||||
|
||||
namespace Regorus.Tests;
|
||||
|
||||
[DoNotParallelize] // Uses global fallback config; must run sequentially.
|
||||
[TestClass]
|
||||
public class ExecutionTimerTests
|
||||
{
|
||||
private const string Policy = @"
|
||||
package limits.timer
|
||||
import rego.v1
|
||||
|
||||
triplet_count := count([1 |
|
||||
x := data.values[_]
|
||||
y := data.values[_]
|
||||
z := data.values[_]
|
||||
])
|
||||
";
|
||||
|
||||
private const string Query = "data.limits.timer.triplet_count";
|
||||
private const int ValueCount = 160;
|
||||
|
||||
[TestMethod]
|
||||
public void Engine_limit_enforced()
|
||||
{
|
||||
Engine.ClearFallbackExecutionTimerConfig();
|
||||
using var engine = CreateEngine(ValueCount);
|
||||
var config = new ExecutionTimerConfig(TimeSpan.FromMilliseconds(2), checkInterval: 1);
|
||||
engine.SetExecutionTimerConfig(config);
|
||||
|
||||
var ex = Assert.ThrowsException<InvalidOperationException>(() => engine.EvalRule(Query));
|
||||
StringAssert.Contains(ex.Message, "execution exceeded time limit");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Fallback_applies_to_new_engines()
|
||||
{
|
||||
var fallback = new ExecutionTimerConfig(TimeSpan.FromMilliseconds(2), checkInterval: 1);
|
||||
Engine.SetFallbackExecutionTimerConfig(fallback);
|
||||
try
|
||||
{
|
||||
using var engine = CreateEngine(ValueCount);
|
||||
var ex = Assert.ThrowsException<InvalidOperationException>(() => engine.EvalRule(Query));
|
||||
StringAssert.Contains(ex.Message, "execution exceeded time limit");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Engine.ClearFallbackExecutionTimerConfig();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Engine_override_relaxes_fallback()
|
||||
{
|
||||
var fallback = new ExecutionTimerConfig(TimeSpan.FromMilliseconds(2), checkInterval: 1);
|
||||
Engine.SetFallbackExecutionTimerConfig(fallback);
|
||||
try
|
||||
{
|
||||
using var engine = CreateEngine(ValueCount);
|
||||
var relaxed = new ExecutionTimerConfig(TimeSpan.FromSeconds(12), checkInterval: 1);
|
||||
engine.SetExecutionTimerConfig(relaxed);
|
||||
|
||||
var resultJson = engine.EvalRule(Query);
|
||||
var result = JsonSerializer.Deserialize<int>(resultJson!);
|
||||
Assert.IsTrue(result > 0, "Expected a positive triplet count when limit is relaxed.");
|
||||
|
||||
engine.ClearExecutionTimerConfig();
|
||||
var ex = Assert.ThrowsException<InvalidOperationException>(() => engine.EvalRule(Query));
|
||||
StringAssert.Contains(ex.Message, "execution exceeded time limit");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Engine.ClearFallbackExecutionTimerConfig();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CompiledPolicy_limit_enforced()
|
||||
{
|
||||
var fallback = new ExecutionTimerConfig(TimeSpan.FromMilliseconds(2), checkInterval: 1);
|
||||
Engine.SetFallbackExecutionTimerConfig(fallback);
|
||||
try
|
||||
{
|
||||
using var policy = CreateCompiledPolicy(ValueCount);
|
||||
var ex = Assert.ThrowsException<InvalidOperationException>(() => policy.EvalWithInput("null"));
|
||||
StringAssert.Contains(ex.Message, "execution exceeded time limit");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Engine.ClearFallbackExecutionTimerConfig();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CompiledPolicy_uses_engine_limits_only()
|
||||
{
|
||||
// Compiled policies no longer store per-policy execution timers; limits are managed by Engine.
|
||||
Engine.ClearFallbackExecutionTimerConfig();
|
||||
using var policy = CreateCompiledPolicy(ValueCount);
|
||||
var resultJson = policy.EvalWithInput("null");
|
||||
var result = JsonSerializer.Deserialize<int>(resultJson!);
|
||||
Assert.IsTrue(result > 0, "CompiledPolicy should evaluate using engine defaults without its own timer");
|
||||
}
|
||||
|
||||
private static Engine CreateEngine(int valueCount)
|
||||
{
|
||||
var engine = new Engine();
|
||||
engine.AddPolicy("limits_timer.rego", Policy);
|
||||
engine.AddDataJson(CreateData(valueCount));
|
||||
return engine;
|
||||
}
|
||||
|
||||
private static CompiledPolicy CreateCompiledPolicy(int valueCount)
|
||||
{
|
||||
var modules = new[] { new PolicyModule("limits_timer.rego", Policy) };
|
||||
return Compiler.CompilePolicyWithEntrypoint(CreateData(valueCount), modules, Query);
|
||||
}
|
||||
|
||||
private static string CreateData(int valueCount)
|
||||
{
|
||||
var payload = new { values = Enumerable.Range(0, valueCount).ToArray() };
|
||||
return JsonSerializer.Serialize(payload);
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ namespace Regorus.Tests;
|
||||
[TestClass]
|
||||
public class RegorusTests
|
||||
{
|
||||
private static readonly object LimitLock = new();
|
||||
private static readonly object LimitLock = new();
|
||||
|
||||
[TestMethod]
|
||||
public void Basic_evaluation_succeeds()
|
||||
@@ -374,17 +374,17 @@ stretched := concat("", [input.block | numbers.range(0, input.repeat - 1)[_]])
|
||||
|
||||
private sealed class MemoryLimitScope : IDisposable
|
||||
{
|
||||
private readonly ulong? _originalLimit;
|
||||
private readonly ulong? _originalLimit;
|
||||
|
||||
public MemoryLimitScope()
|
||||
{
|
||||
_originalLimit = MemoryLimits.GetGlobalMemoryLimit();
|
||||
}
|
||||
public MemoryLimitScope()
|
||||
{
|
||||
_originalLimit = MemoryLimits.GetGlobalMemoryLimit();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
MemoryLimits.SetGlobalMemoryLimit(_originalLimit);
|
||||
MemoryLimits.FlushThreadMemoryCounters();
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
MemoryLimits.SetGlobalMemoryLimit(_originalLimit);
|
||||
MemoryLimits.FlushThreadMemoryCounters();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,5 +203,14 @@ namespace Regorus
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UseHandle(Action<IntPtr> action)
|
||||
{
|
||||
UseHandle<object?>(handlePtr =>
|
||||
{
|
||||
action(handlePtr);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Regorus.Internal;
|
||||
|
||||
|
||||
@@ -24,6 +26,17 @@ namespace Regorus
|
||||
_handle = RegorusEngineHandle.Create();
|
||||
}
|
||||
|
||||
public static void SetFallbackExecutionTimerConfig(ExecutionTimerConfig config)
|
||||
{
|
||||
var nativeConfig = config.ToNative();
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_set_fallback_execution_timer_config(nativeConfig));
|
||||
}
|
||||
|
||||
public static void ClearFallbackExecutionTimerConfig()
|
||||
{
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_clear_fallback_execution_timer_config());
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
@@ -87,6 +100,32 @@ namespace Regorus
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void SetExecutionTimerConfig(ExecutionTimerConfig config)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
var nativeConfig = config.ToNative();
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var localConfig = nativeConfig;
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_execution_timer_config((Regorus.Internal.RegorusEngine*)enginePtr, &localConfig));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void ClearExecutionTimerConfig()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_clear_execution_timer_config((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
}
|
||||
});
|
||||
}
|
||||
public string? AddPolicy(string path, string rego)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
@@ -355,7 +394,21 @@ namespace Regorus
|
||||
});
|
||||
}
|
||||
|
||||
string? CheckAndDropResult(Regorus.Internal.RegorusResult result)
|
||||
private static string? StringFromUtf8(IntPtr ptr)
|
||||
{
|
||||
|
||||
#if NETSTANDARD2_1
|
||||
return Marshal.PtrToStringUTF8(ptr);
|
||||
#else
|
||||
int len = 0;
|
||||
while (Marshal.ReadByte(ptr, len) != 0) { ++len; }
|
||||
byte[] buffer = new byte[len];
|
||||
Marshal.Copy(ptr, buffer, 0, buffer.Length);
|
||||
return Encoding.UTF8.GetString(buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static string? CheckAndDropResult(Regorus.Internal.RegorusResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
62
bindings/csharp/Regorus/ExecutionTimerConfig.cs
Normal file
62
bindings/csharp/Regorus/ExecutionTimerConfig.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Regorus
|
||||
{
|
||||
/// <summary>
|
||||
/// Managed representation of the execution timer configuration used by the engine.
|
||||
/// </summary>
|
||||
public readonly struct ExecutionTimerConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExecutionTimerConfig"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="limit">Maximum wall-clock duration allowed for evaluation. Must be non-negative.</param>
|
||||
/// <param name="checkInterval">Number of work units between timer checks. Must be non-zero.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="limit"/> is negative or <paramref name="checkInterval"/> is zero.</exception>
|
||||
public ExecutionTimerConfig(TimeSpan limit, uint checkInterval)
|
||||
{
|
||||
if (limit < TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(limit), "Execution timer limit must be non-negative.");
|
||||
}
|
||||
|
||||
if (checkInterval == 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(checkInterval), "Execution timer check interval must be non-zero.");
|
||||
}
|
||||
|
||||
Limit = limit;
|
||||
CheckInterval = checkInterval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum wall-clock duration allowed for an evaluation.
|
||||
/// </summary>
|
||||
public TimeSpan Limit { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of work units between timer checks.
|
||||
/// </summary>
|
||||
public uint CheckInterval { get; }
|
||||
|
||||
internal Regorus.Internal.RegorusExecutionTimerConfig ToNative()
|
||||
{
|
||||
if (Limit < TimeSpan.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("Execution timer limit must be non-negative.");
|
||||
}
|
||||
|
||||
ulong ticks = checked((ulong)Limit.Ticks);
|
||||
ulong limitNanoseconds = checked(ticks * 100UL);
|
||||
|
||||
return new Regorus.Internal.RegorusExecutionTimerConfig
|
||||
{
|
||||
limit_ns = limitNanoseconds,
|
||||
check_interval = CheckInterval,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,7 +257,7 @@ namespace Regorus.Internal
|
||||
[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
|
||||
#if REGORUS_FFI_TEST_HOOKS
|
||||
/// <summary>
|
||||
/// Trigger a panic inside the engine for testing purposes.
|
||||
/// </summary>
|
||||
@@ -269,7 +269,35 @@ namespace Regorus.Internal
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_engine_test_reset_poison", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern void regorus_engine_test_reset_poison();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Configure the execution timer for a specific engine instance.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_engine_set_execution_timer_config", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_engine_set_execution_timer_config(RegorusEngine* engine, RegorusExecutionTimerConfig* config);
|
||||
|
||||
/// <summary>
|
||||
/// Clear the execution timer configuration for a specific engine instance.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_engine_clear_execution_timer_config", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_engine_clear_execution_timer_config(RegorusEngine* engine);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Execution Timer Global Methods
|
||||
|
||||
/// <summary>
|
||||
/// Set the process-wide fallback execution timer configuration.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_set_fallback_execution_timer_config", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_set_fallback_execution_timer_config(RegorusExecutionTimerConfig config);
|
||||
|
||||
/// <summary>
|
||||
/// Clear the process-wide fallback execution timer configuration.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_clear_fallback_execution_timer_config", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_clear_fallback_execution_timer_config();
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -579,6 +607,16 @@ namespace Regorus.Internal
|
||||
public byte* error_message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FFI representation of the execution timer configuration.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct RegorusExecutionTimerConfig
|
||||
{
|
||||
public ulong limit_ns;
|
||||
public uint check_interval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper for regorus::Engine.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace TargetExampleApp;
|
||||
@@ -32,14 +33,14 @@ allow if {
|
||||
|
||||
# Allow network security groups with proper inbound rules
|
||||
allow if {
|
||||
input.type == ""Microsoft.Network/networkSecurityGroups""
|
||||
count([rule |
|
||||
rule := input.properties.securityRules[_]
|
||||
rule.properties.direction == ""Inbound""
|
||||
rule.properties.access == ""Allow""
|
||||
rule.properties.sourceAddressPrefix == ""*""
|
||||
rule.properties.destinationPortRange in [parameters.allowedPorts]
|
||||
]) == 0
|
||||
input.type == ""Microsoft.Network/networkSecurityGroups""
|
||||
count([rule |
|
||||
rule := input.properties.securityRules[_]
|
||||
rule.properties.direction == ""Inbound""
|
||||
rule.properties.access == ""Allow""
|
||||
rule.properties.sourceAddressPrefix == ""*""
|
||||
rule.properties.destinationPortRange in [parameters.allowedPorts]
|
||||
]) == 0
|
||||
}";
|
||||
|
||||
private const string AZURE_STORAGE_POLICY_ASSIGNMENT = @"
|
||||
@@ -50,44 +51,58 @@ import rego.v1
|
||||
parameters.requiredTLSVersion = ""TLS1_2""
|
||||
parameters.allowedPorts = [""22"", ""3389""]";
|
||||
|
||||
private const string EXECUTION_TIMER_POLICY = @"
|
||||
package limits.timer
|
||||
import rego.v1
|
||||
|
||||
triplet_count := count([1 |
|
||||
x := data.values[_]
|
||||
y := data.values[_]
|
||||
z := data.values[_]
|
||||
])
|
||||
";
|
||||
|
||||
private const string EXECUTION_TIMER_QUERY = "data.limits.timer.triplet_count";
|
||||
private const int EXECUTION_TIMER_VALUE_COUNT = 40;
|
||||
|
||||
// Test data constants
|
||||
private const string COMPLIANT_STORAGE_ACCOUNT = @"{
|
||||
""type"": ""Microsoft.Storage/storageAccounts"",
|
||||
""name"": ""compliantstorageacct"",
|
||||
""location"": ""eastus"",
|
||||
""kind"": ""StorageV2"",
|
||||
""properties"": {
|
||||
""supportsHttpsTrafficOnly"": true,
|
||||
""minimumTlsVersion"": ""TLS1_2"",
|
||||
""allowBlobPublicAccess"": false,
|
||||
""encryption"": {
|
||||
""services"": {
|
||||
""blob"": { ""enabled"": true },
|
||||
""file"": { ""enabled"": true }
|
||||
}
|
||||
}
|
||||
},
|
||||
""tags"": {
|
||||
""environment"": ""production""
|
||||
""type"": ""Microsoft.Storage/storageAccounts"",
|
||||
""name"": ""compliantstorageacct"",
|
||||
""location"": ""eastus"",
|
||||
""kind"": ""StorageV2"",
|
||||
""properties"": {
|
||||
""supportsHttpsTrafficOnly"": true,
|
||||
""minimumTlsVersion"": ""TLS1_2"",
|
||||
""allowBlobPublicAccess"": false,
|
||||
""encryption"": {
|
||||
""services"": {
|
||||
""blob"": { ""enabled"": true },
|
||||
""file"": { ""enabled"": true }
|
||||
}
|
||||
}
|
||||
},
|
||||
""tags"": {
|
||||
""environment"": ""production""
|
||||
}
|
||||
}";
|
||||
|
||||
private const string NON_COMPLIANT_STORAGE_ACCOUNT = @"{
|
||||
""type"": ""Microsoft.Storage/storageAccounts"",
|
||||
""name"": ""insecurestorageacct"",
|
||||
""location"": ""westus"",
|
||||
""kind"": ""Storage"",
|
||||
""properties"": {
|
||||
""supportsHttpsTrafficOnly"": false,
|
||||
""minimumTlsVersion"": ""TLS1_0"",
|
||||
""allowBlobPublicAccess"": true,
|
||||
""encryption"": {
|
||||
""services"": {
|
||||
""blob"": { ""enabled"": false },
|
||||
""file"": { ""enabled"": false }
|
||||
}
|
||||
}
|
||||
""type"": ""Microsoft.Storage/storageAccounts"",
|
||||
""name"": ""insecurestorageacct"",
|
||||
""location"": ""westus"",
|
||||
""kind"": ""Storage"",
|
||||
""properties"": {
|
||||
""supportsHttpsTrafficOnly"": false,
|
||||
""minimumTlsVersion"": ""TLS1_0"",
|
||||
""allowBlobPublicAccess"": true,
|
||||
""encryption"": {
|
||||
""services"": {
|
||||
""blob"": { ""enabled"": false },
|
||||
""file"": { ""enabled"": false }
|
||||
}
|
||||
}
|
||||
}
|
||||
}";
|
||||
|
||||
static void Main(string[] args)
|
||||
@@ -96,7 +111,6 @@ parameters.allowedPorts = [""22"", ""3389""]";
|
||||
|
||||
try
|
||||
{
|
||||
DemonstrateMemoryLimitHelpers();
|
||||
DemonstrateTargetFunctionality();
|
||||
Console.WriteLine("\n=== Target demonstration completed successfully! ===");
|
||||
}
|
||||
@@ -157,6 +171,9 @@ parameters.allowedPorts = [""22"", ""3389""]";
|
||||
// 4. Demonstrate thread-safe concurrent evaluation
|
||||
Console.WriteLine("\n4. Testing concurrent evaluation from multiple threads:");
|
||||
DemonstrateConcurrentEvaluation(compiledPolicy);
|
||||
|
||||
Console.WriteLine("\n5. Execution timer configuration:");
|
||||
DemonstrateExecutionTimer();
|
||||
}
|
||||
|
||||
static void DemonstrateConcurrentEvaluation(Regorus.CompiledPolicy compiledPolicy)
|
||||
@@ -214,45 +231,6 @@ parameters.allowedPorts = [""22"", ""3389""]";
|
||||
Console.WriteLine("✓ No locks required - CompiledPolicy is thread-safe!");
|
||||
}
|
||||
|
||||
static void DemonstrateMemoryLimitHelpers()
|
||||
{
|
||||
Console.WriteLine("REGORUS MEMORY LIMIT UTILITIES");
|
||||
Console.WriteLine("==============================");
|
||||
|
||||
var originalLimit = Regorus.MemoryLimits.GetGlobalMemoryLimit();
|
||||
var originalThreshold = Regorus.MemoryLimits.GetThreadMemoryFlushThreshold();
|
||||
|
||||
try
|
||||
{
|
||||
const ulong demoLimit = 64 * 1024 * 1024;
|
||||
Regorus.MemoryLimits.SetGlobalMemoryLimit(demoLimit);
|
||||
Console.WriteLine($"✓ Global memory limit set to {Regorus.MemoryLimits.GetGlobalMemoryLimit():N0} bytes");
|
||||
|
||||
const ulong flushThreshold = 256 * 1024;
|
||||
Regorus.MemoryLimits.SetThreadFlushThresholdOverride(flushThreshold);
|
||||
Console.WriteLine($"✓ Thread flush threshold configured for {Regorus.MemoryLimits.GetThreadMemoryFlushThreshold():N0} bytes");
|
||||
|
||||
Console.WriteLine("Attempting an allocation that exceeds a 1 byte budget...");
|
||||
Regorus.MemoryLimits.SetGlobalMemoryLimit(1);
|
||||
|
||||
using var engine = new Regorus.Engine();
|
||||
var payload = new string('x', 128 * 1024);
|
||||
engine.SetInputJson($"{{\"payload\":\"{payload}\"}}");
|
||||
}
|
||||
catch (InvalidOperationException error)
|
||||
{
|
||||
Console.WriteLine($"✗ Memory limit exceeded: {error.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Regorus.MemoryLimits.SetThreadFlushThresholdOverride(null);
|
||||
Regorus.MemoryLimits.SetGlobalMemoryLimit(originalLimit);
|
||||
Regorus.MemoryLimits.FlushThreadMemoryCounters();
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
static void DemonstratePolicyInfo(Regorus.CompiledPolicy compiledPolicy)
|
||||
{
|
||||
Console.WriteLine("Getting policy metadata using GetPolicyInfo()...");
|
||||
@@ -328,4 +306,57 @@ parameters.allowedPorts = [""22"", ""3389""]";
|
||||
Console.WriteLine($"✗ Failed to get policy info: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
static void DemonstrateExecutionTimer()
|
||||
{
|
||||
var dataJson = JsonSerializer.Serialize(new
|
||||
{
|
||||
values = Enumerable.Range(0, EXECUTION_TIMER_VALUE_COUNT).ToArray()
|
||||
});
|
||||
|
||||
var fallback = new Regorus.ExecutionTimerConfig(TimeSpan.FromMilliseconds(2), checkInterval: 1);
|
||||
var relaxed = new Regorus.ExecutionTimerConfig(TimeSpan.FromMilliseconds(1000), checkInterval: 1);
|
||||
|
||||
Console.WriteLine($" Configuring fallback timer (limit={fallback.Limit.TotalMilliseconds:F0} ms, interval={fallback.CheckInterval})...");
|
||||
|
||||
Regorus.Engine.SetFallbackExecutionTimerConfig(fallback);
|
||||
try
|
||||
{
|
||||
using var engine = new Regorus.Engine();
|
||||
engine.AddPolicy("limits_timer.rego", EXECUTION_TIMER_POLICY);
|
||||
engine.AddDataJson(dataJson);
|
||||
|
||||
Console.WriteLine(" Evaluating under fallback limit (expected failure)...");
|
||||
try
|
||||
{
|
||||
engine.EvalRule(EXECUTION_TIMER_QUERY);
|
||||
Console.WriteLine(" ⚠ Evaluation unexpectedly succeeded under fallback limit.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($" ✓ Fallback enforced: {ex.Message}");
|
||||
}
|
||||
|
||||
Console.WriteLine($" Applying per-engine override ({relaxed.Limit.TotalMilliseconds:F0} ms) and retrying...");
|
||||
engine.SetExecutionTimerConfig(relaxed);
|
||||
var result = engine.EvalRule(EXECUTION_TIMER_QUERY);
|
||||
Console.WriteLine($" ✓ Override succeeded; triplet_count = {result}");
|
||||
|
||||
Console.WriteLine(" Clearing engine override to restore fallback...");
|
||||
engine.ClearExecutionTimerConfig();
|
||||
try
|
||||
{
|
||||
engine.EvalRule(EXECUTION_TIMER_QUERY);
|
||||
Console.WriteLine(" ⚠ Evaluation unexpectedly succeeded after clearing override.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($" ✓ Fallback restored: {ex.Message}");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Regorus.Engine.ClearFallbackExecutionTimerConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user