feat: Complete target system with C# bindings and resource inference (#458)

* feat: Add Schema Registry and Validation Framework

This commit introduces a comprehensive schema registry and validation framework, providing schema-based validation of resources and policy effects.

- Thread-safe, in-memory registry for schema storage and management
- Global registry patterns for effects and resources
- Concurrent access with proper error handling
- Unicode schema names support

- JSON Schema-compliant validation for all primitive types
- Advanced constraint validation (patterns, ranges, length limits)
- Discriminated union support with anyOf schemas
- Detailed error reporting with nested validation paths
- Discriminated subobject validation for polymorphic schemas

- **Registry Tests**: All registry operations
- **Effect Tests**: Policy effect validation
- **Resource Tests**: Resource validation
- **Validation Tests**: Core validation engine
- Thread-safety, error handling, integration scenarios, edge cases

- **Dependencies**: dashmap, once_cell, regex
- **Thread Safety**: Minimal locking with Rc<Schema> sharing
- **Error Types**: TypeMismatch, OutOfRange, PatternMismatch, etc.

- Complete schema registry and validation subsystem
- Comprehensive test coverage
- Foundation for policy validation in Regorus

Benchmarks:

- Criterion benchmarks for basic types, effects and Azure resources
- Performance range: 3.22ns (string) to 34.74µs (Azure VM resource schema validation)
- String withs patterns validation: 30.2µs. Need to explore whether regex caching helps
  bring this down.
- Azure policy effects: 188ns-1.4µs

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>

* feat: Complete target system with C# bindings and resource inference

- Add comprehensive target system with TargetRegistry and target-aware compilation
- Implement resource type inference from policy equality expressions
- Create modular C# bindings with separate wrapper classes for each concept
- Add thread-safe CompiledPolicy with reference counting for safe disposal
- Enhance FFI with detailed error propagation and target functionality
- Create TargetExampleApp demonstrating Azure Policy integration
- Add CI/CD pipeline testing for all C# applications
- Support target definitions with schema validation and resource selectors
- Implement PolicyModule struct and target-aware compilation methods
- Add comprehensive test coverage for target functionality

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>

---------

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
Anand Krishnamoorthi
2025-08-19 20:23:43 -05:00
committed by GitHub
parent 3c33d31d08
commit cc917ea75d
71 changed files with 10278 additions and 1000 deletions

421
bindings/csharp/API.md Normal file
View File

@@ -0,0 +1,421 @@
# Regorus C# API Documentation
This document describes the C# API for Regorus, focusing on the compiled policy approach for high-performance policy evaluation.
## Overview
The Regorus C# bindings provide a modern, thread-safe API for compiling and evaluating Open Policy Agent (OPA) Rego policies. The API is designed around pre-compiled policies that can be evaluated efficiently multiple times with different inputs.
## Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ CompiledPolicy Workflow │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Policy Modules │ │ Target/Schema │ │ Static Data │
│ (.rego files) │ │ Registries │ │ (JSON) │
└─────────┬───────┘ └────────┬─────────┘ └─────────┬───────┘
│ │ │
└─────────────────────┼────────────────────────┘
┌─────────────────────────┐
│ Compile │
│ ┌─────────────────────┐│
│ │ Parse & Analyze ││
│ │ Infer Resource Types││
│ │ Build AST & Rules ││
│ │ Target Integration ││
│ └─────────────────────┘│
└─────────────┬───────────┘
┌─────────────────────────┐
│ CompiledPolicy │
│ ┌─────────────────────┐ │
│ │ AST & Rules │ │
│ │ Target Info │ │
│ │ Resource Types │ │
│ │ Function Table │ │
│ │ Compiled Modules │ │
│ └─────────────────────┘ │
└─────────────┬───────────┘
┌─────────────────────┐
│ Service Cache │
│ (Policy Framework, │
│ MS Graph, etc.) │
│ ┌─────────────────┐ │
│ │ CompiledPolicy │ │ ◄─── Same LOCK-FREE policy
│ │ (cached) │ │ instance shared across
│ └─────────────────┘ │ all threads
└─────────┬───────────┘
┌───────┼───────┬───────┐
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Thread 1 │ │ Thread 2 │ │ Thread N │
│ │ │ │ │ │
│ input1 ────▶│ │ input2 ────▶│ │ inputN ────▶│
│ ◄─── result │ │ ◄─── result │ │ ◄─── result │
└─────────────┘ └─────────────┘ └─────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Key Benefits │
├─────────────────────────────────────────────────────────────────┤
│ ✓ Compile Once, Evaluate Many ✓ Lock-Free Concurrent Eval │
│ ✓ No Re-parsing Overhead ✓ Reference Counting Safety │
│ ✓ Reduced GC Pressure ✓ Proper Resource Management │
│ ✓ Cache-Friendly Design ✓ Target System Integration │
└─────────────────────────────────────────────────────────────────┘
```
## Key Features
- **Pre-compiled Policies**: Compile once, evaluate many times for optimal performance
- **Target System Support**: Built-in support for Azure Policy targets with resource type inference
- **Thread Safety**: All operations are thread-safe without external synchronization
- **Registry Management**: Centralized management of targets and schemas
- **Policy Introspection**: Rich metadata about compiled policies
## Core Classes
### CompiledPolicy
The `CompiledPolicy` class represents a pre-compiled Rego policy that can be evaluated efficiently.
```csharp
public sealed class CompiledPolicy : IDisposable
{
// Evaluate the policy with input data
public string? EvalWithInput(string inputJson);
// Get comprehensive policy metadata
public PolicyInfo GetPolicyInfo();
// Dispose of unmanaged resources
public void Dispose();
}
```
**Thread Safety**: All methods are thread-safe. Multiple threads can call `EvalWithInput()` concurrently, and `Dispose()` will safely wait for active evaluations to complete.
### Compiler
The `Compiler` class provides static methods for compiling policies.
```csharp
public static class Compiler
{
// Compile a policy with a specific entrypoint rule
public static CompiledPolicy CompilePolicyWithEntrypoint(
string dataJson,
IEnumerable<PolicyModule> modules,
string entryPointRule);
// Compile a target-aware policy (requires azure_policy feature)
public static CompiledPolicy CompilePolicyForTarget(
string dataJson,
IEnumerable<PolicyModule> modules);
}
```
### PolicyModule
Represents a single policy module to be compiled. Each PolicyModule corresponds to a Rego file (.rego), and each Rego file defines a Rego package using the `package` declaration at the top of the file.
```csharp
public struct PolicyModule
{
public string Id { get; set; }
public string Content { get; set; }
public PolicyModule(string id, string content);
}
```
**Properties:**
- `Id`: A unique identifier for the module, typically the filename (e.g., "policy.rego", "rules/storage.rego")
- `Content`: The complete Rego policy content, including the `package` declaration and all rules
**Example:**
```csharp
var module = new PolicyModule("storage-policy.rego", @"
package azure.storage
import rego.v1
default allow := false
allow if input.type == ""Microsoft.Storage/storageAccounts""
");
```
### PolicyInfo
Provides comprehensive metadata about a compiled policy.
```csharp
public class PolicyInfo
{
// List of module identifiers
public List<string> ModuleIds { get; set; }
// Target name (for target-aware policies)
public string? TargetName { get; set; }
// Resource types this policy can evaluate
public List<string> ApplicableResourceTypes { get; set; }
// Primary rule/entrypoint
public string EntrypointRule { get; set; }
// Effect rule (for target-aware policies)
public string? EffectRule { get; set; }
// Policy parameters
public List<PolicyParameters> Parameters { get; set; }
}
```
## Registry Classes
### TargetRegistry
Manages target definitions for Azure Policy-style evaluations.
```csharp
public static class TargetRegistry
{
// Register a target from JSON
public static void RegisterFromJson(string targetJson);
// Check if a target exists
public static bool Contains(string name);
// List all registered targets
public static string ListNames();
// Remove a target
public static bool Remove(string name);
// Clear all targets
public static void Clear();
// Get count of registered targets
public static int Count { get; }
// Check if registry is empty
public static bool IsEmpty { get; }
}
```
### SchemaRegistry
Manages schema definitions for validation.
```csharp
public static class SchemaRegistry
{
// Register resource schemas
public static void RegisterResourceSchema(string name, string schemaJson);
public static bool ContainsResourceSchema(string name);
public static string ListResourceSchemas();
// Register effect schemas
public static void RegisterEffectSchema(string name, string schemaJson);
public static bool ContainsEffectSchema(string name);
public static string ListEffectSchemas();
// Clear methods
public static void ClearResourceSchemas();
public static void ClearEffectSchemas();
}
```
## Usage Examples
### Basic Policy Compilation and Evaluation
```csharp
// Define policy modules
var modules = new List<PolicyModule>
{
new PolicyModule("policy.rego", @"
package example
import rego.v1
default allow := false
allow if input.user == ""admin""
")
};
// Compile the policy
using var policy = Compiler.CompilePolicyWithEntrypoint("{}", modules, "data.example.allow");
// Evaluate with different inputs
var result1 = policy.EvalWithInput(@"{""user"": ""admin""}"); // true
var result2 = policy.EvalWithInput(@"{""user"": ""guest""}"); // false
```
### Target-Aware Policy (Azure Policy Style)
```csharp
// Register target definition
TargetRegistry.RegisterFromJson(@"{
""name"": ""azure.storage"",
""resource_schema_selector"": ""type"",
""resource_types"": {
""Microsoft.Storage/storageAccounts"": {
""schema"": { /* JSON Schema */ }
}
}
}");
// Define policy with target
var modules = new List<PolicyModule>
{
new PolicyModule("policy.rego", @"
package policy
import rego.v1
__target__ := ""azure.storage""
default effect := ""deny""
effect := ""allow"" if {
input.type == ""Microsoft.Storage/storageAccounts""
input.properties.supportsHttpsTrafficOnly == true
}
")
};
// Compile for target
using var policy = Compiler.CompilePolicyForTarget("{}", modules);
// Evaluate Azure resource
var resource = @"{
""type"": ""Microsoft.Storage/storageAccounts"",
""properties"": {
""supportsHttpsTrafficOnly"": true
}
}";
var result = policy.EvalWithInput(resource); // "allow"
```
### Policy Introspection
```csharp
// Get policy metadata
var info = policy.GetPolicyInfo();
Console.WriteLine($"Target: {info.TargetName}");
Console.WriteLine($"Effect Rule: {info.EffectRule}");
Console.WriteLine($"Modules: {string.Join(", ", info.ModuleIds)}");
Console.WriteLine($"Resource Types: {string.Join(", ", info.ApplicableResourceTypes)}");
// Access parameters
if (info.Parameters != null && info.Parameters.Count > 0)
{
foreach (var parameterSet in info.Parameters)
{
Console.WriteLine($"Module: {parameterSet.SourceFile}");
foreach (var param in parameterSet.Parameters)
{
Console.WriteLine($"Parameter: {param.Name} ({param.Type})");
if (param.Default != null)
Console.WriteLine($" Default: {param.Default}");
}
}
}
```
### Concurrent Evaluation
```csharp
// CompiledPolicy is thread-safe
var tasks = Enumerable.Range(0, 100).Select(i =>
Task.Run(() => policy.EvalWithInput($@"{{""id"": {i}}}"))
).ToArray();
var results = await Task.WhenAll(tasks);
```
## Performance Considerations
### Compilation Overhead
- Policy compilation has significant overhead due to parsing and analysis
- **Best Practice**: Compile once, reuse many times
- Consider caching compiled policies for repeated use
### Memory Management
- `CompiledPolicy` manages unmanaged resources
- **Always** dispose of compiled policies using `using` statements or explicit `Dispose()`
- Disposal is thread-safe and waits for active evaluations
### Thread Safety
- All classes are thread-safe for concurrent reads/evaluations
- Registry modifications should be done during initialization
- No external synchronization required
## Error Handling
All methods throw `Exception` on errors with descriptive messages:
```csharp
try
{
var policy = Compiler.CompilePolicyWithEntrypoint(data, modules, rule);
var result = policy.EvalWithInput(input);
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
```
## Feature Flags
Some functionality requires specific Rust feature flags:
- **azure_policy**: Required for target-aware compilation and policy parameters
- Without this feature, target-related methods will not be available
## Version Compatibility
- Requires .NET Standard 2.0 or later
- Compatible with .NET Framework 4.6.1+, .NET Core 2.0+, .NET 5+
- Uses System.Text.Json for JSON serialization (added as dependency)
## Best Practices
1. **Compile Once, Evaluate Many**: Pre-compile policies for repeated evaluation
2. **Use Disposable Pattern**: Always dispose of CompiledPolicy instances
3. **Thread-Safe Design**: Take advantage of built-in thread safety
4. **Registry Setup**: Configure targets and schemas during application startup
5. **Error Handling**: Wrap operations in try-catch blocks for robust error handling
6. **Performance Monitoring**: Monitor evaluation times for performance optimization
## Migration from Engine-Based API
If migrating from an engine-based approach:
```csharp
// Old approach (if it existed)
// var engine = new Engine();
// engine.AddPolicy("policy.rego", policyContent);
// engine.SetInputJson(inputJson);
// var result = engine.EvalRule("data.policy.allow");
// New compiled approach
var modules = new[] { new PolicyModule("policy.rego", policyContent) };
using var policy = Compiler.CompilePolicyWithEntrypoint("{}", modules, "data.policy.allow");
var result = policy.EvalWithInput(inputJson);
```
The compiled approach provides better performance for repeated evaluations and clearer resource management.

View File

@@ -0,0 +1,168 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Text;
using System.Text.Json;
#nullable enable
namespace Regorus
{
/// <summary>
/// Represents a compiled Regorus policy that can be evaluated efficiently.
/// This class wraps a pre-compiled policy that can be evaluated multiple times
/// with different inputs without recompilation overhead.
///
/// This class manages unmanaged resources and should not be copied or cloned.
/// Each instance represents a unique native policy object.
///
/// Thread Safety: This class is thread-safe for all operations. Multiple threads
/// can safely call EvalWithInput() concurrently, and Dispose() will safely wait
/// for all active evaluations to complete before freeing resources. No external
/// synchronization is required.
/// </summary>
public unsafe sealed class CompiledPolicy : IDisposable
{
private Internal.RegorusCompiledPolicy* _policy;
private int _isDisposed;
private int _activeEvaluations;
internal CompiledPolicy(Internal.RegorusCompiledPolicy* policy)
{
_policy = policy;
}
/// <summary>
/// Evaluates the compiled policy with the given input.
/// For target policies, evaluates the target's effect rule.
/// For regular policies, evaluates the originally compiled rule.
/// </summary>
/// <param name="inputJson">JSON encoded input data (resource) to validate against the policy</param>
/// <returns>The evaluation result as JSON string</returns>
/// <exception cref="Exception">Thrown when policy evaluation fails</exception>
/// <exception cref="ObjectDisposedException">Thrown when the policy has been disposed</exception>
public string? EvalWithInput(string inputJson)
{
// Increment active evaluations count
System.Threading.Interlocked.Increment(ref _activeEvaluations);
try
{
ThrowIfDisposed();
var inputBytes = Encoding.UTF8.GetBytes(inputJson + char.MinValue);
fixed (byte* inputPtr = inputBytes)
{
return CheckAndDropResult(Internal.API.regorus_compiled_policy_eval_with_input(_policy, inputPtr));
}
}
finally
{
// Decrement active evaluations count
System.Threading.Interlocked.Decrement(ref _activeEvaluations);
}
}
/// <summary>
/// Gets information about the compiled policy including metadata about modules,
/// target configuration, and resource types.
/// </summary>
/// <returns>Policy information containing module IDs, target name, applicable resource types, entry point rule, and parameters</returns>
/// <exception cref="Exception">Thrown when getting policy info fails</exception>
/// <exception cref="ObjectDisposedException">Thrown when the policy has been disposed</exception>
public PolicyInfo GetPolicyInfo()
{
ThrowIfDisposed();
var jsonResult = CheckAndDropResult(Internal.API.regorus_compiled_policy_get_policy_info(_policy));
if (string.IsNullOrEmpty(jsonResult))
{
throw new Exception("Failed to get policy info: empty response");
}
try
{
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
return JsonSerializer.Deserialize<PolicyInfo>(jsonResult!, options)
?? throw new Exception("Failed to deserialize policy info");
}
catch (JsonException ex)
{
throw new Exception($"Failed to parse policy info JSON: {ex.Message}", ex);
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (System.Threading.Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
{
if (_policy != null)
{
// Wait for all active evaluations to complete
while (System.Threading.Volatile.Read(ref _activeEvaluations) > 0)
{
System.Threading.Thread.Yield();
}
Internal.API.regorus_compiled_policy_drop(_policy);
_policy = null;
}
}
}
~CompiledPolicy() => Dispose(disposing: false);
private void ThrowIfDisposed()
{
if (_isDisposed != 0)
throw new ObjectDisposedException(nameof(CompiledPolicy));
}
private string? StringFromUTF8(IntPtr ptr)
{
#if NETSTANDARD2_1
return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr);
#else
int len = 0;
while (System.Runtime.InteropServices.Marshal.ReadByte(ptr, len) != 0) { ++len; }
byte[] buffer = new byte[len];
System.Runtime.InteropServices.Marshal.Copy(ptr, buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer);
#endif
}
private string? CheckAndDropResult(Internal.RegorusResult result)
{
try
{
if (result.status != Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
throw new Exception(message ?? "Unknown error occurred");
}
return result.data_type switch
{
Internal.RegorusDataType.String => StringFromUTF8((IntPtr)result.output),
Internal.RegorusDataType.Boolean => result.bool_value.ToString().ToLowerInvariant(),
Internal.RegorusDataType.Integer => result.int_value.ToString(),
Internal.RegorusDataType.None => null,
_ => StringFromUTF8((IntPtr)result.output)
};
}
finally
{
Internal.API.regorus_result_drop(result);
}
}
}
}

View File

@@ -0,0 +1,196 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
#nullable enable
namespace Regorus
{
/// <summary>
/// Represents a policy module with an ID and content.
/// </summary>
public struct PolicyModule
{
/// <summary>
/// Gets or sets the unique identifier for this policy module.
/// </summary>
public string Id { get; set; }
/// <summary>
/// Gets or sets the Rego policy content.
/// </summary>
public string Content { get; set; }
/// <summary>
/// Initializes a new instance of the PolicyModule struct.
/// </summary>
/// <param name="id">The unique identifier for this policy module</param>
/// <param name="content">The Rego policy content</param>
public PolicyModule(string id, string content)
{
Id = id;
Content = content;
}
}
/// <summary>
/// Provides static methods for compiling policies into efficient compiled representations.
/// These are convenience methods that create an engine internally and perform compilation.
/// </summary>
public static unsafe class Compiler
{
/// <summary>
/// Compiles a policy from data and modules with a specific entry point rule.
/// This is a convenience function that sets up an Engine internally and calls the appropriate compilation method.
/// </summary>
/// <param name="dataJson">JSON string containing static data for policy evaluation</param>
/// <param name="modules">List of policy modules to compile</param>
/// <param name="entryPointRule">The specific rule path to evaluate (e.g., "data.policy.allow")</param>
/// <returns>A compiled policy that can be evaluated efficiently</returns>
/// <exception cref="Exception">Thrown when compilation fails</exception>
public static CompiledPolicy CompilePolicyWithEntrypoint(string dataJson, IEnumerable<PolicyModule> modules, string entryPointRule)
{
var dataBytes = Encoding.UTF8.GetBytes(dataJson + char.MinValue);
var entryPointBytes = Encoding.UTF8.GetBytes(entryPointRule + char.MinValue);
var modulesArray = modules.ToArray();
// Convert C# modules to native structs
var nativeModules = new Internal.RegorusPolicyModule[modulesArray.Length];
var pinnedHandles = new List<GCHandle>();
try
{
for (int i = 0; i < modulesArray.Length; i++)
{
var idBytes = Encoding.UTF8.GetBytes(modulesArray[i].Id + char.MinValue);
var contentBytes = Encoding.UTF8.GetBytes(modulesArray[i].Content + char.MinValue);
var idHandle = GCHandle.Alloc(idBytes, GCHandleType.Pinned);
var contentHandle = GCHandle.Alloc(contentBytes, GCHandleType.Pinned);
pinnedHandles.Add(idHandle);
pinnedHandles.Add(contentHandle);
nativeModules[i] = new Internal.RegorusPolicyModule
{
id = (byte*)idHandle.AddrOfPinnedObject(),
content = (byte*)contentHandle.AddrOfPinnedObject()
};
}
fixed (byte* dataPtr = dataBytes)
fixed (byte* entryPointPtr = entryPointBytes)
fixed (Internal.RegorusPolicyModule* modulesPtr = nativeModules)
{
var result = Internal.API.regorus_compile_policy_with_entrypoint(
dataPtr, modulesPtr, (UIntPtr)modulesArray.Length, entryPointPtr);
var policy = GetCompiledPolicyResult(result);
return policy;
}
}
finally
{
foreach (var handle in pinnedHandles)
{
handle.Free();
}
}
}
/// <summary>
/// Compiles a target-aware policy from data and modules.
/// This is a convenience function that sets up an Engine internally and calls target-aware compilation.
/// At least one module must contain a `__target__` declaration.
/// </summary>
/// <param name="dataJson">JSON string containing static data for policy evaluation</param>
/// <param name="modules">List of policy modules to compile</param>
/// <returns>A compiled policy that can be evaluated efficiently</returns>
/// <exception cref="Exception">Thrown when compilation fails</exception>
public static CompiledPolicy CompilePolicyForTarget(string dataJson, IEnumerable<PolicyModule> modules)
{
var dataBytes = Encoding.UTF8.GetBytes(dataJson + char.MinValue);
var modulesArray = modules.ToArray();
// Convert C# modules to native structs
var nativeModules = new Internal.RegorusPolicyModule[modulesArray.Length];
var pinnedHandles = new List<GCHandle>();
try
{
for (int i = 0; i < modulesArray.Length; i++)
{
var idBytes = Encoding.UTF8.GetBytes(modulesArray[i].Id + char.MinValue);
var contentBytes = Encoding.UTF8.GetBytes(modulesArray[i].Content + char.MinValue);
var idHandle = GCHandle.Alloc(idBytes, GCHandleType.Pinned);
var contentHandle = GCHandle.Alloc(contentBytes, GCHandleType.Pinned);
pinnedHandles.Add(idHandle);
pinnedHandles.Add(contentHandle);
nativeModules[i] = new Internal.RegorusPolicyModule
{
id = (byte*)idHandle.AddrOfPinnedObject(),
content = (byte*)contentHandle.AddrOfPinnedObject()
};
}
fixed (byte* dataPtr = dataBytes)
fixed (Internal.RegorusPolicyModule* modulesPtr = nativeModules)
{
var result = Internal.API.regorus_compile_policy_for_target(
dataPtr, modulesPtr, (UIntPtr)modulesArray.Length);
var policy = GetCompiledPolicyResult(result);
return policy;
}
}
finally
{
foreach (var handle in pinnedHandles)
{
handle.Free();
}
}
}
private static string? StringFromUTF8(IntPtr ptr)
{
#if NETSTANDARD2_1
return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr);
#else
int len = 0;
while (System.Runtime.InteropServices.Marshal.ReadByte(ptr, len) != 0) { ++len; }
byte[] buffer = new byte[len];
System.Runtime.InteropServices.Marshal.Copy(ptr, buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer);
#endif
}
private static CompiledPolicy GetCompiledPolicyResult(Internal.RegorusResult result)
{
try
{
if (result.status != Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
throw new Exception(message ?? "Unknown compilation error occurred");
}
if (result.data_type != Internal.RegorusDataType.Pointer || result.pointer_value == null)
{
throw new Exception("Expected compiled policy pointer but got different data type");
}
return new CompiledPolicy((Internal.RegorusCompiledPolicy*)result.pointer_value);
}
finally
{
Internal.API.regorus_result_drop(result);
}
}
}
}

View File

@@ -243,7 +243,7 @@ namespace Regorus
string? CheckAndDropResult(Regorus.Internal.RegorusResult result)
{
if (result.status != Regorus.Internal.RegorusStatus.RegorusStatusOk)
if (result.status != Regorus.Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
var ex = new Exception(message);

View File

@@ -0,0 +1,546 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Runtime.InteropServices;
#pragma warning disable CS8500
#pragma warning disable CS8981
namespace Regorus.Internal
{
/// <summary>
/// Native FFI method declarations for Regorus.
/// This file contains all P/Invoke declarations for the Regorus native library.
/// </summary>
internal static unsafe partial class API
{
private const string LibraryName = "regorus_ffi";
#region Common Methods
/// <summary>
/// Drop a RegorusResult.
/// output and error_message strings are not valid after drop.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_result_drop", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern void regorus_result_drop(RegorusResult result);
#endregion
#region Engine Methods
/// <summary>
/// Construct a new Engine.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusEngine* regorus_engine_new();
/// <summary>
/// Clone a RegorusEngine.
/// To avoid having to parse same policy again, the engine can be cloned
/// after policies and data have been added.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusEngine* regorus_engine_clone(RegorusEngine* engine);
/// <summary>
/// Drop a RegorusEngine.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_drop", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern void regorus_engine_drop(RegorusEngine* engine);
/// <summary>
/// Add a policy.
/// The policy is parsed into AST.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.add_policy
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_add_policy", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_add_policy(RegorusEngine* engine, byte* path, byte* rego);
/// <summary>
/// Add a policy from file.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_add_policy_from_file", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_add_policy_from_file(RegorusEngine* engine, byte* path);
/// <summary>
/// Add policy data.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.add_data
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_add_data_json", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_add_data_json(RegorusEngine* engine, byte* data);
/// <summary>
/// Get list of loaded Rego packages as JSON.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_packages
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_get_packages", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_get_packages(RegorusEngine* engine);
/// <summary>
/// Get list of policies as JSON.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_policies
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_get_policies", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_get_policies(RegorusEngine* engine);
/// <summary>
/// Add data from JSON file.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_add_data_from_json_file", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_add_data_from_json_file(RegorusEngine* engine, byte* path);
/// <summary>
/// Clear policy data.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.clear_data
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_clear_data", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_clear_data(RegorusEngine* engine);
/// <summary>
/// Set input.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_input
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_set_input_json", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_set_input_json(RegorusEngine* engine, byte* input);
/// <summary>
/// Set input from JSON file.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_set_input_from_json_file", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_set_input_from_json_file(RegorusEngine* engine, byte* path);
/// <summary>
/// Evaluate query.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.eval_query
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_eval_query", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_eval_query(RegorusEngine* engine, byte* query);
/// <summary>
/// Evaluate specified rule.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.eval_rule
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_eval_rule", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_eval_rule(RegorusEngine* engine, byte* rule);
/// <summary>
/// Enable/disable coverage.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_enable_coverage
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_set_enable_coverage", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_set_enable_coverage(RegorusEngine* engine, [MarshalAs(UnmanagedType.U1)] bool enable);
/// <summary>
/// Get coverage report.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_coverage_report
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_get_coverage_report", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_get_coverage_report(RegorusEngine* engine);
/// <summary>
/// Enable/disable strict builtin errors.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_strict_builtin_errors
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_set_strict_builtin_errors", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_set_strict_builtin_errors(RegorusEngine* engine, [MarshalAs(UnmanagedType.U1)] bool strict);
/// <summary>
/// Get pretty printed coverage report.
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Report.html#method.to_string_pretty
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_get_coverage_report_pretty", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_get_coverage_report_pretty(RegorusEngine* engine);
/// <summary>
/// Clear coverage data.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.clear_coverage_data
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_clear_coverage_data", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_clear_coverage_data(RegorusEngine* engine);
/// <summary>
/// Whether to gather output of print statements.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_gather_prints
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_set_gather_prints", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_set_gather_prints(RegorusEngine* engine, [MarshalAs(UnmanagedType.U1)] bool enable);
/// <summary>
/// Take all the gathered print statements.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.take_prints
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_take_prints", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_take_prints(RegorusEngine* engine);
/// <summary>
/// Get AST of policies.
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_ast_as_json
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_get_ast_as_json", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_get_ast_as_json(RegorusEngine* engine);
/// <summary>
/// Gets the package names defined in each policy added to the engine.
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_policy_package_names
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_get_policy_package_names", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_get_policy_package_names(RegorusEngine* engine);
/// <summary>
/// Gets the parameters defined in each policy added to the engine.
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_policy_parameters
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_get_policy_parameters", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_get_policy_parameters(RegorusEngine* engine);
/// <summary>
/// Enable/disable rego v1.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_rego_v0
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_set_rego_v0", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_set_rego_v0(RegorusEngine* engine, [MarshalAs(UnmanagedType.U1)] bool enable);
/// <summary>
/// Compile a target-aware policy from the current engine state.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.compile_for_target
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_engine_compile_for_target", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_compile_for_target(RegorusEngine* engine);
/// <summary>
/// Compile a policy with a specific entry point rule.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.compile_with_entrypoint
/// </summary>
[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);
#endregion
#region Compilation Methods
/// <summary>
/// Compiles a policy from data and modules with a specific entry point rule.
/// This is a convenience function that wraps regorus::compile_policy_with_entrypoint.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_compile_policy_with_entrypoint", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_compile_policy_with_entrypoint(byte* data_json, RegorusPolicyModule* modules, UIntPtr modules_len, byte* entry_point_rule);
/// <summary>
/// Compiles a target-aware policy from data and modules.
/// This is a convenience function that wraps regorus::compile_policy_for_target.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_compile_policy_for_target", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_compile_policy_for_target(byte* data_json, RegorusPolicyModule* modules, UIntPtr modules_len);
#endregion
#region Compiled Policy Methods
/// <summary>
/// Drop a RegorusCompiledPolicy.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_compiled_policy_drop", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern void regorus_compiled_policy_drop(RegorusCompiledPolicy* compiled_policy);
/// <summary>
/// Evaluate the compiled policy with the given input.
/// For target policies, evaluates the target's effect rule.
/// For regular policies, evaluates the originally compiled rule.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_compiled_policy_eval_with_input", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_compiled_policy_eval_with_input(RegorusCompiledPolicy* compiled_policy, byte* input);
/// <summary>
/// Get information about the compiled policy including metadata about modules,
/// target configuration, and resource types.
/// Returns a JSON-encoded PolicyInfo struct containing comprehensive
/// information about the compiled policy such as module IDs, target name,
/// applicable resource types, entry point rule, and parameters.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_compiled_policy_get_policy_info", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_compiled_policy_get_policy_info(RegorusCompiledPolicy* compiled_policy);
#endregion
#region Target Registry Methods
/// <summary>
/// Register a target from JSON definition.
/// The target JSON should follow the target schema format.
/// Once registered, the target can be referenced in Rego policies using __target__ rules.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_register_target_from_json", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_register_target_from_json(byte* target_json);
/// <summary>
/// Check if a target is registered.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_target_registry_contains", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_target_registry_contains(byte* name);
/// <summary>
/// Get a list of all registered target names as JSON array.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_target_registry_list_names", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_target_registry_list_names();
/// <summary>
/// Remove a target from the registry by name.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_target_registry_remove", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_target_registry_remove(byte* name);
/// <summary>
/// Clear all targets from the registry.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_target_registry_clear", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_target_registry_clear();
/// <summary>
/// Get the number of registered targets.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_target_registry_len", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_target_registry_len();
/// <summary>
/// Check if the target registry is empty.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_target_registry_is_empty", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_target_registry_is_empty();
#endregion
#region Resource Schema Registry Methods
/// <summary>
/// Register a resource schema from JSON with a given name.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_resource_schema_register", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_resource_schema_register(byte* name, byte* schema_json);
/// <summary>
/// Check if a resource schema with the given name exists.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_resource_schema_contains", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_resource_schema_contains(byte* name);
/// <summary>
/// Get the number of registered resource schemas.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_resource_schema_len", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_resource_schema_len();
/// <summary>
/// Check if the resource schema registry is empty.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_resource_schema_is_empty", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_resource_schema_is_empty();
/// <summary>
/// List all registered resource schema names as a JSON array.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_resource_schema_list_names", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_resource_schema_list_names();
/// <summary>
/// Remove a resource schema by name.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_resource_schema_remove", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_resource_schema_remove(byte* name);
/// <summary>
/// Clear all resource schemas from the registry.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_resource_schema_clear", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_resource_schema_clear();
#endregion
#region Effect Schema Registry Methods
/// <summary>
/// Register an effect schema from JSON with a given name.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_effect_schema_register", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_effect_schema_register(byte* name, byte* schema_json);
/// <summary>
/// Check if an effect schema with the given name exists.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_effect_schema_contains", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_effect_schema_contains(byte* name);
/// <summary>
/// Get the number of registered effect schemas.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_effect_schema_len", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_effect_schema_len();
/// <summary>
/// Check if the effect schema registry is empty.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_effect_schema_is_empty", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_effect_schema_is_empty();
/// <summary>
/// List all registered effect schema names as a JSON array.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_effect_schema_list_names", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_effect_schema_list_names();
/// <summary>
/// Remove an effect schema by name.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_effect_schema_remove", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_effect_schema_remove(byte* name);
/// <summary>
/// Clear all effect schemas from the registry.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_effect_schema_clear", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_effect_schema_clear();
#endregion
}
#region Native Structures
/// <summary>
/// Type of data contained in RegorusResult.
/// </summary>
internal enum RegorusDataType : uint
{
/// <summary>
/// No data / void.
/// </summary>
None,
/// <summary>
/// String data (output field is valid).
/// </summary>
String,
/// <summary>
/// Boolean data (bool_value field is valid).
/// </summary>
Boolean,
/// <summary>
/// Integer data (int_value field is valid).
/// </summary>
Integer,
/// <summary>
/// Pointer data (pointer_value field is valid).
/// </summary>
Pointer,
}
/// <summary>
/// Status of a call on RegorusEngine.
/// </summary>
internal enum RegorusStatus : uint
{
/// <summary>
/// The operation was successful.
/// </summary>
Ok,
/// <summary>
/// The operation was unsuccessful.
/// </summary>
Error,
/// <summary>
/// Invalid data format provided.
/// </summary>
InvalidDataFormat,
/// <summary>
/// Invalid entrypoint rule specified.
/// </summary>
InvalidEntrypoint,
/// <summary>
/// Compilation failed.
/// </summary>
CompilationFailed,
/// <summary>
/// Invalid argument provided.
/// </summary>
InvalidArgument,
/// <summary>
/// Invalid module ID.
/// </summary>
InvalidModuleId,
/// <summary>
/// Invalid policy content.
/// </summary>
InvalidPolicy,
}
/// <summary>
/// Result of a call on RegorusEngine.
/// Must be freed using regorus_result_drop.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal unsafe partial struct RegorusResult
{
/// <summary>
/// Status.
/// </summary>
public RegorusStatus status;
/// <summary>
/// Type of data contained in this result.
/// </summary>
public RegorusDataType data_type;
/// <summary>
/// String output produced by the call.
/// Valid when data_type is String. Owned by Rust.
/// </summary>
public byte* output;
/// <summary>
/// Boolean value.
/// Valid when data_type is Boolean.
/// </summary>
public bool bool_value;
/// <summary>
/// Integer value.
/// Valid when data_type is Integer.
/// </summary>
public long int_value;
/// <summary>
/// Pointer value.
/// Valid when data_type is Pointer.
/// </summary>
public void* pointer_value;
/// <summary>
/// Errors produced by the call.
/// Owned by Rust.
/// </summary>
public byte* error_message;
}
/// <summary>
/// Wrapper for regorus::Engine.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal unsafe partial struct RegorusEngine
{
}
/// <summary>
/// Wrapper for regorus::CompiledPolicy.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal unsafe partial struct RegorusCompiledPolicy
{
}
/// <summary>
/// FFI wrapper for PolicyModule struct.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal unsafe partial struct RegorusPolicyModule
{
public byte* id;
public byte* content;
}
#endregion
}

View File

@@ -0,0 +1,141 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Collections.Generic;
using System.Text.Json.Serialization;
#nullable enable
namespace Regorus
{
/// <summary>
/// Information about a compiled policy, including metadata about modules,
/// target configuration, and resource types that the policy can evaluate.
/// </summary>
public class PolicyInfo
{
/// <summary>
/// List of module identifiers that were compiled into this policy.
/// Each module ID represents a unique policy module that contributes
/// rules, functions, or data to the compiled policy.
/// </summary>
[JsonPropertyName("module_ids")]
public List<string> ModuleIds { get; set; } = new List<string>();
/// <summary>
/// Name of the target configuration used during compilation, if any.
/// This indicates which target schema and validation rules were applied.
/// </summary>
[JsonPropertyName("target_name")]
public string? TargetName { get; set; }
/// <summary>
/// List of resource types that this policy can evaluate.
/// For target-aware policies, this contains the inferred or configured
/// resource types. For general policies, this may be empty.
/// </summary>
[JsonPropertyName("applicable_resource_types")]
public List<string> ApplicableResourceTypes { get; set; } = new List<string>();
/// <summary>
/// The primary rule or entrypoint that this policy evaluates.
/// This is the rule path that will be executed when the policy runs.
/// </summary>
[JsonPropertyName("entrypoint_rule")]
public string EntrypointRule { get; set; } = string.Empty;
/// <summary>
/// The effect rule name for target-aware policies, if applicable.
/// This is the specific effect rule (e.g., "effect", "allow", "deny")
/// that determines the policy decision for target evaluation.
/// </summary>
[JsonPropertyName("effect_rule")]
public string? EffectRule { get; set; }
/// <summary>
/// Parameters that can be configured for this policy.
/// Contains parameter names and their expected types or default values.
/// Used for parameterized policies that accept configuration at evaluation time.
/// Each element represents parameters from a different module.
/// </summary>
[JsonPropertyName("parameters")]
public List<PolicyParameters> Parameters { get; set; } = new List<PolicyParameters>();
}
/// <summary>
/// Parameters that can be configured for a policy.
/// </summary>
public class PolicyParameters
{
/// <summary>
/// Source file where the parameters are defined.
/// </summary>
[JsonPropertyName("source_file")]
public string SourceFile { get; set; } = string.Empty;
/// <summary>
/// List of parameter definitions.
/// </summary>
[JsonPropertyName("parameters")]
public List<PolicyParameter> Parameters { get; set; } = new List<PolicyParameter>();
/// <summary>
/// List of parameter modifiers.
/// </summary>
[JsonPropertyName("modifiers")]
public List<PolicyParameterModifier> Modifiers { get; set; } = new List<PolicyParameterModifier>();
}
/// <summary>
/// A single parameter definition.
/// </summary>
public class PolicyParameter
{
/// <summary>
/// Name of the parameter.
/// </summary>
[JsonPropertyName("name")]
public string Name { get; set; } = string.Empty;
/// <summary>
/// Type of the parameter.
/// </summary>
[JsonPropertyName("type")]
public string Type { get; set; } = string.Empty;
/// <summary>
/// Default value of the parameter, if any.
/// </summary>
[JsonPropertyName("default")]
public object? Default { get; set; }
/// <summary>
/// Description of the parameter.
/// </summary>
[JsonPropertyName("description")]
public string? Description { get; set; }
/// <summary>
/// Allowed values for the parameter, if constrained.
/// </summary>
[JsonPropertyName("allowed_values")]
public List<object>? AllowedValues { get; set; }
}
/// <summary>
/// A parameter modifier that affects parameter behavior.
/// </summary>
public class PolicyParameterModifier
{
/// <summary>
/// Name of the modifier.
/// </summary>
[JsonPropertyName("name")]
public string Name { get; set; } = string.Empty;
/// <summary>
/// Value of the modifier.
/// </summary>
[JsonPropertyName("value")]
public object? Value { get; set; }
}
}

View File

@@ -13,6 +13,10 @@
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="8.0.0" />
</ItemGroup>
<!--
$(RegorusFFIArtifactsDir) is the location where regorus shared libraries have been
built for various platforms and copied to. RegorusFFIArtifactsDir is passed in
@@ -20,8 +24,10 @@
For each target triple, `Pack` expects the regorus ffi shared library
to be found in $(RegorusFFIArtifactsDir)/<target-triple>/release.
If $(IgnoreMissingArtifacts) is not set, ensure that the binaries for officially supported platforms exists.
-->
<Target Name="ChecksRegorusFFIArtifactsDir" BeforeTargets="Pack">
<Target Name="ChecksRegorusFFIArtifactsDir" BeforeTargets="Pack" Condition="'$(IgnoreMissingArtifacts)' == ''">
<Error Text="RegorusFFIArtifactsDir must be supplied." Condition="$(RegorusFFIArtifactsDir) == ''" />
<!-- Ensure that the binaries for officially supported platforms exists. -->

View File

@@ -1,244 +0,0 @@
// <auto-generated>
// This code is generated by csbindgen.
// DON'T CHANGE THIS DIRECTLY.
// </auto-generated>
#pragma warning disable CS8500
#pragma warning disable CS8981
using System;
using System.Runtime.InteropServices;
namespace Regorus.Internal
{
internal static unsafe partial class API
{
const string __DllName = "regorus_ffi";
/// <summary>
/// Drop a `RegorusResult`.
///
/// `output` and `error_message` strings are not valid after drop.
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_result_drop", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern void regorus_result_drop(RegorusResult r);
/// <summary>
/// Construct a new Engine
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusEngine* regorus_engine_new();
/// <summary>
/// Clone a [`RegorusEngine`]
///
/// To avoid having to parse same policy again, the engine can be cloned
/// after policies and data have been added.
///
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusEngine* regorus_engine_clone(RegorusEngine* engine);
[DllImport(__DllName, EntryPoint = "regorus_engine_drop", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern void regorus_engine_drop(RegorusEngine* engine);
/// <summary>
/// Add a policy
///
/// The policy is parsed into AST.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.add_policy
///
/// * `path`: A filename to be associated with the policy.
/// * `rego`: Rego policy.
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_add_policy", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_add_policy(RegorusEngine* engine, byte* path, byte* rego);
[DllImport(__DllName, EntryPoint = "regorus_engine_add_policy_from_file", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_add_policy_from_file(RegorusEngine* engine, byte* path);
/// <summary>
/// Add policy data.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.add_data
/// * `data`: JSON encoded value to be used as policy data.
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_add_data_json", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_add_data_json(RegorusEngine* engine, byte* data);
/// <summary>
/// Get list of loaded Rego packages as JSON.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_packages
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_get_packages", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_get_packages(RegorusEngine* engine);
/// <summary>
/// Get list of policies as JSON.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_policies
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_get_policies", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_get_policies(RegorusEngine* engine);
[DllImport(__DllName, EntryPoint = "regorus_engine_add_data_from_json_file", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_add_data_from_json_file(RegorusEngine* engine, byte* path);
/// <summary>
/// Clear policy data.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.clear_data
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_clear_data", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_clear_data(RegorusEngine* engine);
/// <summary>
/// Set input.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_input
/// * `input`: JSON encoded value to be used as input to query.
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_set_input_json", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_set_input_json(RegorusEngine* engine, byte* input);
[DllImport(__DllName, EntryPoint = "regorus_engine_set_input_from_json_file", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_set_input_from_json_file(RegorusEngine* engine, byte* path);
/// <summary>
/// Evaluate query.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.eval_query
/// * `query`: Rego expression to be evaluate.
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_eval_query", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_eval_query(RegorusEngine* engine, byte* query);
/// <summary>
/// Evaluate specified rule.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.eval_rule
/// * `rule`: Path to the rule.
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_eval_rule", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_eval_rule(RegorusEngine* engine, byte* rule);
/// <summary>
/// Enable/disable coverage.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_enable_coverage
/// * `enable`: Whether to enable or disable coverage.
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_set_enable_coverage", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_set_enable_coverage(RegorusEngine* engine, [MarshalAs(UnmanagedType.U1)] bool enable);
/// <summary>
/// Get coverage report.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_coverage_report
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_get_coverage_report", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_get_coverage_report(RegorusEngine* engine);
/// <summary>
/// Enable/disable strict builtin errors.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_strict_builtin_errors
/// * `strict`: Whether to raise errors or return undefined on certain scenarios.
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_set_strict_builtin_errors", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_set_strict_builtin_errors(RegorusEngine* engine, [MarshalAs(UnmanagedType.U1)] bool strict);
/// <summary>
/// Get pretty printed coverage report.
///
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Report.html#method.to_string_pretty
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_get_coverage_report_pretty", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_get_coverage_report_pretty(RegorusEngine* engine);
/// <summary>
/// Clear coverage data.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.clear_coverage_data
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_clear_coverage_data", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_clear_coverage_data(RegorusEngine* engine);
/// <summary>
/// Whether to gather output of print statements.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_gather_prints
/// * `enable`: Whether to enable or disable gathering print statements.
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_set_gather_prints", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_set_gather_prints(RegorusEngine* engine, [MarshalAs(UnmanagedType.U1)] bool enable);
/// <summary>
/// Take all the gathered print statements.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.take_prints
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_take_prints", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_take_prints(RegorusEngine* engine);
/// <summary>
/// Get AST of policies.
///
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_ast_as_json
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_get_ast_as_json", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_get_ast_as_json(RegorusEngine* engine);
/// <summary>
/// Gets the package names of policies added to the engine.
///
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_policy_package_names
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_get_policy_package_names", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_get_policy_package_names(RegorusEngine* engine);
/// <summary>
/// Gets the parameters defined in each policy added to the engine
///
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_policy_parameters
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_get_policy_parameters", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_get_policy_parameters(RegorusEngine* engine);
/// <summary>
/// Enable/disable rego v1.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_rego_v0
/// </summary>
[DllImport(__DllName, EntryPoint = "regorus_engine_set_rego_v0", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_engine_set_rego_v0(RegorusEngine* engine, [MarshalAs(UnmanagedType.U1)] bool enable);
}
[StructLayout(LayoutKind.Sequential)]
internal unsafe partial struct RegorusResult
{
public RegorusStatus status;
public byte* output;
public byte* error_message;
}
[StructLayout(LayoutKind.Sequential)]
internal unsafe partial struct RegorusEngine
{
}
internal enum RegorusStatus : uint
{
RegorusStatusOk,
RegorusStatusError,
}
}

View File

@@ -0,0 +1,284 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Text;
#nullable enable
namespace Regorus
{
/// <summary>
/// Provides static methods for managing the global resource schema registry.
/// Resource schemas define the structure and validation rules for Azure Policy resources.
/// </summary>
public static unsafe class SchemaRegistry
{
/// <summary>
/// Register a resource schema from JSON with a given name.
/// </summary>
/// <param name="name">Name to register the schema under</param>
/// <param name="schemaJson">JSON string representing the schema</param>
/// <exception cref="Exception">Thrown when schema registration fails</exception>
public static void RegisterResource(string name, string schemaJson)
{
var nameBytes = Encoding.UTF8.GetBytes(name + char.MinValue);
var schemaBytes = Encoding.UTF8.GetBytes(schemaJson + char.MinValue);
fixed (byte* namePtr = nameBytes)
fixed (byte* schemaPtr = schemaBytes)
{
CheckAndDropResult(Internal.API.regorus_resource_schema_register(namePtr, schemaPtr));
}
}
/// <summary>
/// Check if a resource schema with the given name exists.
/// </summary>
/// <param name="name">Name of the schema to check</param>
/// <returns>True if the schema exists, false otherwise</returns>
/// <exception cref="Exception">Thrown when the operation fails</exception>
public static bool ContainsResource(string name)
{
var nameBytes = Encoding.UTF8.GetBytes(name + char.MinValue);
fixed (byte* namePtr = nameBytes)
{
var result = Internal.API.regorus_resource_schema_contains(namePtr);
return GetBoolResult(result);
}
}
/// <summary>
/// Get the number of registered resource schemas.
/// </summary>
/// <returns>The number of registered resource schemas</returns>
/// <exception cref="Exception">Thrown when the operation fails</exception>
public static long ResourceCount
{
get
{
var result = Internal.API.regorus_resource_schema_len();
return GetIntResult(result);
}
}
/// <summary>
/// Check if the resource schema registry is empty.
/// </summary>
/// <returns>True if the registry is empty, false otherwise</returns>
/// <exception cref="Exception">Thrown when the operation fails</exception>
public static bool IsResourceRegistryEmpty
{
get
{
var result = Internal.API.regorus_resource_schema_is_empty();
return GetBoolResult(result);
}
}
/// <summary>
/// List all registered resource schema names.
/// </summary>
/// <returns>JSON array of schema names</returns>
/// <exception cref="Exception">Thrown when the operation fails</exception>
public static string ListResourceNames()
{
return CheckAndDropResult(Internal.API.regorus_resource_schema_list_names()) ?? "[]";
}
/// <summary>
/// Remove a resource schema by name.
/// </summary>
/// <param name="name">Name of the schema to remove</param>
/// <returns>True if the schema was removed, false if it wasn't found</returns>
/// <exception cref="Exception">Thrown when the operation fails</exception>
public static bool RemoveResource(string name)
{
var nameBytes = Encoding.UTF8.GetBytes(name + char.MinValue);
fixed (byte* namePtr = nameBytes)
{
var result = Internal.API.regorus_resource_schema_remove(namePtr);
return GetBoolResult(result);
}
}
/// <summary>
/// Clear all resource schemas from the registry.
/// </summary>
/// <exception cref="Exception">Thrown when the operation fails</exception>
public static void ClearResources()
{
CheckAndDropResult(Internal.API.regorus_resource_schema_clear());
}
/// <summary>
/// Register an effect schema from JSON with a given name.
/// </summary>
/// <param name="name">Name to register the schema under</param>
/// <param name="schemaJson">JSON string representing the schema</param>
/// <exception cref="Exception">Thrown when schema registration fails</exception>
public static void RegisterEffect(string name, string schemaJson)
{
var nameBytes = Encoding.UTF8.GetBytes(name + char.MinValue);
var schemaBytes = Encoding.UTF8.GetBytes(schemaJson + char.MinValue);
fixed (byte* namePtr = nameBytes)
fixed (byte* schemaPtr = schemaBytes)
{
CheckAndDropResult(Internal.API.regorus_effect_schema_register(namePtr, schemaPtr));
}
}
/// <summary>
/// Check if an effect schema with the given name exists.
/// </summary>
/// <param name="name">Name of the schema to check</param>
/// <returns>True if the schema exists, false otherwise</returns>
/// <exception cref="Exception">Thrown when the operation fails</exception>
public static bool ContainsEffect(string name)
{
var nameBytes = Encoding.UTF8.GetBytes(name + char.MinValue);
fixed (byte* namePtr = nameBytes)
{
var result = Internal.API.regorus_effect_schema_contains(namePtr);
return GetBoolResult(result);
}
}
/// <summary>
/// Get the number of registered effect schemas.
/// </summary>
/// <returns>The number of registered effect schemas</returns>
/// <exception cref="Exception">Thrown when the operation fails</exception>
public static long EffectCount
{
get
{
var result = Internal.API.regorus_effect_schema_len();
return GetIntResult(result);
}
}
/// <summary>
/// Check if the effect schema registry is empty.
/// </summary>
/// <returns>True if the registry is empty, false otherwise</returns>
/// <exception cref="Exception">Thrown when the operation fails</exception>
public static bool IsEffectRegistryEmpty
{
get
{
var result = Internal.API.regorus_effect_schema_is_empty();
return GetBoolResult(result);
}
}
/// <summary>
/// List all registered effect schema names.
/// </summary>
/// <returns>JSON array of schema names</returns>
/// <exception cref="Exception">Thrown when the operation fails</exception>
public static string ListEffectNames()
{
return CheckAndDropResult(Internal.API.regorus_effect_schema_list_names()) ?? "[]";
}
/// <summary>
/// Remove an effect schema by name.
/// </summary>
/// <param name="name">Name of the schema to remove</param>
/// <returns>True if the schema was removed, false if it wasn't found</returns>
/// <exception cref="Exception">Thrown when the operation fails</exception>
public static bool RemoveEffect(string name)
{
var nameBytes = Encoding.UTF8.GetBytes(name + char.MinValue);
fixed (byte* namePtr = nameBytes)
{
var result = Internal.API.regorus_effect_schema_remove(namePtr);
return GetBoolResult(result);
}
}
/// <summary>
/// Clear all effect schemas from the registry.
/// </summary>
/// <exception cref="Exception">Thrown when the operation fails</exception>
public static void ClearEffects()
{
CheckAndDropResult(Internal.API.regorus_effect_schema_clear());
}
private static string? StringFromUTF8(IntPtr ptr)
{
#if NETSTANDARD2_1
return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr);
#else
int len = 0;
while (System.Runtime.InteropServices.Marshal.ReadByte(ptr, len) != 0) { ++len; }
byte[] buffer = new byte[len];
System.Runtime.InteropServices.Marshal.Copy(ptr, buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer);
#endif
}
private static string? CheckAndDropResult(Internal.RegorusResult result)
{
try
{
if (result.status != Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
throw new Exception(message ?? "Unknown error occurred");
}
return result.data_type switch
{
Internal.RegorusDataType.String => StringFromUTF8((IntPtr)result.output),
Internal.RegorusDataType.Boolean => result.bool_value.ToString().ToLowerInvariant(),
Internal.RegorusDataType.Integer => result.int_value.ToString(),
Internal.RegorusDataType.None => null,
_ => StringFromUTF8((IntPtr)result.output)
};
}
finally
{
Internal.API.regorus_result_drop(result);
}
}
private static bool GetBoolResult(Internal.RegorusResult result)
{
try
{
if (result.status != Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
throw new Exception(message ?? "Unknown error occurred");
}
return result.data_type == Internal.RegorusDataType.Boolean ? result.bool_value : false;
}
finally
{
Internal.API.regorus_result_drop(result);
}
}
private static long GetIntResult(Internal.RegorusResult result)
{
try
{
if (result.status != Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
throw new Exception(message ?? "Unknown error occurred");
}
return result.data_type == Internal.RegorusDataType.Integer ? result.int_value : 0;
}
finally
{
Internal.API.regorus_result_drop(result);
}
}
}
}

View File

@@ -0,0 +1,185 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Text;
#nullable enable
namespace Regorus
{
/// <summary>
/// Provides static methods for managing the global target registry.
/// Targets define resource types and their associated schemas for Azure Policy evaluation.
/// </summary>
public static unsafe class TargetRegistry
{
/// <summary>
/// Register a target from JSON definition.
/// The target JSON should follow the target schema format.
/// Once registered, the target can be referenced in Rego policies using `__target__` rules.
/// </summary>
/// <param name="targetJson">JSON encoded target definition</param>
/// <exception cref="Exception">Thrown when target registration fails</exception>
public static void RegisterFromJson(string targetJson)
{
var targetBytes = Encoding.UTF8.GetBytes(targetJson + char.MinValue);
fixed (byte* targetPtr = targetBytes)
{
CheckAndDropResult(Internal.API.regorus_register_target_from_json(targetPtr));
}
}
/// <summary>
/// Check if a target is registered.
/// </summary>
/// <param name="name">Name of the target to check</param>
/// <returns>True if the target is registered, false otherwise</returns>
/// <exception cref="Exception">Thrown when the operation fails</exception>
public static bool Contains(string name)
{
var nameBytes = Encoding.UTF8.GetBytes(name + char.MinValue);
fixed (byte* namePtr = nameBytes)
{
var result = Internal.API.regorus_target_registry_contains(namePtr);
return GetBoolResult(result);
}
}
/// <summary>
/// Get a list of all registered target names.
/// </summary>
/// <returns>JSON array of target names</returns>
/// <exception cref="Exception">Thrown when the operation fails</exception>
public static string ListNames()
{
return CheckAndDropResult(Internal.API.regorus_target_registry_list_names()) ?? "[]";
}
/// <summary>
/// Remove a target from the registry by name.
/// </summary>
/// <param name="name">The target name to remove</param>
/// <returns>True if the target was removed, false if it wasn't found</returns>
/// <exception cref="Exception">Thrown when the operation fails</exception>
public static bool Remove(string name)
{
var nameBytes = Encoding.UTF8.GetBytes(name + char.MinValue);
fixed (byte* namePtr = nameBytes)
{
var result = Internal.API.regorus_target_registry_remove(namePtr);
return GetBoolResult(result);
}
}
/// <summary>
/// Clear all targets from the registry.
/// </summary>
/// <exception cref="Exception">Thrown when the operation fails</exception>
public static void Clear()
{
CheckAndDropResult(Internal.API.regorus_target_registry_clear());
}
/// <summary>
/// Get the number of registered targets.
/// </summary>
/// <returns>The number of registered targets</returns>
/// <exception cref="Exception">Thrown when the operation fails</exception>
public static long Count
{
get
{
var result = Internal.API.regorus_target_registry_len();
return GetIntResult(result);
}
}
/// <summary>
/// Check if the target registry is empty.
/// </summary>
/// <returns>True if the registry is empty, false otherwise</returns>
/// <exception cref="Exception">Thrown when the operation fails</exception>
public static bool IsEmpty
{
get
{
var result = Internal.API.regorus_target_registry_is_empty();
return GetBoolResult(result);
}
}
private static string? StringFromUTF8(IntPtr ptr)
{
#if NETSTANDARD2_1
return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr);
#else
int len = 0;
while (System.Runtime.InteropServices.Marshal.ReadByte(ptr, len) != 0) { ++len; }
byte[] buffer = new byte[len];
System.Runtime.InteropServices.Marshal.Copy(ptr, buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer);
#endif
}
private static string? CheckAndDropResult(Internal.RegorusResult result)
{
try
{
if (result.status != Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
throw new Exception(message ?? "Unknown error occurred");
}
return result.data_type switch
{
Internal.RegorusDataType.String => StringFromUTF8((IntPtr)result.output),
Internal.RegorusDataType.Boolean => result.bool_value.ToString().ToLowerInvariant(),
Internal.RegorusDataType.Integer => result.int_value.ToString(),
Internal.RegorusDataType.None => null,
_ => StringFromUTF8((IntPtr)result.output)
};
}
finally
{
Internal.API.regorus_result_drop(result);
}
}
private static bool GetBoolResult(Internal.RegorusResult result)
{
try
{
if (result.status != Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
throw new Exception(message ?? "Unknown error occurred");
}
return result.data_type == Internal.RegorusDataType.Boolean ? result.bool_value : false;
}
finally
{
Internal.API.regorus_result_drop(result);
}
}
private static long GetIntResult(Internal.RegorusResult result)
{
try
{
if (result.status != Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
throw new Exception(message ?? "Unknown error occurred");
}
return result.data_type == Internal.RegorusDataType.Integer ? result.int_value : 0;
}
finally
{
Internal.API.regorus_result_drop(result);
}
}
}
}

View File

@@ -0,0 +1,291 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Text.Json;
namespace TargetExampleApp;
class Program
{
// Policy definition constants
private const string AZURE_STORAGE_POLICY_DEFINITION = @"
package policy
import rego.v1
# Target declaration for Azure Policy
__target__ := ""target.tests.azure_policy""
default parameters.requiredTLSVersion = """"
default parameters.allowedPorts = []
# Policy rules for storage accounts
default allow := false
# Allow storage accounts with HTTPS-only traffic and proper encryption
allow if {
input.type == ""Microsoft.Storage/storageAccounts""
input.properties.supportsHttpsTrafficOnly == true
input.properties.encryption.services.blob.enabled == true
input.properties.minimumTlsVersion in [parameters.requiredTLSVersion]
}
# 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
}";
private const string AZURE_STORAGE_POLICY_ASSIGNMENT = @"
package policy
import rego.v1
parameters.requiredTLSVersion = ""TLS1_2""
parameters.allowedPorts = [""22"", ""3389""]";
// 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""
}
}";
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 }
}
}
}
}";
static void Main(string[] args)
{
Console.WriteLine("=== Regorus Target Example Application ===\n");
try
{
DemonstrateTargetFunctionality();
Console.WriteLine("\n=== Target demonstration completed successfully! ===");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
Environment.Exit(1);
}
}
static void DemonstrateTargetFunctionality()
{
Console.WriteLine("REGORUS TARGET FUNCTIONALITY DEMONSTRATION");
Console.WriteLine("==========================================");
// 1. Register target using JSON from file
var targetJsonPath = Path.Combine(AppContext.BaseDirectory, "azure_policy.target.json");
var targetJson = File.ReadAllText(targetJsonPath);
Console.WriteLine("1. Registering target from JSON file:");
Console.WriteLine(targetJson);
Regorus.TargetRegistry.RegisterFromJson(targetJson);
Console.WriteLine($"Target registered. Registry contains {Regorus.TargetRegistry.Count} target(s)");
Console.WriteLine($"Registered targets: {Regorus.TargetRegistry.ListNames()}");
// 2. Compile policy for target
var policyModules = new List<Regorus.PolicyModule>
{
new Regorus.PolicyModule($"definition-{Guid.NewGuid():N}", AZURE_STORAGE_POLICY_DEFINITION),
new Regorus.PolicyModule($"assignment-{Guid.NewGuid():N}", AZURE_STORAGE_POLICY_ASSIGNMENT)
};
var policyDataJson = "{}";
Console.WriteLine("\n2. Compiling policy for target...");
using var compiledPolicy = Regorus.Compiler.CompilePolicyForTarget(policyDataJson, policyModules);
Console.WriteLine("Policy compiled successfully!");
// 2.5. Demonstrate policy information retrieval
Console.WriteLine("\n2.5. Retrieving policy information:");
DemonstratePolicyInfo(compiledPolicy);
// 3. Evaluate with different inputs
Console.WriteLine("\n3. Testing policy evaluation:");
Console.WriteLine("Compliant storage account:");
Console.WriteLine(COMPLIANT_STORAGE_ACCOUNT);
var compliantResult = compiledPolicy.EvalWithInput(COMPLIANT_STORAGE_ACCOUNT);
Console.WriteLine($"Result: {compliantResult}");
Console.WriteLine("\nNon-compliant storage account:");
Console.WriteLine(NON_COMPLIANT_STORAGE_ACCOUNT);
var nonCompliantResult = compiledPolicy.EvalWithInput(NON_COMPLIANT_STORAGE_ACCOUNT);
Console.WriteLine($"Result: {nonCompliantResult}");
// 4. Demonstrate thread-safe concurrent evaluation
Console.WriteLine("\n4. Testing concurrent evaluation from multiple threads:");
DemonstrateConcurrentEvaluation(compiledPolicy);
}
static void DemonstrateConcurrentEvaluation(Regorus.CompiledPolicy compiledPolicy)
{
var testInputs = new[]
{
("Thread-1-Compliant", COMPLIANT_STORAGE_ACCOUNT),
("Thread-2-NonCompliant", NON_COMPLIANT_STORAGE_ACCOUNT),
("Thread-3-Compliant", COMPLIANT_STORAGE_ACCOUNT.Replace("compliantstorageacct", "thread3storage")),
("Thread-4-NonCompliant", NON_COMPLIANT_STORAGE_ACCOUNT.Replace("insecurestorageacct", "thread4storage")),
("Thread-5-Compliant", COMPLIANT_STORAGE_ACCOUNT.Replace("compliantstorageacct", "thread5storage"))
};
Console.WriteLine($"Starting {testInputs.Length} concurrent evaluations...");
var tasks = testInputs.Select(input =>
Task.Run(() => {
var (threadName, json) = input;
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
// Multiple evaluations per thread to stress test
var results = new List<string>();
for (int i = 0; i < 1000; i++)
{
var result = compiledPolicy.EvalWithInput(json);
results.Add(result);
}
stopwatch.Stop();
var microseconds = stopwatch.ElapsedTicks * 1000000 / System.Diagnostics.Stopwatch.Frequency;
// Verify all results are identical (thread safety)
var firstResult = results[0];
var allIdentical = results.All(r => r == firstResult);
Console.WriteLine($"✓ {threadName}: {results.Count} evaluations in {microseconds}μs, " +
$"Results consistent: {allIdentical}");
return (threadName, results.Count, microseconds, allIdentical);
})
).ToArray();
// Wait for all threads to complete
var results = Task.WhenAll(tasks).Result;
Console.WriteLine("\nConcurrency test results:");
var totalEvaluations = results.Sum(r => r.Item2);
var maxTime = results.Max(r => r.Item3);
var allConsistent = results.All(r => r.allIdentical);
Console.WriteLine($"✓ Total evaluations: {totalEvaluations}");
Console.WriteLine($"✓ Max thread time: {maxTime}μs");
Console.WriteLine($"✓ All threads consistent: {allConsistent}");
Console.WriteLine($"✓ Approximate throughput: {totalEvaluations * 1000000.0 / maxTime:F0} evaluations/second");
Console.WriteLine("✓ No locks required - CompiledPolicy is thread-safe!");
}
static void DemonstratePolicyInfo(Regorus.CompiledPolicy compiledPolicy)
{
Console.WriteLine("Getting policy metadata using GetPolicyInfo()...");
try
{
var policyInfo = compiledPolicy.GetPolicyInfo();
Console.WriteLine($"✓ Policy Information Retrieved:");
Console.WriteLine($" Target Name: {policyInfo.TargetName ?? "None"}");
Console.WriteLine($" Effect Rule: {policyInfo.EffectRule ?? "None"}");
Console.WriteLine($" Entrypoint Rule: {policyInfo.EntrypointRule}");
Console.WriteLine($" Module IDs ({policyInfo.ModuleIds.Count}):");
foreach (var moduleId in policyInfo.ModuleIds)
{
Console.WriteLine($" - {moduleId}");
}
Console.WriteLine($" Applicable Resource Types ({policyInfo.ApplicableResourceTypes.Count}):");
foreach (var resourceType in policyInfo.ApplicableResourceTypes)
{
Console.WriteLine($" - {resourceType}");
}
if (policyInfo.Parameters != null && policyInfo.Parameters.Count > 0)
{
Console.WriteLine($" Policy Parameters:");
foreach (var parameterSet in policyInfo.Parameters)
{
Console.WriteLine($" From '{parameterSet.SourceFile}':");
Console.WriteLine($" Parameters ({parameterSet.Parameters.Count}):");
foreach (var param in parameterSet.Parameters)
{
Console.WriteLine($" - {param.Name} ({param.Type})");
if (param.Default != null)
{
Console.WriteLine($" Default: {param.Default}");
}
if (!string.IsNullOrEmpty(param.Description))
{
Console.WriteLine($" Description: {param.Description}");
}
}
if (parameterSet.Modifiers.Count > 0)
{
Console.WriteLine($" Modifiers ({parameterSet.Modifiers.Count}):");
foreach (var modifier in parameterSet.Modifiers)
{
Console.WriteLine($" - {modifier.Name}: {modifier.Value}");
}
}
}
}
else
{
Console.WriteLine(" No parameter information available");
}
// Demonstrate JSON serialization of policy info
Console.WriteLine("\n✓ Policy Info as JSON:");
var jsonOptions = new JsonSerializerOptions
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
var policyInfoJson = JsonSerializer.Serialize(policyInfo, jsonOptions);
Console.WriteLine(policyInfoJson);
}
catch (Exception ex)
{
Console.WriteLine($"✗ Failed to get policy info: {ex.Message}");
}
}
}

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<!-- If the environment variable is set (such as in a Github Action run), append the suffix to the version number -->
<RegorusPackageVersionSuffix Condition="'$(VersionSuffix)' != ''">-$(VersionSuffix)</RegorusPackageVersionSuffix>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Regorus" Version="0.6.0$(RegorusPackageVersionSuffix)"/>
</ItemGroup>
<ItemGroup>
<Content Include="azure_policy.target.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,125 @@
{
"name": "target.tests.azure_policy",
"description": "Azure Policy target for comprehensive policy evaluation testing",
"version": "1.0.0",
"resource_schema_selector": "type",
"resource_schemas": [
{
"type": "object",
"properties": {
"type": { "const": "Microsoft.Resources/subscriptions" },
"subscriptionId": { "type": "string" },
"tenantId": { "type": "string" },
"displayName": { "type": "string" }
},
"required": ["type", "subscriptionId"]
},
{
"type": "object",
"properties": {
"type": { "const": "Microsoft.Storage/storageAccounts" },
"name": { "type": "string" },
"location": { "type": "string" },
"kind": { "enum": ["Storage", "StorageV2", "BlobStorage", "FileStorage", "BlockBlobStorage"] },
"properties": {
"type": "object",
"properties": {
"supportsHttpsTrafficOnly": { "type": "boolean" },
"minimumTlsVersion": { "enum": ["TLS1_0", "TLS1_1", "TLS1_2"] },
"allowBlobPublicAccess": { "type": "boolean" },
"encryption": {
"type": "object",
"properties": {
"services": {
"type": "object",
"properties": {
"blob": { "type": "object", "properties": { "enabled": { "type": "boolean" } } },
"file": { "type": "object", "properties": { "enabled": { "type": "boolean" } } }
}
}
}
}
}
},
"tags": { "type": "object" }
},
"required": ["type", "name", "location"]
},
{
"type": "object",
"properties": {
"type": { "const": "Microsoft.Network/networkSecurityGroups" },
"name": { "type": "string" },
"location": { "type": "string" },
"properties": {
"type": "object",
"properties": {
"securityRules": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"properties": {
"type": "object",
"properties": {
"direction": { "enum": ["Inbound", "Outbound"] },
"access": { "enum": ["Allow", "Deny"] },
"protocol": { "enum": ["Tcp", "Udp", "*"] },
"sourcePortRange": { "type": "string" },
"destinationPortRange": { "type": "string" },
"sourceAddressPrefix": { "type": "string" },
"destinationAddressPrefix": { "type": "string" },
"priority": { "type": "integer", "minimum": 100, "maximum": 4096 }
}
}
}
}
}
}
}
},
"required": ["type", "name", "location"]
}
],
"effects": {
"allow": { "type": "boolean" },
"deny": {
"type": "object",
"properties": {
"message": { "type": "string" }
}
},
"audit": {
"type": "object",
"properties": {
"level": { "enum": ["info", "warning", "error"] },
"message": { "type": "string" },
"complianceState": { "enum": ["Compliant", "NonCompliant", "Unknown"] }
}
},
"modify": {
"type": "object",
"properties": {
"operations": {
"type": "array",
"items": {
"type": "object",
"properties": {
"operation": { "enum": ["add", "replace", "remove"] },
"field": { "type": "string" },
"value": { "type": "any" }
}
}
}
}
},
"deployIfNotExists": {
"type": "object",
"properties": {
"template": { "type": "object" },
"parameters": { "type": "object" }
}
}
}
}

View File

@@ -5,6 +5,6 @@
"sdk": {
"allowPrerelease": false,
"version": "8.0.412",
"rollForward": "disable"
"rollForward": "latestFeature"
}
}