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
+31
View File
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#[cfg(feature = "custom_allocator")]
extern "C" {
fn regorus_aligned_alloc(alignment: usize, size: usize) -> *mut u8;
fn regorus_free(ptr: *mut u8);
}
#[cfg(feature = "custom_allocator")]
mod allocator {
use std::alloc::{GlobalAlloc, Layout};
struct RegorusAllocator {}
unsafe impl GlobalAlloc for RegorusAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let size = layout.size();
let align = layout.align();
crate::allocator::regorus_aligned_alloc(align, size)
}
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
crate::allocator::regorus_free(ptr)
}
}
#[global_allocator]
static ALLOCATOR: RegorusAllocator = RegorusAllocator {};
}
+229
View File
@@ -0,0 +1,229 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use anyhow::{anyhow, bail, Result};
use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_longlong};
/// Status of a call on `RegorusEngine`.
#[repr(C)]
pub enum RegorusStatus {
/// The operation was successful.
Ok,
/// The operation was unsuccessful.
Error,
/// Invalid data format provided.
InvalidDataFormat,
/// Invalid entrypoint rule specified.
InvalidEntrypoint,
/// Compilation failed.
CompilationFailed,
/// Invalid argument provided.
InvalidArgument,
/// Invalid module ID.
InvalidModuleId,
/// Invalid policy content.
InvalidPolicy,
}
/// Type of data contained in RegorusResult
#[repr(C)]
#[allow(unused)]
pub enum RegorusDataType {
/// No data / void
None,
/// String data (output field is valid)
String,
/// Boolean data (bool_value field is valid)
Boolean,
/// Integer data (int_value field is valid)
Integer,
/// Pointer data (pointer_value field is valid)
Pointer,
}
/// Result of a call on `RegorusEngine`.
///
/// Must be freed using `regorus_result_drop`.
#[repr(C)]
pub struct RegorusResult {
/// Status
pub(crate) status: RegorusStatus,
/// Type of data contained in this result
pub(crate) data_type: RegorusDataType,
/// String output produced by the call.
/// Valid when data_type is String. Owned by Rust.
pub(crate) output: *mut c_char,
/// Boolean value.
/// Valid when data_type is Boolean.
pub(crate) bool_value: bool,
/// Integer value.
/// Valid when data_type is Integer.
pub(crate) int_value: c_longlong,
/// Pointer value.
/// Valid when data_type is Pointer.
pub(crate) pointer_value: *mut std::os::raw::c_void,
/// Errors produced by the call.
/// Owned by Rust.
pub(crate) error_message: *mut c_char,
}
impl RegorusResult {
/// Create a successful result with no data.
pub(crate) fn ok_void() -> Self {
Self {
status: RegorusStatus::Ok,
data_type: RegorusDataType::None,
output: std::ptr::null_mut(),
bool_value: false,
int_value: 0,
pointer_value: std::ptr::null_mut(),
error_message: std::ptr::null_mut(),
}
}
/// Create a successful result with string output.
pub(crate) fn ok_string(output: String) -> Self {
Self {
status: RegorusStatus::Ok,
data_type: RegorusDataType::String,
output: to_c_str(output),
bool_value: false,
int_value: 0,
pointer_value: std::ptr::null_mut(),
error_message: std::ptr::null_mut(),
}
}
/// Create a successful result with boolean value.
#[allow(unused)]
pub(crate) fn ok_bool(value: bool) -> Self {
Self {
status: RegorusStatus::Ok,
data_type: RegorusDataType::Boolean,
output: std::ptr::null_mut(),
bool_value: value,
int_value: 0,
pointer_value: std::ptr::null_mut(),
error_message: std::ptr::null_mut(),
}
}
/// Create a successful result with integer value.
#[allow(unused)]
pub(crate) fn ok_int(value: i64) -> Self {
Self {
status: RegorusStatus::Ok,
data_type: RegorusDataType::Integer,
output: std::ptr::null_mut(),
bool_value: false,
int_value: value as c_longlong,
pointer_value: std::ptr::null_mut(),
error_message: std::ptr::null_mut(),
}
}
/// Create a successful result with pointer value.
pub(crate) fn ok_pointer(pointer: *mut std::os::raw::c_void) -> Self {
Self {
status: RegorusStatus::Ok,
data_type: RegorusDataType::Pointer,
output: std::ptr::null_mut(),
bool_value: false,
int_value: 0,
pointer_value: pointer,
error_message: std::ptr::null_mut(),
}
}
/// Create an error result with specific status.
pub(crate) fn err(status: RegorusStatus) -> Self {
Self {
status,
data_type: RegorusDataType::None,
output: std::ptr::null_mut(),
bool_value: false,
int_value: 0,
pointer_value: std::ptr::null_mut(),
error_message: std::ptr::null_mut(),
}
}
/// Create an error result with status and message.
pub(crate) fn err_with_message(status: RegorusStatus, message: String) -> Self {
Self {
status,
data_type: RegorusDataType::None,
output: std::ptr::null_mut(),
bool_value: false,
int_value: 0,
pointer_value: std::ptr::null_mut(),
error_message: to_c_str(message),
}
}
}
pub(crate) fn to_c_str(s: String) -> *mut c_char {
match CString::new(s) {
Ok(cs) => cs.into_raw(),
_ => to_c_str("binding error: failed to create c-style string".to_string()),
}
}
pub(crate) fn from_c_str(s: *const c_char) -> Result<String> {
if s.is_null() {
bail!("null pointer");
}
unsafe {
CStr::from_ptr(s)
.to_str()
.map_err(|e| anyhow!("invalid utf8: {e}"))
.map(|s| s.to_string())
}
}
pub(crate) fn to_ref<'a, T>(t: *mut T) -> Result<&'a mut T> {
unsafe { t.as_mut().ok_or_else(|| anyhow!("null pointer")) }
}
pub(crate) fn to_regorus_result(r: Result<()>) -> RegorusResult {
match r {
Ok(()) => RegorusResult::ok_void(),
Err(e) => RegorusResult::err_with_message(RegorusStatus::Error, format!("{e}")),
}
}
pub(crate) fn to_regorus_string_result(r: Result<String>) -> RegorusResult {
match r {
Ok(s) => RegorusResult::ok_string(s),
Err(e) => RegorusResult::err_with_message(RegorusStatus::Error, format!("{e}")),
}
}
/// Drop a `RegorusResult`.
///
/// `output` and `error_message` strings are not valid after drop.
#[no_mangle]
pub extern "C" fn regorus_result_drop(r: RegorusResult) {
unsafe {
if !r.error_message.is_null() {
let _ = CString::from_raw(r.error_message);
}
if !r.output.is_null() {
let _ = CString::from_raw(r.output);
}
}
}
+208
View File
@@ -0,0 +1,208 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use crate::common::{from_c_str, RegorusResult, RegorusStatus};
use crate::compiled_policy::RegorusCompiledPolicy;
use regorus::{compile_policy_with_entrypoint, PolicyModule, Value};
#[cfg(feature = "azure_policy")]
use regorus::compile_policy_for_target;
use std::os::raw::c_char;
/// FFI wrapper for PolicyModule struct.
#[repr(C)]
pub struct RegorusPolicyModule {
pub id: *const c_char,
pub content: *const c_char,
}
/// 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`].
/// It sets up an Engine internally and calls the appropriate compilation method.
///
/// # Parameters
/// * `data_json` - JSON string containing static data for policy evaluation
/// * `modules` - Array of policy modules to compile
/// * `modules_len` - Number of modules in the array
/// * `entry_point_rule` - The specific rule path to evaluate (e.g., "data.policy.allow")
///
/// # Returns
/// Returns a RegorusResult containing a RegorusCompiledPolicy handle on success.
///
/// # Safety
/// All string parameters must be valid null-terminated UTF-8 strings.
/// The modules array must contain exactly `modules_len` valid elements.
/// The caller must eventually call regorus_compiled_policy_drop on the returned handle.
#[no_mangle]
pub extern "C" fn regorus_compile_policy_with_entrypoint(
data_json: *const c_char,
modules: *const RegorusPolicyModule,
modules_len: usize,
entry_point_rule: *const c_char,
) -> RegorusResult {
let data_str = match from_c_str(data_json) {
Ok(s) => s,
Err(e) => {
return RegorusResult::err_with_message(
RegorusStatus::InvalidDataFormat,
format!("Invalid data JSON string: {e}"),
)
}
};
let entry_rule = match from_c_str(entry_point_rule) {
Ok(s) => s,
Err(e) => {
return RegorusResult::err_with_message(
RegorusStatus::InvalidEntrypoint,
format!("Invalid entry point rule string: {e}"),
)
}
};
// Parse data JSON
let data = match Value::from_json_str(&data_str) {
Ok(data) => data,
Err(e) => {
return RegorusResult::err_with_message(
RegorusStatus::InvalidDataFormat,
format!("Failed to parse data JSON: {e}"),
)
}
};
// Convert C modules array to Rust Vec
let policy_modules = match convert_c_modules_to_rust(modules, modules_len) {
Ok(modules) => modules,
Err(status) => return RegorusResult::err(status),
};
// Call the convenience function
match compile_policy_with_entrypoint(data, &policy_modules, entry_rule.into()) {
Ok(compiled_policy) => {
let wrapped_policy = RegorusCompiledPolicy { compiled_policy };
let boxed_policy = Box::new(wrapped_policy);
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut std::os::raw::c_void)
}
Err(e) => RegorusResult::err_with_message(
RegorusStatus::CompilationFailed,
format!("Policy compilation failed: {e}"),
),
}
}
/// Compiles a target-aware policy from data and modules.
///
/// This is a convenience function that wraps [`regorus::compile_policy_for_target`].
/// It sets up an Engine internally and calls target-aware compilation.
///
/// # Parameters
/// * `data_json` - JSON string containing static data for policy evaluation
/// * `modules` - Array of policy modules to compile
/// * `modules_len` - Number of modules in the array
///
/// # Returns
/// Returns a RegorusResult containing a RegorusCompiledPolicy handle on success.
///
/// # Note
/// This function is only available when the `azure_policy` feature is enabled.
/// At least one module must contain a `__target__` declaration.
///
/// # Safety
/// All string parameters must be valid null-terminated UTF-8 strings.
/// The modules array must contain exactly `modules_len` valid elements.
/// The caller must eventually call regorus_compiled_policy_drop on the returned handle.
#[cfg(feature = "azure_policy")]
#[no_mangle]
pub extern "C" fn regorus_compile_policy_for_target(
data_json: *const c_char,
modules: *const RegorusPolicyModule,
modules_len: usize,
) -> RegorusResult {
let data_str = match from_c_str(data_json) {
Ok(s) => s,
Err(e) => {
return RegorusResult::err_with_message(
RegorusStatus::InvalidDataFormat,
format!("Invalid data JSON string: {e}"),
)
}
};
// Parse data JSON
let data = match Value::from_json_str(&data_str) {
Ok(data) => data,
Err(e) => {
return RegorusResult::err_with_message(
RegorusStatus::InvalidDataFormat,
format!("Failed to parse data JSON: {e}"),
)
}
};
// Convert C modules array to Rust Vec
let policy_modules = match convert_c_modules_to_rust(modules, modules_len) {
Ok(modules) => modules,
Err(status) => return RegorusResult::err(status),
};
// Call the convenience function
match compile_policy_for_target(data, &policy_modules) {
Ok(compiled_policy) => {
let wrapped_policy = RegorusCompiledPolicy { compiled_policy };
let boxed_policy = Box::new(wrapped_policy);
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut std::os::raw::c_void)
}
Err(e) => RegorusResult::err_with_message(
RegorusStatus::CompilationFailed,
format!("Target-aware policy compilation failed: {e}"),
),
}
}
/// Helper function to convert C module array to Rust Vec<PolicyModule>.
fn convert_c_modules_to_rust(
modules: *const RegorusPolicyModule,
modules_len: usize,
) -> Result<Vec<PolicyModule>, RegorusStatus> {
if modules.is_null() && modules_len > 0 {
return Err(RegorusStatus::InvalidArgument);
}
let mut policy_modules = Vec::with_capacity(modules_len);
for i in 0..modules_len {
unsafe {
let module = modules.add(i);
if module.is_null() {
return Err(RegorusStatus::InvalidArgument);
}
let module_ref = &*module;
let id = match from_c_str(module_ref.id) {
Ok(s) => s,
Err(e) => {
eprintln!("Invalid module ID at index {}: {}", i, e);
return Err(RegorusStatus::InvalidModuleId);
}
};
let content = match from_c_str(module_ref.content) {
Ok(s) => s,
Err(e) => {
eprintln!("Invalid module content at index {}: {}", i, e);
return Err(RegorusStatus::InvalidPolicy);
}
};
policy_modules.push(PolicyModule {
id: id.into(),
content: content.into(),
});
}
}
Ok(policy_modules)
}
+69
View File
@@ -0,0 +1,69 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use crate::common::*;
use anyhow::Result;
use std::os::raw::c_char;
/// Wrapper for `regorus::CompiledPolicy`.
#[derive(Clone)]
pub struct RegorusCompiledPolicy {
pub(crate) compiled_policy: regorus::CompiledPolicy,
}
/// Drop a `RegorusCompiledPolicy`.
#[no_mangle]
pub extern "C" fn regorus_compiled_policy_drop(compiled_policy: *mut RegorusCompiledPolicy) {
if let Ok(cp) = to_ref(compiled_policy) {
unsafe {
let _ = Box::from_raw(std::ptr::from_mut(cp));
}
}
}
/// 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.
///
/// * `input`: JSON encoded input data (resource) to validate against the policy.
#[no_mangle]
pub extern "C" fn regorus_compiled_policy_eval_with_input(
compiled_policy: *mut RegorusCompiledPolicy,
input: *const c_char,
) -> RegorusResult {
let output = || -> Result<String> {
let input_value = regorus::Value::from_json_str(&from_c_str(input)?)?;
let result = to_ref(compiled_policy)?
.compiled_policy
.eval_with_input(input_value)?;
result.to_json_str()
}();
match output {
Ok(out) => RegorusResult::ok_string(out),
Err(e) => to_regorus_result(Err(e)),
}
}
/// 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.
#[no_mangle]
pub extern "C" fn regorus_compiled_policy_get_policy_info(
compiled_policy: *mut RegorusCompiledPolicy,
) -> RegorusResult {
let output = || -> Result<String> {
let info = to_ref(compiled_policy)?.compiled_policy.get_policy_info()?;
serde_json::to_string(&info)
.map_err(|e| anyhow::anyhow!("Failed to serialize policy info: {}", e))
}();
match output {
Ok(out) => RegorusResult::ok_string(out),
Err(e) => to_regorus_result(Err(e)),
}
}
+175
View File
@@ -0,0 +1,175 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//! Effect schema registry functions for FFI.
//!
//! These functions provide access to regorus's effect schema registry functionality,
//! enabling registration and management of Azure Policy effect schemas.
#![cfg(feature = "azure_policy")]
use crate::common::{from_c_str, RegorusResult, RegorusStatus};
use regorus::{registry::schemas, Schema};
use std::os::raw::c_char;
/// Register an effect schema from JSON with a given name.
///
/// # Parameters
/// * `name` - Name to register the schema under
/// * `schema_json` - JSON string representing the schema
///
/// # Returns
/// Returns a RegorusResult with success/error status.
///
/// # Safety
/// All string parameters must be valid null-terminated UTF-8 strings.
#[cfg(feature = "azure_policy")]
#[no_mangle]
pub extern "C" fn regorus_effect_schema_register(
name: *const c_char,
schema_json: *const c_char,
) -> RegorusResult {
let schema_name = match from_c_str(name) {
Ok(s) => s,
Err(e) => {
return RegorusResult::err_with_message(
RegorusStatus::InvalidArgument,
format!("Invalid effect schema name string: {e}"),
)
}
};
let schema_str = match from_c_str(schema_json) {
Ok(s) => s,
Err(e) => {
return RegorusResult::err_with_message(
RegorusStatus::InvalidDataFormat,
format!("Invalid effect schema JSON string: {e}"),
)
}
};
// Parse schema from JSON
let schema = match Schema::from_json_str(&schema_str) {
Ok(schema) => schema,
Err(e) => {
return RegorusResult::err_with_message(
RegorusStatus::InvalidDataFormat,
format!("Failed to parse effect schema JSON: {e}"),
)
}
};
// Register the schema
match schemas::effect::register(schema_name, schema.into()) {
Ok(()) => RegorusResult::ok_pointer(std::ptr::null_mut()),
Err(e) => RegorusResult::err_with_message(
RegorusStatus::Error,
format!("Failed to register effect schema: {e}"),
),
}
}
/// Check if an effect schema with the given name exists.
///
/// # Parameters
/// * `name` - Name of the schema to check
///
/// # Returns
/// Returns a RegorusResult with "true" or "false" string output.
///
/// # Safety
/// The name parameter must be a valid null-terminated UTF-8 string.
#[cfg(feature = "azure_policy")]
#[no_mangle]
pub extern "C" fn regorus_effect_schema_contains(name: *const c_char) -> RegorusResult {
let schema_name = match from_c_str(name) {
Ok(s) => s,
Err(e) => {
return RegorusResult::err_with_message(
RegorusStatus::InvalidArgument,
format!("Invalid effect schema name string: {e}"),
)
}
};
let contains = schemas::effect::contains(&schema_name);
RegorusResult::ok_bool(contains)
}
/// Get the number of registered effect schemas.
///
/// # Returns
/// Returns a RegorusResult with the count as a string.
#[cfg(feature = "azure_policy")]
#[no_mangle]
pub extern "C" fn regorus_effect_schema_len() -> RegorusResult {
let count = schemas::effect::len();
RegorusResult::ok_int(count as i64)
}
/// Check if the effect schema registry is empty.
///
/// # Returns
/// Returns a RegorusResult with "true" or "false" string output.
#[cfg(feature = "azure_policy")]
#[no_mangle]
pub extern "C" fn regorus_effect_schema_is_empty() -> RegorusResult {
let is_empty = schemas::effect::is_empty();
RegorusResult::ok_bool(is_empty)
}
/// List all registered effect schema names as a JSON array.
///
/// # Returns
/// Returns a RegorusResult with a JSON array of schema names.
#[cfg(feature = "azure_policy")]
#[no_mangle]
pub extern "C" fn regorus_effect_schema_list_names() -> RegorusResult {
let names = schemas::effect::list_names();
match serde_json::to_string(&names) {
Ok(json_str) => RegorusResult::ok_string(json_str),
Err(e) => RegorusResult::err_with_message(
RegorusStatus::Error,
format!("Failed to serialize effect schema names to JSON: {e}"),
),
}
}
/// Remove an effect schema by name.
///
/// # Parameters
/// * `name` - Name of the schema to remove
///
/// # Returns
/// Returns a RegorusResult with "true" if removed, "false" if not found.
///
/// # Safety
/// The name parameter must be a valid null-terminated UTF-8 string.
#[cfg(feature = "azure_policy")]
#[no_mangle]
pub extern "C" fn regorus_effect_schema_remove(name: *const c_char) -> RegorusResult {
let schema_name = match from_c_str(name) {
Ok(s) => s,
Err(e) => {
return RegorusResult::err_with_message(
RegorusStatus::InvalidArgument,
format!("Invalid effect schema name string: {e}"),
)
}
};
let removed = schemas::effect::remove(&schema_name).is_some();
RegorusResult::ok_bool(removed)
}
/// Clear all effect schemas from the registry.
///
/// # Returns
/// Returns a RegorusResult with success status.
#[cfg(feature = "azure_policy")]
#[no_mangle]
pub extern "C" fn regorus_effect_schema_clear() -> RegorusResult {
schemas::effect::clear();
RegorusResult::ok_pointer(std::ptr::null_mut())
}
+454
View File
@@ -0,0 +1,454 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use crate::common::{
from_c_str, to_ref, to_regorus_result, to_regorus_string_result, RegorusResult, RegorusStatus,
};
use crate::compiled_policy::RegorusCompiledPolicy;
use anyhow::Result;
use std::os::raw::c_char;
/// Wrapper for `regorus::Engine`.
#[derive(Clone)]
pub struct RegorusEngine {
engine: ::regorus::Engine,
}
#[no_mangle]
/// Construct a new Engine
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html
pub extern "C" fn regorus_engine_new() -> *mut RegorusEngine {
let mut engine = ::regorus::Engine::new();
// For more OPA compatibility out of the box, we ask builtins to return undefined
// instead of raising errors in certain failure scenarios.
engine.set_strict_builtin_errors(false);
Box::into_raw(Box::new(RegorusEngine { engine }))
}
/// Clone a [`RegorusEngine`]
///
/// To avoid having to parse same policy again, the engine can be cloned
/// after policies and data have been added.
///
#[no_mangle]
pub extern "C" fn regorus_engine_clone(engine: *mut RegorusEngine) -> *mut RegorusEngine {
match to_ref(engine) {
Ok(e) => Box::into_raw(Box::new(e.clone())),
_ => std::ptr::null_mut(),
}
}
#[no_mangle]
pub extern "C" fn regorus_engine_drop(engine: *mut RegorusEngine) {
if let Ok(e) = to_ref(engine) {
unsafe {
let _ = Box::from_raw(std::ptr::from_mut(e));
}
}
}
/// 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.
#[no_mangle]
pub extern "C" fn regorus_engine_add_policy(
engine: *mut RegorusEngine,
path: *const c_char,
rego: *const c_char,
) -> RegorusResult {
to_regorus_string_result(|| -> Result<String> {
to_ref(engine)?
.engine
.add_policy(from_c_str(path)?, from_c_str(rego)?)
}())
}
#[cfg(feature = "std")]
#[no_mangle]
pub extern "C" fn regorus_engine_add_policy_from_file(
engine: *mut RegorusEngine,
path: *const c_char,
) -> RegorusResult {
to_regorus_string_result(|| -> Result<String> {
to_ref(engine)?
.engine
.add_policy_from_file(from_c_str(path)?)
}())
}
/// 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.
#[no_mangle]
pub extern "C" fn regorus_engine_add_data_json(
engine: *mut RegorusEngine,
data: *const c_char,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(engine)?
.engine
.add_data(regorus::Value::from_json_str(&from_c_str(data)?)?)
}())
}
/// Get list of loaded Rego packages as JSON.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_packages
#[no_mangle]
pub extern "C" fn regorus_engine_get_packages(engine: *mut RegorusEngine) -> RegorusResult {
to_regorus_string_result(|| -> Result<String> {
serde_json::to_string_pretty(&to_ref(engine)?.engine.get_packages()?)
.map_err(anyhow::Error::msg)
}())
}
/// Get list of policies as JSON.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_policies
#[no_mangle]
pub extern "C" fn regorus_engine_get_policies(engine: *mut RegorusEngine) -> RegorusResult {
to_regorus_string_result(|| -> Result<String> {
to_ref(engine)?.engine.get_policies_as_json()
}())
}
#[cfg(feature = "std")]
#[no_mangle]
pub extern "C" fn regorus_engine_add_data_from_json_file(
engine: *mut RegorusEngine,
path: *const c_char,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(engine)?
.engine
.add_data(regorus::Value::from_json_file(from_c_str(path)?)?)
}())
}
/// Clear policy data.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.clear_data
#[no_mangle]
pub extern "C" fn regorus_engine_clear_data(engine: *mut RegorusEngine) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(engine)?.engine.clear_data();
Ok(())
}())
}
/// 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.
#[no_mangle]
pub extern "C" fn regorus_engine_set_input_json(
engine: *mut RegorusEngine,
input: *const c_char,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(engine)?
.engine
.set_input(regorus::Value::from_json_str(&from_c_str(input)?)?);
Ok(())
}())
}
#[cfg(feature = "std")]
#[no_mangle]
pub extern "C" fn regorus_engine_set_input_from_json_file(
engine: *mut RegorusEngine,
path: *const c_char,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(engine)?
.engine
.set_input(regorus::Value::from_json_file(from_c_str(path)?)?);
Ok(())
}())
}
/// Evaluate query.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.eval_query
/// * `query`: Rego expression to be evaluate.
#[no_mangle]
pub extern "C" fn regorus_engine_eval_query(
engine: *mut RegorusEngine,
query: *const c_char,
) -> RegorusResult {
let output = || -> Result<String> {
let results = to_ref(engine)?
.engine
.eval_query(from_c_str(query)?, false)?;
Ok(serde_json::to_string_pretty(&results)?)
}();
match output {
Ok(out) => RegorusResult::ok_string(out),
Err(e) => to_regorus_result(Err(e)),
}
}
/// Evaluate specified rule.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.eval_rule
/// * `rule`: Path to the rule.
#[no_mangle]
pub extern "C" fn regorus_engine_eval_rule(
engine: *mut RegorusEngine,
rule: *const c_char,
) -> RegorusResult {
let output = || -> Result<String> {
to_ref(engine)?
.engine
.eval_rule(from_c_str(rule)?)?
.to_json_str()
}();
match output {
Ok(out) => RegorusResult::ok_string(out),
Err(e) => to_regorus_result(Err(e)),
}
}
/// Enable/disable coverage.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_enable_coverage
/// * `enable`: Whether to enable or disable coverage.
#[no_mangle]
#[cfg(feature = "coverage")]
pub extern "C" fn regorus_engine_set_enable_coverage(
engine: *mut RegorusEngine,
enable: bool,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(engine)?.engine.set_enable_coverage(enable);
Ok(())
}())
}
/// Get coverage report.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_coverage_report
#[no_mangle]
#[cfg(feature = "coverage")]
pub extern "C" fn regorus_engine_get_coverage_report(engine: *mut RegorusEngine) -> RegorusResult {
let output = || -> Result<String> {
Ok(serde_json::to_string_pretty(
&to_ref(engine)?.engine.get_coverage_report()?,
)?)
}();
match output {
Ok(out) => RegorusResult::ok_string(out),
Err(e) => to_regorus_result(Err(e)),
}
}
/// 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.
#[no_mangle]
pub extern "C" fn regorus_engine_set_strict_builtin_errors(
engine: *mut RegorusEngine,
strict: bool,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(engine)?.engine.set_strict_builtin_errors(strict);
Ok(())
}())
}
/// Get pretty printed coverage report.
///
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Report.html#method.to_string_pretty
#[no_mangle]
#[cfg(feature = "coverage")]
pub extern "C" fn regorus_engine_get_coverage_report_pretty(
engine: *mut RegorusEngine,
) -> RegorusResult {
let output = || -> Result<String> {
to_ref(engine)?
.engine
.get_coverage_report()?
.to_string_pretty()
}();
match output {
Ok(out) => RegorusResult::ok_string(out),
Err(e) => to_regorus_result(Err(e)),
}
}
/// Clear coverage data.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.clear_coverage_data
#[no_mangle]
#[cfg(feature = "coverage")]
pub extern "C" fn regorus_engine_clear_coverage_data(engine: *mut RegorusEngine) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(engine)?.engine.clear_coverage_data();
Ok(())
}())
}
/// 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.
#[no_mangle]
pub extern "C" fn regorus_engine_set_gather_prints(
engine: *mut RegorusEngine,
enable: bool,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(engine)?.engine.set_gather_prints(enable);
Ok(())
}())
}
/// Take all the gathered print statements.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.take_prints
#[no_mangle]
pub extern "C" fn regorus_engine_take_prints(engine: *mut RegorusEngine) -> RegorusResult {
let output = || -> Result<String> {
Ok(serde_json::to_string_pretty(
&to_ref(engine)?.engine.take_prints()?,
)?)
}();
match output {
Ok(out) => RegorusResult::ok_string(out),
Err(e) => to_regorus_result(Err(e)),
}
}
/// Get AST of policies.
///
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_ast_as_json
#[no_mangle]
#[cfg(feature = "ast")]
pub extern "C" fn regorus_engine_get_ast_as_json(engine: *mut RegorusEngine) -> RegorusResult {
let output = || -> Result<String> { to_ref(engine)?.engine.get_ast_as_json() }();
match output {
Ok(out) => RegorusResult::ok_string(out),
Err(e) => to_regorus_result(Err(e)),
}
}
/// Gets the package names defined in each policy added to the engine.
///
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_policy_package_names
#[no_mangle]
#[cfg(feature = "azure_policy")]
pub extern "C" fn regorus_engine_get_policy_package_names(
engine: *mut RegorusEngine,
) -> RegorusResult {
let output = || -> Result<String> {
serde_json::to_string_pretty(&to_ref(engine)?.engine.get_policy_package_names()?)
.map_err(anyhow::Error::msg)
}();
match output {
Ok(out) => RegorusResult::ok_string(out),
Err(e) => to_regorus_result(Err(e)),
}
}
/// Gets the parameters defined in each policy added to the engine.
///
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_policy_parameters
#[no_mangle]
#[cfg(feature = "azure_policy")]
pub extern "C" fn regorus_engine_get_policy_parameters(
engine: *mut RegorusEngine,
) -> RegorusResult {
let output = || -> Result<String> {
serde_json::to_string_pretty(&to_ref(engine)?.engine.get_policy_parameters()?)
.map_err(anyhow::Error::msg)
}();
match output {
Ok(out) => RegorusResult::ok_string(out),
Err(e) => to_regorus_result(Err(e)),
}
}
/// Enable/disable rego v1.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_rego_v0
#[no_mangle]
pub extern "C" fn regorus_engine_set_rego_v0(
engine: *mut RegorusEngine,
enable: bool,
) -> RegorusResult {
let output = || -> Result<()> {
to_ref(engine)?.engine.set_rego_v0(enable);
Ok(())
}();
match output {
Ok(()) => RegorusResult::ok_void(),
Err(e) => to_regorus_result(Err(e)),
}
}
/// Compile a target-aware policy from the current engine state.
///
/// This method creates a compiled policy that can work with Azure Policy targets,
/// enabling resource type inference and target-specific evaluation.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.compile_for_target
#[no_mangle]
#[cfg(feature = "azure_policy")]
pub extern "C" fn regorus_engine_compile_for_target(engine: *mut RegorusEngine) -> RegorusResult {
match to_ref(engine) {
Ok(e) => match e.engine.compile_for_target() {
Ok(compiled_policy) => {
let wrapped_policy = RegorusCompiledPolicy { compiled_policy };
let boxed_policy = Box::new(wrapped_policy);
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut std::os::raw::c_void)
}
Err(e) => RegorusResult::err_with_message(
RegorusStatus::CompilationFailed,
format!("Failed to compile for target: {e}"),
),
},
Err(e) => RegorusResult::err_with_message(
RegorusStatus::InvalidArgument,
format!("Failed to get engine reference: {e}"),
),
}
}
/// Compile a policy with a specific entry point rule.
///
/// This method creates a compiled policy that evaluates a specific rule as the entry point.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.compile_with_entrypoint
/// * `rule`: The specific rule path to evaluate (e.g., "data.policy.allow")
#[no_mangle]
pub extern "C" fn regorus_engine_compile_with_entrypoint(
engine: *mut RegorusEngine,
rule: *const c_char,
) -> RegorusResult {
let result = || -> Result<RegorusCompiledPolicy> {
let rule_str = from_c_str(rule)?;
let rule_rc: regorus::Rc<str> = rule_str.into();
let compiled_policy = to_ref(engine)?.engine.compile_with_entrypoint(&rule_rc)?;
Ok(RegorusCompiledPolicy { compiled_policy })
}();
match result {
Ok(wrapped_policy) => {
let boxed_policy = Box::new(wrapped_policy);
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut std::os::raw::c_void)
}
Err(e) => RegorusResult::err_with_message(
RegorusStatus::CompilationFailed,
format!("Failed to compile with entrypoint: {e}"),
),
}
}
+8 -550
View File
@@ -1,553 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use anyhow::{anyhow, bail, Result};
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
/// Status of a call on `RegorusEngine`.
#[repr(C)]
pub enum RegorusStatus {
/// The operation was successful.
RegorusStatusOk,
/// The operation was unsuccessful.
RegorusStatusError,
}
/// Result of a call on `RegorusEngine`.
///
/// Must be freed using `regorus_result_drop`.
#[repr(C)]
pub struct RegorusResult {
/// Status
status: RegorusStatus,
/// Output produced by the call.
/// Owned by Rust.
output: *mut c_char,
/// Errors produced by the call.
/// Owned by Rust.
error_message: *mut c_char,
}
fn to_c_str(s: String) -> *mut c_char {
match CString::new(s) {
Ok(cs) => cs.into_raw(),
_ => to_c_str("binding error: failed to create c-style string".to_string()),
}
}
fn from_c_str(name: &str, s: *const c_char) -> Result<String> {
if s.is_null() {
bail!("null pointer");
}
unsafe {
CStr::from_ptr(s)
.to_str()
.map_err(|e| anyhow!("`{name}`: invalid utf8.\n{e}"))
.map(|s| s.to_string())
}
}
fn to_ref<'a, T>(t: *mut T) -> Result<&'a mut T> {
unsafe { t.as_mut().ok_or_else(|| anyhow!("null pointer")) }
}
fn to_regorus_result(r: Result<()>) -> RegorusResult {
match r {
Ok(()) => RegorusResult {
status: RegorusStatus::RegorusStatusOk,
output: std::ptr::null_mut(),
error_message: std::ptr::null_mut(),
},
Err(e) => RegorusResult {
status: RegorusStatus::RegorusStatusError,
output: std::ptr::null_mut(),
error_message: to_c_str(format!("{e}")),
},
}
}
fn to_regorus_string_result(r: Result<String>) -> RegorusResult {
match r {
Ok(s) => RegorusResult {
status: RegorusStatus::RegorusStatusOk,
output: to_c_str(s),
error_message: std::ptr::null_mut(),
},
Err(e) => RegorusResult {
status: RegorusStatus::RegorusStatusError,
output: std::ptr::null_mut(),
error_message: to_c_str(format!("{e}")),
},
}
}
/// Wrapper for `regorus::Engine`.
#[derive(Clone)]
pub struct RegorusEngine {
engine: ::regorus::Engine,
}
/// Drop a `RegorusResult`.
///
/// `output` and `error_message` strings are not valid after drop.
#[no_mangle]
pub extern "C" fn regorus_result_drop(r: RegorusResult) {
unsafe {
if !r.error_message.is_null() {
let _ = CString::from_raw(r.error_message);
}
if !r.output.is_null() {
let _ = CString::from_raw(r.output);
}
}
}
#[no_mangle]
/// Construct a new Engine
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html
pub extern "C" fn regorus_engine_new() -> *mut RegorusEngine {
let mut engine = ::regorus::Engine::new();
// For more OPA compatibility out of the box, we ask builtins to return undefined
// instead of raising errors in certain failure scenarios.
engine.set_strict_builtin_errors(false);
Box::into_raw(Box::new(RegorusEngine { engine }))
}
/// Clone a [`RegorusEngine`]
///
/// To avoid having to parse same policy again, the engine can be cloned
/// after policies and data have been added.
///
#[no_mangle]
pub extern "C" fn regorus_engine_clone(engine: *mut RegorusEngine) -> *mut RegorusEngine {
match to_ref(engine) {
Ok(e) => Box::into_raw(Box::new(e.clone())),
_ => std::ptr::null_mut(),
}
}
#[no_mangle]
pub extern "C" fn regorus_engine_drop(engine: *mut RegorusEngine) {
if let Ok(e) = to_ref(engine) {
unsafe {
let _ = Box::from_raw(std::ptr::from_mut(e));
}
}
}
/// 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.
#[no_mangle]
pub extern "C" fn regorus_engine_add_policy(
engine: *mut RegorusEngine,
path: *const c_char,
rego: *const c_char,
) -> RegorusResult {
to_regorus_string_result(|| -> Result<String> {
to_ref(engine)?
.engine
.add_policy(from_c_str("path", path)?, from_c_str("rego", rego)?)
}())
}
#[cfg(feature = "std")]
#[no_mangle]
pub extern "C" fn regorus_engine_add_policy_from_file(
engine: *mut RegorusEngine,
path: *const c_char,
) -> RegorusResult {
to_regorus_string_result(|| -> Result<String> {
to_ref(engine)?
.engine
.add_policy_from_file(from_c_str("path", path)?)
}())
}
/// 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.
#[no_mangle]
pub extern "C" fn regorus_engine_add_data_json(
engine: *mut RegorusEngine,
data: *const c_char,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(engine)?
.engine
.add_data(regorus::Value::from_json_str(&from_c_str("data", data)?)?)
}())
}
/// Get list of loaded Rego packages as JSON.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_packages
#[no_mangle]
pub extern "C" fn regorus_engine_get_packages(engine: *mut RegorusEngine) -> RegorusResult {
to_regorus_string_result(|| -> Result<String> {
serde_json::to_string_pretty(&to_ref(engine)?.engine.get_packages()?)
.map_err(anyhow::Error::msg)
}())
}
/// Get list of policies as JSON.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_policies
#[no_mangle]
pub extern "C" fn regorus_engine_get_policies(engine: *mut RegorusEngine) -> RegorusResult {
to_regorus_string_result(|| -> Result<String> {
to_ref(engine)?.engine.get_policies_as_json()
}())
}
#[cfg(feature = "std")]
#[no_mangle]
pub extern "C" fn regorus_engine_add_data_from_json_file(
engine: *mut RegorusEngine,
path: *const c_char,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(engine)?
.engine
.add_data(regorus::Value::from_json_file(from_c_str("path", path)?)?)
}())
}
/// Clear policy data.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.clear_data
#[no_mangle]
pub extern "C" fn regorus_engine_clear_data(engine: *mut RegorusEngine) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(engine)?.engine.clear_data();
Ok(())
}())
}
/// 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.
#[no_mangle]
pub extern "C" fn regorus_engine_set_input_json(
engine: *mut RegorusEngine,
input: *const c_char,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(engine)?
.engine
.set_input(regorus::Value::from_json_str(&from_c_str("input", input)?)?);
Ok(())
}())
}
#[cfg(feature = "std")]
#[no_mangle]
pub extern "C" fn regorus_engine_set_input_from_json_file(
engine: *mut RegorusEngine,
path: *const c_char,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(engine)?
.engine
.set_input(regorus::Value::from_json_file(from_c_str("path", path)?)?);
Ok(())
}())
}
/// Evaluate query.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.eval_query
/// * `query`: Rego expression to be evaluate.
#[no_mangle]
pub extern "C" fn regorus_engine_eval_query(
engine: *mut RegorusEngine,
query: *const c_char,
) -> RegorusResult {
let output = || -> Result<String> {
let results = to_ref(engine)?
.engine
.eval_query(from_c_str("query", query)?, false)?;
Ok(serde_json::to_string_pretty(&results)?)
}();
match output {
Ok(out) => RegorusResult {
status: RegorusStatus::RegorusStatusOk,
output: to_c_str(out),
error_message: std::ptr::null_mut(),
},
Err(e) => to_regorus_result(Err(e)),
}
}
/// Evaluate specified rule.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.eval_rule
/// * `rule`: Path to the rule.
#[no_mangle]
pub extern "C" fn regorus_engine_eval_rule(
engine: *mut RegorusEngine,
rule: *const c_char,
) -> RegorusResult {
let output = || -> Result<String> {
to_ref(engine)?
.engine
.eval_rule(from_c_str("rule", rule)?)?
.to_json_str()
}();
match output {
Ok(out) => RegorusResult {
status: RegorusStatus::RegorusStatusOk,
output: to_c_str(out),
error_message: std::ptr::null_mut(),
},
Err(e) => to_regorus_result(Err(e)),
}
}
/// Enable/disable coverage.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_enable_coverage
/// * `enable`: Whether to enable or disable coverage.
#[no_mangle]
#[cfg(feature = "coverage")]
pub extern "C" fn regorus_engine_set_enable_coverage(
engine: *mut RegorusEngine,
enable: bool,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(engine)?.engine.set_enable_coverage(enable);
Ok(())
}())
}
/// Get coverage report.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_coverage_report
#[no_mangle]
#[cfg(feature = "coverage")]
pub extern "C" fn regorus_engine_get_coverage_report(engine: *mut RegorusEngine) -> RegorusResult {
let output = || -> Result<String> {
Ok(serde_json::to_string_pretty(
&to_ref(engine)?.engine.get_coverage_report()?,
)?)
}();
match output {
Ok(out) => RegorusResult {
status: RegorusStatus::RegorusStatusOk,
output: to_c_str(out),
error_message: std::ptr::null_mut(),
},
Err(e) => to_regorus_result(Err(e)),
}
}
/// 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.
#[no_mangle]
pub extern "C" fn regorus_engine_set_strict_builtin_errors(
engine: *mut RegorusEngine,
strict: bool,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(engine)?.engine.set_strict_builtin_errors(strict);
Ok(())
}())
}
/// Get pretty printed coverage report.
///
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Report.html#method.to_string_pretty
#[no_mangle]
#[cfg(feature = "coverage")]
pub extern "C" fn regorus_engine_get_coverage_report_pretty(
engine: *mut RegorusEngine,
) -> RegorusResult {
let output = || -> Result<String> {
to_ref(engine)?
.engine
.get_coverage_report()?
.to_string_pretty()
}();
match output {
Ok(out) => RegorusResult {
status: RegorusStatus::RegorusStatusOk,
output: to_c_str(out),
error_message: std::ptr::null_mut(),
},
Err(e) => to_regorus_result(Err(e)),
}
}
/// Clear coverage data.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.clear_coverage_data
#[no_mangle]
#[cfg(feature = "coverage")]
pub extern "C" fn regorus_engine_clear_coverage_data(engine: *mut RegorusEngine) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(engine)?.engine.clear_coverage_data();
Ok(())
}())
}
/// 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.
#[no_mangle]
pub extern "C" fn regorus_engine_set_gather_prints(
engine: *mut RegorusEngine,
enable: bool,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(engine)?.engine.set_gather_prints(enable);
Ok(())
}())
}
/// Take all the gathered print statements.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.take_prints
#[no_mangle]
pub extern "C" fn regorus_engine_take_prints(engine: *mut RegorusEngine) -> RegorusResult {
let output = || -> Result<String> {
Ok(serde_json::to_string_pretty(
&to_ref(engine)?.engine.take_prints()?,
)?)
}();
match output {
Ok(out) => RegorusResult {
status: RegorusStatus::RegorusStatusOk,
output: to_c_str(out),
error_message: std::ptr::null_mut(),
},
Err(e) => to_regorus_result(Err(e)),
}
}
/// Get AST of policies.
///
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Engine.html#method.get_ast_as_json
#[no_mangle]
#[cfg(feature = "ast")]
pub extern "C" fn regorus_engine_get_ast_as_json(engine: *mut RegorusEngine) -> RegorusResult {
let output = || -> Result<String> { to_ref(engine)?.engine.get_ast_as_json() }();
match output {
Ok(out) => RegorusResult {
status: RegorusStatus::RegorusStatusOk,
output: to_c_str(out),
error_message: std::ptr::null_mut(),
},
Err(e) => to_regorus_result(Err(e)),
}
}
/// 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
#[no_mangle]
#[cfg(feature = "azure_policy")]
pub extern "C" fn regorus_engine_get_policy_package_names(
engine: *mut RegorusEngine,
) -> RegorusResult {
let output = || -> Result<String> {
serde_json::to_string_pretty(&to_ref(engine)?.engine.get_policy_package_names()?)
.map_err(anyhow::Error::msg)
}();
match output {
Ok(out) => RegorusResult {
status: RegorusStatus::RegorusStatusOk,
output: to_c_str(out),
error_message: std::ptr::null_mut(),
},
Err(e) => to_regorus_result(Err(e)),
}
}
/// 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
#[no_mangle]
#[cfg(feature = "azure_policy")]
pub extern "C" fn regorus_engine_get_policy_parameters(
engine: *mut RegorusEngine,
) -> RegorusResult {
let output = || -> Result<String> {
serde_json::to_string_pretty(&to_ref(engine)?.engine.get_policy_parameters()?)
.map_err(anyhow::Error::msg)
}();
match output {
Ok(out) => RegorusResult {
status: RegorusStatus::RegorusStatusOk,
output: to_c_str(out),
error_message: std::ptr::null_mut(),
},
Err(e) => to_regorus_result(Err(e)),
}
}
/// Enable/disable rego v1.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_rego_v0
#[no_mangle]
pub extern "C" fn regorus_engine_set_rego_v0(
engine: *mut RegorusEngine,
enable: bool,
) -> RegorusResult {
let output = || -> Result<()> {
to_ref(engine)?.engine.set_rego_v0(enable);
Ok(())
}();
match output {
Ok(()) => RegorusResult {
status: RegorusStatus::RegorusStatusOk,
output: std::ptr::null_mut(),
error_message: std::ptr::null_mut(),
},
Err(e) => to_regorus_result(Err(e)),
}
}
#[cfg(feature = "custom_allocator")]
extern "C" {
fn regorus_aligned_alloc(alignment: usize, size: usize) -> *mut u8;
fn regorus_free(ptr: *mut u8);
}
#[cfg(feature = "custom_allocator")]
mod allocator {
use std::alloc::{GlobalAlloc, Layout};
struct RegorusAllocator {}
unsafe impl GlobalAlloc for RegorusAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let size = layout.size();
let align = layout.align();
crate::regorus_aligned_alloc(align, size)
}
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
crate::regorus_free(ptr)
}
}
#[global_allocator]
static ALLOCATOR: RegorusAllocator = RegorusAllocator {};
}
mod allocator;
mod common;
mod compile;
mod compiled_policy;
mod effect_registry;
mod engine;
mod schema_registry;
mod target_registry;
+178
View File
@@ -0,0 +1,178 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//! Schema registry functions for FFI.
//!
//! These functions provide access to regorus's resource schema registry functionality,
//! enabling registration and management of Azure Policy resource schemas.
#![cfg(feature = "azure_policy")]
use crate::common::{from_c_str, RegorusResult, RegorusStatus};
use regorus::{registry::schemas, Schema};
use std::os::raw::c_char;
// Resource Schema Registry Functions
/// Register a resource schema from JSON with a given name.
///
/// # Parameters
/// * `name` - Name to register the schema under
/// * `schema_json` - JSON string representing the schema
///
/// # Returns
/// Returns a RegorusResult with success/error status.
///
/// # Safety
/// All string parameters must be valid null-terminated UTF-8 strings.
#[cfg(feature = "azure_policy")]
#[no_mangle]
pub extern "C" fn regorus_resource_schema_register(
name: *const c_char,
schema_json: *const c_char,
) -> RegorusResult {
let schema_name = match from_c_str(name) {
Ok(s) => s,
Err(e) => {
return RegorusResult::err_with_message(
RegorusStatus::InvalidArgument,
format!("Invalid schema name string: {e}"),
)
}
};
let schema_str = match from_c_str(schema_json) {
Ok(s) => s,
Err(e) => {
return RegorusResult::err_with_message(
RegorusStatus::InvalidDataFormat,
format!("Invalid schema JSON string: {e}"),
)
}
};
// Parse schema from JSON
let schema = match Schema::from_json_str(&schema_str) {
Ok(schema) => schema,
Err(e) => {
return RegorusResult::err_with_message(
RegorusStatus::InvalidDataFormat,
format!("Failed to parse schema JSON: {e}"),
)
}
};
// Register the schema
match schemas::resource::register(schema_name, schema.into()) {
Ok(()) => RegorusResult::ok_pointer(std::ptr::null_mut()),
Err(e) => RegorusResult::err_with_message(
RegorusStatus::Error,
format!("Failed to register schema: {e}"),
),
}
}
/// Check if a resource schema with the given name exists.
///
/// # Parameters
/// * `name` - Name of the schema to check
///
/// # Returns
/// Returns a RegorusResult with "true" or "false" string output.
///
/// # Safety
/// The name parameter must be a valid null-terminated UTF-8 string.
#[cfg(feature = "azure_policy")]
#[no_mangle]
pub extern "C" fn regorus_resource_schema_contains(name: *const c_char) -> RegorusResult {
let schema_name = match from_c_str(name) {
Ok(s) => s,
Err(e) => {
return RegorusResult::err_with_message(
RegorusStatus::InvalidArgument,
format!("Invalid schema name string: {e}"),
)
}
};
let contains = schemas::resource::contains(&schema_name);
RegorusResult::ok_bool(contains)
}
/// Get the number of registered resource schemas.
///
/// # Returns
/// Returns a RegorusResult with the count as a string.
#[cfg(feature = "azure_policy")]
#[no_mangle]
pub extern "C" fn regorus_resource_schema_len() -> RegorusResult {
let count = schemas::resource::len();
RegorusResult::ok_int(count as i64)
}
/// Check if the resource schema registry is empty.
///
/// # Returns
/// Returns a RegorusResult with "true" or "false" string output.
#[cfg(feature = "azure_policy")]
#[no_mangle]
pub extern "C" fn regorus_resource_schema_is_empty() -> RegorusResult {
let is_empty = schemas::resource::is_empty();
RegorusResult::ok_bool(is_empty)
}
/// List all registered resource schema names as a JSON array.
///
/// # Returns
/// Returns a RegorusResult with a JSON array of schema names.
#[cfg(feature = "azure_policy")]
#[no_mangle]
pub extern "C" fn regorus_resource_schema_list_names() -> RegorusResult {
let names = schemas::resource::list_names();
match serde_json::to_string(&names) {
Ok(json_str) => RegorusResult::ok_string(json_str),
Err(e) => RegorusResult::err_with_message(
RegorusStatus::Error,
format!("Failed to serialize schema names to JSON: {e}"),
),
}
}
/// Remove a resource schema by name.
///
/// # Parameters
/// * `name` - Name of the schema to remove
///
/// # Returns
/// Returns a RegorusResult with "true" if removed, "false" if not found.
///
/// # Safety
/// The name parameter must be a valid null-terminated UTF-8 string.
#[cfg(feature = "azure_policy")]
#[no_mangle]
pub extern "C" fn regorus_resource_schema_remove(name: *const c_char) -> RegorusResult {
let schema_name = match from_c_str(name) {
Ok(s) => s,
Err(e) => {
return RegorusResult::err_with_message(
RegorusStatus::InvalidArgument,
format!("Invalid schema name string: {e}"),
)
}
};
let removed = schemas::resource::remove(&schema_name).is_some();
RegorusResult::ok_bool(removed)
}
/// Clear all resource schemas from the registry.
///
/// # Returns
/// Returns a RegorusResult with success status.
#[cfg(feature = "azure_policy")]
#[no_mangle]
pub extern "C" fn regorus_resource_schema_clear() -> RegorusResult {
schemas::resource::clear();
RegorusResult::ok_pointer(std::ptr::null_mut())
}
+107
View File
@@ -0,0 +1,107 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#![cfg(feature = "azure_policy")]
use crate::common::*;
use anyhow::Result;
use std::os::raw::c_char;
/// 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.
///
/// * `target_json`: JSON encoded target definition
#[no_mangle]
#[cfg(feature = "azure_policy")]
pub extern "C" fn regorus_register_target_from_json(target_json: *const c_char) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
let target_str = from_c_str(target_json)?;
let target = regorus::Target::from_json_str(&target_str)?;
regorus::registry::targets::register(regorus::Rc::new(target))?;
Ok(())
}())
}
/// Check if a target is registered.
///
/// # Parameters
/// * `name` - Name of the target to check
///
/// # Returns
/// Returns a RegorusResult with boolean value indicating if the target is registered.
///
/// # Safety
/// The name parameter must be a valid null-terminated UTF-8 string.
#[no_mangle]
pub extern "C" fn regorus_target_registry_contains(name: *const c_char) -> RegorusResult {
let target_name = match from_c_str(name) {
Ok(s) => s,
Err(e) => {
return RegorusResult::err_with_message(
RegorusStatus::InvalidArgument,
format!("Invalid target name string: {e}"),
)
}
};
let contains = regorus::registry::targets::contains(&target_name);
RegorusResult::ok_bool(contains)
}
/// Get a list of all registered target names as JSON array.
#[no_mangle]
#[cfg(feature = "azure_policy")]
pub extern "C" fn regorus_target_registry_list_names() -> RegorusResult {
let names = regorus::registry::targets::list_names();
let output = serde_json::to_string_pretty(&names).map_err(anyhow::Error::msg);
match output {
Ok(out) => RegorusResult::ok_string(out),
Err(e) => to_regorus_result(Err(e)),
}
}
/// Remove a target from the registry by name.
///
/// * `name`: The target name to remove
#[no_mangle]
#[cfg(feature = "azure_policy")]
pub extern "C" fn regorus_target_registry_remove(name: *const c_char) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
let name_str = from_c_str(name)?;
regorus::registry::targets::remove(&name_str);
Ok(())
}())
}
/// Clear all targets from the registry.
#[no_mangle]
#[cfg(feature = "azure_policy")]
pub extern "C" fn regorus_target_registry_clear() -> RegorusResult {
regorus::registry::targets::clear();
RegorusResult::ok_void()
}
/// Get the number of registered targets.
///
/// # Returns
/// Returns a RegorusResult with the count as an integer value.
#[no_mangle]
#[cfg(feature = "azure_policy")]
pub extern "C" fn regorus_target_registry_len() -> RegorusResult {
let count = regorus::registry::targets::len();
RegorusResult::ok_int(count as i64)
}
/// Check if the target registry is empty.
///
/// # Returns
/// Returns a RegorusResult with boolean value indicating if the registry is empty.
#[no_mangle]
#[cfg(feature = "azure_policy")]
pub extern "C" fn regorus_target_registry_is_empty() -> RegorusResult {
let is_empty = regorus::registry::targets::is_empty();
RegorusResult::ok_bool(is_empty)
}