mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
feat: make policy length limits configurable per engine (#624)
- Add PolicyLengthConfig struct with max_col, max_file_bytes, and max_lines fields, replacing hardcoded constants in the lexer. - Add Engine::set_policy_length_config and clear_policy_length_config to allow callers to override the default limits. - Add Source::from_contents_with_limits and from_file_with_limits for direct Source construction with custom limits; existing from_contents and from_file signatures are preserved using defaults. - Add tests for default rejection, custom limits, and engine plumbing. - Add bindings for C, C++, Python, WASM/JS, Java, Ruby, C#, Go
This commit is contained in:
@@ -11,6 +11,13 @@ int main() {
|
||||
if (r.status != Ok)
|
||||
goto error;
|
||||
|
||||
// Raise the default col limit to 2000
|
||||
RegorusPolicyLengthConfig len_config = { .max_col = 2000, .max_file_bytes = 1048576, .max_lines = 20000 };
|
||||
r = regorus_engine_set_policy_length_config(engine, len_config);
|
||||
if (r.status != Ok)
|
||||
goto error;
|
||||
regorus_result_drop(r);
|
||||
|
||||
// Load policies.
|
||||
r = regorus_engine_add_policy_from_file(engine, "../../../tests/aci/framework.rego");
|
||||
if (r.status != Ok)
|
||||
|
||||
@@ -8,6 +8,13 @@ void example()
|
||||
|
||||
engine.set_rego_v0(true);
|
||||
engine.set_enable_coverage(true);
|
||||
|
||||
RegorusPolicyLengthConfig len_config;
|
||||
// Raise the default col limit to 2000
|
||||
len_config.max_col = 2000;
|
||||
len_config.max_file_bytes = 1048576;
|
||||
len_config.max_lines = 20000;
|
||||
engine.set_policy_length_config(len_config);
|
||||
|
||||
// Add policies.
|
||||
engine.add_policy("objects.rego",R"(package objects
|
||||
|
||||
@@ -131,7 +131,15 @@ namespace regorus {
|
||||
Result get_coverage_report_pretty() {
|
||||
return Result(regorus_engine_get_coverage_report_pretty(engine));
|
||||
}
|
||||
|
||||
|
||||
Result set_policy_length_config(RegorusPolicyLengthConfig config) {
|
||||
return Result(regorus_engine_set_policy_length_config(engine, config));
|
||||
}
|
||||
|
||||
Result clear_policy_length_config() {
|
||||
return Result(regorus_engine_clear_policy_length_config(engine));
|
||||
}
|
||||
|
||||
~Engine() {
|
||||
regorus_engine_drop(engine);
|
||||
}
|
||||
|
||||
@@ -82,6 +82,24 @@ namespace Regorus
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_clear_execution_timer_config((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
});
|
||||
}
|
||||
|
||||
public void SetPolicyLengthConfig(PolicyLengthConfig config)
|
||||
{
|
||||
var nativeConfig = config.ToNative();
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_policy_length_config((Regorus.Internal.RegorusEngine*)enginePtr, nativeConfig));
|
||||
});
|
||||
}
|
||||
|
||||
public void ClearPolicyLengthConfig()
|
||||
{
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_clear_policy_length_config((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
});
|
||||
}
|
||||
|
||||
public string? AddPolicy(string path, string rego)
|
||||
{
|
||||
return Utf8Marshaller.WithUtf8(path, pathPtr =>
|
||||
|
||||
@@ -428,6 +428,18 @@ namespace Regorus.Internal
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_engine_clear_execution_timer_config", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_engine_clear_execution_timer_config(RegorusEngine* engine);
|
||||
|
||||
/// <summary>
|
||||
/// Set the policy length limits for a specific engine instance.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_engine_set_policy_length_config", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_engine_set_policy_length_config(RegorusEngine* engine, RegorusPolicyLengthConfig config);
|
||||
|
||||
/// <summary>
|
||||
/// Clear the policy length configuration for a specific engine instance.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_engine_clear_policy_length_config", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_engine_clear_policy_length_config(RegorusEngine* engine);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Execution Timer Global Methods
|
||||
@@ -772,6 +784,17 @@ namespace Regorus.Internal
|
||||
public uint check_interval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FFI representation of the policy length configuration.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct RegorusPolicyLengthConfig
|
||||
{
|
||||
public uint max_col;
|
||||
public UIntPtr max_file_bytes;
|
||||
public UIntPtr max_lines;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Byte buffer returned from FFI.
|
||||
/// </summary>
|
||||
|
||||
53
bindings/csharp/Regorus/PolicyLengthConfig.cs
Normal file
53
bindings/csharp/Regorus/PolicyLengthConfig.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Regorus
|
||||
{
|
||||
/// <summary>
|
||||
/// Policy source length limits enforced when loading policy files.
|
||||
/// </summary>
|
||||
public readonly struct PolicyLengthConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PolicyLengthConfig"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="maxCol">Maximum column width per line. Must be non-zero.</param>
|
||||
/// <param name="maxFileBytes">Maximum policy file size in bytes. Must be non-zero.</param>
|
||||
/// <param name="maxLines">Maximum number of lines per policy file. Must be non-zero.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when any parameter is zero.</exception>
|
||||
public PolicyLengthConfig(uint maxCol, nuint maxFileBytes, nuint maxLines)
|
||||
{
|
||||
if (maxCol == 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(maxCol), "Must be non-zero.");
|
||||
if (maxFileBytes == 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(maxFileBytes), "Must be non-zero.");
|
||||
if (maxLines == 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(maxLines), "Must be non-zero.");
|
||||
|
||||
MaxCol = maxCol;
|
||||
MaxFileBytes = maxFileBytes;
|
||||
MaxLines = maxLines;
|
||||
}
|
||||
|
||||
/// <summary>Maximum column width per line (default: 1024).</summary>
|
||||
public uint MaxCol { get; }
|
||||
|
||||
/// <summary>Maximum policy file size in bytes (default: 1 MiB).</summary>
|
||||
public nuint MaxFileBytes { get; }
|
||||
|
||||
/// <summary>Maximum number of lines per policy file (default: 20000).</summary>
|
||||
public nuint MaxLines { get; }
|
||||
|
||||
internal Regorus.Internal.RegorusPolicyLengthConfig ToNative()
|
||||
{
|
||||
return new Regorus.Internal.RegorusPolicyLengthConfig
|
||||
{
|
||||
max_col = MaxCol,
|
||||
max_file_bytes = MaxFileBytes,
|
||||
max_lines = MaxLines,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@ w.Restart();
|
||||
|
||||
var engine = new Regorus.Engine();
|
||||
engine.SetRegoV0(true);
|
||||
// Raise the default col limit to 2000
|
||||
engine.SetPolicyLengthConfig(new Regorus.PolicyLengthConfig(maxCol: 2000, maxFileBytes: 1048576, maxLines: 20000));
|
||||
|
||||
w.Stop();
|
||||
var newEngineTicks = w.ElapsedTicks;
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::common::{
|
||||
};
|
||||
use crate::compiled_policy::RegorusCompiledPolicy;
|
||||
use crate::limits::RegorusExecutionTimerConfig;
|
||||
use crate::limits::RegorusPolicyLengthConfig;
|
||||
use crate::lock::{new_handle, read, try_read, try_write, Handle, ReadGuard, WriteGuard};
|
||||
use crate::panic_guard::with_unwind_guard;
|
||||
use alloc::boxed::Box;
|
||||
@@ -491,6 +492,37 @@ pub extern "C" fn regorus_engine_clear_execution_timer_config(
|
||||
}())
|
||||
}
|
||||
|
||||
/// Set the policy length limits used when loading policies.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_set_policy_length_config(
|
||||
engine: *mut RegorusEngine,
|
||||
config: RegorusPolicyLengthConfig,
|
||||
) -> RegorusResult {
|
||||
with_unwind_guard(|| {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.set_policy_length_config(config.to_policy_length_config()?);
|
||||
Ok(())
|
||||
}())
|
||||
})
|
||||
}
|
||||
|
||||
/// Clear the policy length configuration, reverting to defaults.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_clear_policy_length_config(
|
||||
engine: *mut RegorusEngine,
|
||||
) -> RegorusResult {
|
||||
with_unwind_guard(|| {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.clear_policy_length_config();
|
||||
Ok(())
|
||||
}())
|
||||
})
|
||||
}
|
||||
|
||||
/// Get pretty printed coverage report.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Report.html#method.to_string_pretty
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use crate::common::{to_regorus_result, RegorusResult, RegorusStatus};
|
||||
use alloc::format;
|
||||
use anyhow::{anyhow, Result};
|
||||
use core::num::NonZeroU32;
|
||||
use core::num::{NonZeroU32, NonZeroUsize};
|
||||
use core::time::Duration;
|
||||
use regorus::utils::limits::{self, ExecutionTimerConfig};
|
||||
|
||||
@@ -158,6 +158,31 @@ impl RegorusExecutionTimerConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// FFI representation of [`regorus::PolicyLengthConfig`].
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RegorusPolicyLengthConfig {
|
||||
/// Maximum column width per line (must be non-zero).
|
||||
pub max_col: u32,
|
||||
/// Maximum policy file size in bytes (must be non-zero).
|
||||
pub max_file_bytes: usize,
|
||||
/// Maximum number of lines per policy file (must be non-zero).
|
||||
pub max_lines: usize,
|
||||
}
|
||||
|
||||
impl RegorusPolicyLengthConfig {
|
||||
pub fn to_policy_length_config(self) -> Result<regorus::PolicyLengthConfig> {
|
||||
Ok(regorus::PolicyLengthConfig {
|
||||
max_col: NonZeroU32::new(self.max_col)
|
||||
.ok_or_else(|| anyhow!("max_col must be non-zero"))?,
|
||||
max_file_bytes: NonZeroUsize::new(self.max_file_bytes)
|
||||
.ok_or_else(|| anyhow!("max_file_bytes must be non-zero"))?,
|
||||
max_lines: NonZeroUsize::new(self.max_lines)
|
||||
.ok_or_else(|| anyhow!("max_lines must be non-zero"))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_set_fallback_execution_timer_config(
|
||||
config: RegorusExecutionTimerConfig,
|
||||
|
||||
@@ -18,6 +18,8 @@ func main() {
|
||||
defer engine.Close()
|
||||
|
||||
engine.SetRegoV0(true)
|
||||
// Raise the default col limit to 2000
|
||||
engine.SetPolicyLengthConfig(regorus.PolicyLengthConfig{MaxCol: 2000, MaxFileBytes: 1048576, MaxLines: 20000})
|
||||
elapsed1 := time.Since(t)
|
||||
|
||||
|
||||
|
||||
@@ -214,3 +214,32 @@ func (e *Engine) TakePrints() (string, error) {
|
||||
|
||||
return C.GoString(result.output), nil
|
||||
}
|
||||
|
||||
type PolicyLengthConfig struct {
|
||||
MaxCol uint32
|
||||
MaxFileBytes uint
|
||||
MaxLines uint
|
||||
}
|
||||
|
||||
func (e *Engine) SetPolicyLengthConfig(config PolicyLengthConfig) error {
|
||||
c := C.RegorusPolicyLengthConfig{
|
||||
max_col: C.uint32_t(config.MaxCol),
|
||||
max_file_bytes: C.size_t(config.MaxFileBytes),
|
||||
max_lines: C.size_t(config.MaxLines),
|
||||
}
|
||||
result := C.regorus_engine_set_policy_length_config(e.e, c)
|
||||
defer C.regorus_result_drop(result)
|
||||
if result.status != C.Ok {
|
||||
return fmt.Errorf("%s", C.GoString(result.error_message))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Engine) ClearPolicyLengthConfig() error {
|
||||
result := C.regorus_engine_clear_policy_length_config(e.e)
|
||||
defer C.regorus_result_drop(result)
|
||||
if result.status != C.Ok {
|
||||
return fmt.Errorf("%s", C.GoString(result.error_message))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import com.microsoft.regorus.Engine;
|
||||
import com.microsoft.regorus.PolicyLengthConfig;
|
||||
import com.microsoft.regorus.PolicyModule;
|
||||
import com.microsoft.regorus.Program;
|
||||
import com.microsoft.regorus.Rvm;
|
||||
@@ -26,6 +27,9 @@ public class Test {
|
||||
// Enable coverage.
|
||||
engine.setEnableCoverage(true);
|
||||
|
||||
// Raise the default col limit to 2000
|
||||
engine.setPolicyLengthConfig(new PolicyLengthConfig(2000, 1048576, 20000));
|
||||
|
||||
// Evaluate rule.
|
||||
String valueJson = engine.evalRule("data.test.message");
|
||||
System.out.println(valueJson);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use anyhow::Result;
|
||||
use core::num::{NonZeroU32, NonZeroUsize};
|
||||
use jni::objects::{JBooleanArray, JByteArray, JClass, JObject, JObjectArray, JString};
|
||||
use jni::sys::{jboolean, jbooleanArray, jbyteArray, jlong, jobjectArray, jstring};
|
||||
use jni::JNIEnv;
|
||||
@@ -365,6 +366,39 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_getAstAsJson(
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetPolicyLengthConfig(
|
||||
env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
max_col: u32,
|
||||
max_file_bytes: jlong,
|
||||
max_lines: jlong,
|
||||
) {
|
||||
let _ = throw_err(env, |_env| {
|
||||
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
|
||||
engine.set_policy_length_config(regorus::PolicyLengthConfig {
|
||||
max_col: NonZeroU32::new(max_col)
|
||||
.ok_or_else(|| anyhow::anyhow!("maxCol must be non-zero"))?,
|
||||
max_file_bytes: NonZeroUsize::new(max_file_bytes as usize)
|
||||
.ok_or_else(|| anyhow::anyhow!("maxFileBytes must be non-zero"))?,
|
||||
max_lines: NonZeroUsize::new(max_lines as usize)
|
||||
.ok_or_else(|| anyhow::anyhow!("maxLines must be non-zero"))?,
|
||||
});
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeClearPolicyLengthConfig(
|
||||
_env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
) {
|
||||
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
|
||||
engine.clear_policy_length_config();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeDestroyEngine(
|
||||
_env: JNIEnv,
|
||||
|
||||
@@ -39,6 +39,8 @@ public class Engine implements AutoCloseable, Cloneable {
|
||||
private static native void nativeClearCoverageData(long enginePtr);
|
||||
private static native void nativeSetGatherPrints(long enginePtr, boolean b);
|
||||
private static native String nativeTakePrints(long enginePtr);
|
||||
private static native void nativeSetPolicyLengthConfig(long enginePtr, int maxCol, long maxFileBytes, long maxLines);
|
||||
private static native void nativeClearPolicyLengthConfig(long enginePtr);
|
||||
private static native void nativeDestroyEngine(long enginePtr);
|
||||
|
||||
// Pointer to Engine allocated on Rust's heap, all native methods works on
|
||||
@@ -259,6 +261,22 @@ public class Engine implements AutoCloseable, Cloneable {
|
||||
return nativeTakePrints(enginePtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the policy length limits used when loading policies.
|
||||
*
|
||||
* @param config Policy length configuration.
|
||||
*/
|
||||
public void setPolicyLengthConfig(PolicyLengthConfig config) {
|
||||
nativeSetPolicyLengthConfig(enginePtr, config.maxCol, config.maxFileBytes, config.maxLines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the policy length configuration, reverting to defaults.
|
||||
*/
|
||||
public void clearPolicyLengthConfig() {
|
||||
nativeClearPolicyLengthConfig(enginePtr);
|
||||
}
|
||||
|
||||
long getPtr() {
|
||||
return enginePtr;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
* Licensed under the MIT License.
|
||||
**/
|
||||
|
||||
package com.microsoft.regorus;
|
||||
|
||||
/**
|
||||
* Policy source length limits enforced when loading policy files.
|
||||
*
|
||||
* All values must be positive (non-zero).
|
||||
*/
|
||||
public final class PolicyLengthConfig {
|
||||
/**
|
||||
* Maximum column width per line (default: 1024).
|
||||
*/
|
||||
public final int maxCol;
|
||||
|
||||
/**
|
||||
* Maximum policy file size in bytes (default: 1 MiB).
|
||||
*/
|
||||
public final long maxFileBytes;
|
||||
|
||||
/**
|
||||
* Maximum number of lines per policy file (default: 20000).
|
||||
*/
|
||||
public final long maxLines;
|
||||
|
||||
/**
|
||||
* Create a new policy length configuration.
|
||||
*
|
||||
* @param maxCol Maximum column width per line.
|
||||
* @param maxFileBytes Maximum policy file size in bytes.
|
||||
* @param maxLines Maximum number of lines per policy file.
|
||||
*/
|
||||
public PolicyLengthConfig(int maxCol, long maxFileBytes, long maxLines) {
|
||||
if (maxCol <= 0) {
|
||||
throw new IllegalArgumentException("maxCol must be positive");
|
||||
}
|
||||
if (maxFileBytes <= 0) {
|
||||
throw new IllegalArgumentException("maxFileBytes must be positive");
|
||||
}
|
||||
if (maxLines <= 0) {
|
||||
throw new IllegalArgumentException("maxLines must be positive");
|
||||
}
|
||||
this.maxCol = maxCol;
|
||||
this.maxFileBytes = maxFileBytes;
|
||||
this.maxLines = maxLines;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
use anyhow::{anyhow, Result};
|
||||
use core::num::{NonZeroU32, NonZeroUsize};
|
||||
use pyo3::exceptions::PyTypeError;
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::*;
|
||||
@@ -392,6 +393,35 @@ impl Engine {
|
||||
.add_extension(path, nargs, Box::new(extension_impl))
|
||||
}
|
||||
|
||||
/// Set the policy length limits used when loading policies.
|
||||
///
|
||||
/// * `max_col`: Maximum column width per line.
|
||||
/// * `max_file_bytes`: Maximum policy file size in bytes.
|
||||
/// * `max_lines`: Maximum number of lines per policy file.
|
||||
#[pyo3(signature = (*, max_col, max_file_bytes, max_lines))]
|
||||
pub fn set_policy_length_config(
|
||||
&mut self,
|
||||
max_col: u32,
|
||||
max_file_bytes: usize,
|
||||
max_lines: usize,
|
||||
) -> Result<()> {
|
||||
self.engine
|
||||
.set_policy_length_config(::regorus::PolicyLengthConfig {
|
||||
max_col: NonZeroU32::new(max_col)
|
||||
.ok_or_else(|| anyhow!("max_col must be non-zero"))?,
|
||||
max_file_bytes: NonZeroUsize::new(max_file_bytes)
|
||||
.ok_or_else(|| anyhow!("max_file_bytes must be non-zero"))?,
|
||||
max_lines: NonZeroUsize::new(max_lines)
|
||||
.ok_or_else(|| anyhow!("max_lines must be non-zero"))?,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clear the policy length configuration, reverting to defaults.
|
||||
pub fn clear_policy_length_config(&mut self) {
|
||||
self.engine.clear_policy_length_config();
|
||||
}
|
||||
|
||||
/// Enable code coverage
|
||||
///
|
||||
/// * `enable`: Whether to enable coverage or not.
|
||||
|
||||
@@ -10,6 +10,8 @@ if hasattr(sys.stdout, "reconfigure"):
|
||||
engine = regorus.Engine()
|
||||
|
||||
engine.set_rego_v0(True)
|
||||
# Raise the default col limit to 2000
|
||||
engine.set_policy_length_config(max_col=2000, max_file_bytes=1048576, max_lines=20000)
|
||||
|
||||
# Load policies
|
||||
pkg = engine.add_policy_from_file('../../tests/aci/framework.rego')
|
||||
|
||||
1
bindings/ruby/Cargo.lock
generated
1
bindings/ruby/Cargo.lock
generated
@@ -962,6 +962,7 @@ version = "0.9.1"
|
||||
dependencies = [
|
||||
"magnus",
|
||||
"regorus",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_magnus",
|
||||
]
|
||||
|
||||
@@ -18,5 +18,6 @@ coverage = ["regorus/coverage"]
|
||||
[dependencies]
|
||||
magnus = { version = "0.8.2" }
|
||||
regorus = { path = "../../../..", default-features = false, features = ["arc"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1.0.140"
|
||||
serde_magnus = "0.11.0"
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
use core::num::{NonZeroU32, NonZeroUsize};
|
||||
use magnus::{Error, Ruby, exception::runtime_error, method, module, prelude::*};
|
||||
use regorus::Engine as RegorusEngine;
|
||||
use serde::Deserialize;
|
||||
use std::cell::RefCell;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
// `Value` exists under magnus, regorus, and serde_json, so be explicit
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct PolicyLengthSpec {
|
||||
max_col: u32,
|
||||
max_file_bytes: usize,
|
||||
max_lines: usize,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[magnus::wrap(class = "Regorus::Engine")]
|
||||
pub struct Engine {
|
||||
@@ -55,15 +64,17 @@ impl Engine {
|
||||
.map_err(|e| Error::new(runtime_error(), format!("Failed to add policy: {e}")))
|
||||
}
|
||||
|
||||
fn add_data(&self, ruby_hash: magnus::RHash) -> Result<(), Error> {
|
||||
let data_value: regorus::Value = serde_magnus::deserialize(ruby_hash).map_err(|e| {
|
||||
Error::new(
|
||||
runtime_error(),
|
||||
format!("Failed to deserialize Ruby value: {e}"),
|
||||
)
|
||||
})?;
|
||||
fn add_data(ruby: &Ruby, rb_self: &Self, ruby_hash: magnus::RHash) -> Result<(), Error> {
|
||||
let data_value: regorus::Value =
|
||||
serde_magnus::deserialize(ruby, ruby_hash).map_err(|e| {
|
||||
Error::new(
|
||||
runtime_error(),
|
||||
format!("Failed to deserialize Ruby value: {e}"),
|
||||
)
|
||||
})?;
|
||||
|
||||
self.engine
|
||||
rb_self
|
||||
.engine
|
||||
.borrow_mut()
|
||||
.add_data(data_value)
|
||||
.map_err(|e| Error::new(runtime_error(), format!("Failed to add data: {e}")))
|
||||
@@ -111,15 +122,16 @@ impl Engine {
|
||||
.map_err(|e| Error::new(runtime_error(), format!("Failed to get policies: {e}")))
|
||||
}
|
||||
|
||||
fn set_input(&self, ruby_hash: magnus::RHash) -> Result<(), Error> {
|
||||
let input_value: regorus::Value = serde_magnus::deserialize(ruby_hash).map_err(|e| {
|
||||
Error::new(
|
||||
runtime_error(),
|
||||
format!("Failed to deserialize Ruby value: {e}"),
|
||||
)
|
||||
})?;
|
||||
fn set_input(ruby: &Ruby, rb_self: &Self, ruby_hash: magnus::RHash) -> Result<(), Error> {
|
||||
let input_value: regorus::Value =
|
||||
serde_magnus::deserialize(ruby, ruby_hash).map_err(|e| {
|
||||
Error::new(
|
||||
runtime_error(),
|
||||
format!("Failed to deserialize Ruby value: {e}"),
|
||||
)
|
||||
})?;
|
||||
|
||||
self.engine.borrow_mut().set_input(input_value);
|
||||
rb_self.engine.borrow_mut().set_input(input_value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -142,14 +154,14 @@ impl Engine {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn eval_query(&self, query: String) -> Result<magnus::Value, Error> {
|
||||
let results = self
|
||||
fn eval_query(ruby: &Ruby, rb_self: &Self, query: String) -> Result<magnus::Value, Error> {
|
||||
let results = rb_self
|
||||
.engine
|
||||
.borrow_mut()
|
||||
.eval_query(query, false)
|
||||
.map_err(|e| Error::new(runtime_error(), format!("Failed to evaluate query: {e}")))?;
|
||||
|
||||
serde_magnus::serialize(&results).map_err(|e| {
|
||||
serde_magnus::serialize(ruby, &results).map_err(|e| {
|
||||
Error::new(
|
||||
runtime_error(),
|
||||
format!("Failed to serailzie query results: {e}"),
|
||||
@@ -177,15 +189,19 @@ impl Engine {
|
||||
})
|
||||
}
|
||||
|
||||
fn eval_rule(&self, query: String) -> Result<Option<magnus::Value>, Error> {
|
||||
fn eval_rule(
|
||||
ruby: &Ruby,
|
||||
rb_self: &Self,
|
||||
query: String,
|
||||
) -> Result<Option<magnus::Value>, Error> {
|
||||
let result =
|
||||
self.engine.borrow_mut().eval_rule(query).map_err(|e| {
|
||||
rb_self.engine.borrow_mut().eval_rule(query).map_err(|e| {
|
||||
Error::new(runtime_error(), format!("Failed to evaluate rule: {e}"))
|
||||
})?;
|
||||
|
||||
match result {
|
||||
regorus::Value::Undefined => Ok(None), // Convert undefined to Ruby's nil
|
||||
_ => serde_magnus::serialize(&result) // Serialize other results normally
|
||||
regorus::Value::Undefined => Ok(None),
|
||||
_ => serde_magnus::serialize(ruby, &result)
|
||||
.map(Some)
|
||||
.map_err(|e| {
|
||||
magnus::Error::new(
|
||||
@@ -280,6 +296,34 @@ impl Engine {
|
||||
})
|
||||
}
|
||||
|
||||
fn set_policy_length_config(
|
||||
ruby: &Ruby,
|
||||
rb_self: &Self,
|
||||
hash: magnus::RHash,
|
||||
) -> Result<(), Error> {
|
||||
let spec: PolicyLengthSpec = serde_magnus::deserialize(ruby, hash).map_err(|e| {
|
||||
Error::new(
|
||||
runtime_error(),
|
||||
format!("Failed to deserialize policy length config: {e}"),
|
||||
)
|
||||
})?;
|
||||
let config = regorus::PolicyLengthConfig {
|
||||
max_col: NonZeroU32::new(spec.max_col)
|
||||
.ok_or_else(|| Error::new(runtime_error(), "max_col must be non-zero"))?,
|
||||
max_file_bytes: NonZeroUsize::new(spec.max_file_bytes)
|
||||
.ok_or_else(|| Error::new(runtime_error(), "max_file_bytes must be non-zero"))?,
|
||||
max_lines: NonZeroUsize::new(spec.max_lines)
|
||||
.ok_or_else(|| Error::new(runtime_error(), "max_lines must be non-zero"))?,
|
||||
};
|
||||
rb_self.engine.borrow_mut().set_policy_length_config(config);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clear_policy_length_config(&self) -> Result<(), Error> {
|
||||
self.engine.borrow_mut().clear_policy_length_config();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ast")]
|
||||
fn get_ast_as_json(&self) -> Result<String, Error> {
|
||||
self.engine
|
||||
@@ -361,6 +405,16 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
||||
engine_class.define_method("set_gather_prints", method!(Engine::set_gather_prints, 1))?;
|
||||
engine_class.define_method("take_prints", method!(Engine::take_prints, 0))?;
|
||||
|
||||
// policy length limits
|
||||
engine_class.define_method(
|
||||
"set_policy_length_config",
|
||||
method!(Engine::set_policy_length_config, 1),
|
||||
)?;
|
||||
engine_class.define_method(
|
||||
"clear_policy_length_config",
|
||||
method!(Engine::clear_policy_length_config, 0),
|
||||
)?;
|
||||
|
||||
// ast
|
||||
engine_class.define_method("get_ast_as_json", method!(Engine::get_ast_as_json, 0))?;
|
||||
Ok(())
|
||||
|
||||
@@ -183,6 +183,11 @@ class TestRegorus < Minitest::Test
|
||||
assert_equal ["<query.rego>:1: Hello"], @engine.take_prints
|
||||
end
|
||||
|
||||
def test_set_policy_length_config
|
||||
@engine.set_policy_length_config({ max_col: 2000, max_file_bytes: 1048576, max_lines: 20000 })
|
||||
@engine.clear_policy_length_config
|
||||
end
|
||||
|
||||
def alice_results
|
||||
{
|
||||
result: [
|
||||
|
||||
12
bindings/wasm/Cargo.lock
generated
12
bindings/wasm/Cargo.lock
generated
@@ -941,6 +941,7 @@ dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"regorus",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"serde_json",
|
||||
"uuid",
|
||||
"wasm-bindgen",
|
||||
@@ -990,6 +991,17 @@ dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-wasm-bindgen"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
|
||||
@@ -42,6 +42,7 @@ regorus = { path = "../..", default-features = false, features = ["arc", "rvm"]
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_json = "1.0.140"
|
||||
wasm-bindgen = "0.2.100"
|
||||
serde-wasm-bindgen = "0.6"
|
||||
# Specify uuid as a mandatory dependency so as to enable `js` feature which is now required
|
||||
# when targeting wasm32-unknown-unknown.
|
||||
uuid = { version = "1.15.1", default-features = false, features = ["v4", "fast-rng", "js"]}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use core::num::{NonZeroU32, NonZeroUsize};
|
||||
use regorus::languages::rego::compiler::Compiler;
|
||||
use regorus::rvm::program::{
|
||||
generate_assembly_listing, generate_tabular_assembly_listing, AssemblyListingConfig,
|
||||
@@ -26,6 +27,14 @@ struct ModuleSpec {
|
||||
content: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct PolicyLengthSpec {
|
||||
max_col: u32,
|
||||
max_file_bytes: usize,
|
||||
max_lines: usize,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct Program {
|
||||
program: Arc<RvmProgram>,
|
||||
@@ -183,6 +192,29 @@ impl Engine {
|
||||
self.engine.set_gather_prints(b)
|
||||
}
|
||||
|
||||
/// Set the policy length limits used when loading policies.
|
||||
///
|
||||
/// Accepts a JS object: `{ maxCol, maxFileBytes, maxLines }`.
|
||||
pub fn setPolicyLengthConfig(&mut self, config: JsValue) -> Result<(), JsValue> {
|
||||
let spec: PolicyLengthSpec =
|
||||
serde_wasm_bindgen::from_value(config).map_err(error_to_jsvalue)?;
|
||||
self.engine
|
||||
.set_policy_length_config(regorus::PolicyLengthConfig {
|
||||
max_col: NonZeroU32::new(spec.max_col)
|
||||
.ok_or_else(|| JsValue::from_str("maxCol must be non-zero"))?,
|
||||
max_file_bytes: NonZeroUsize::new(spec.max_file_bytes)
|
||||
.ok_or_else(|| JsValue::from_str("maxFileBytes must be non-zero"))?,
|
||||
max_lines: NonZeroUsize::new(spec.max_lines)
|
||||
.ok_or_else(|| JsValue::from_str("maxLines must be non-zero"))?,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clear the policy length configuration, reverting to defaults.
|
||||
pub fn clearPolicyLengthConfig(&mut self) {
|
||||
self.engine.clear_policy_length_config();
|
||||
}
|
||||
|
||||
/// Take the gathered output of print statements.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.take_prints
|
||||
|
||||
@@ -9,6 +9,9 @@ var engine = new regorus.Engine();
|
||||
// Enable code coverage
|
||||
engine.setEnableCoverage(true);
|
||||
|
||||
// Raise the default col limit to 2000
|
||||
engine.setPolicyLengthConfig({ maxCol: 2000, maxFileBytes: 1048576, maxLines: 20000 });
|
||||
|
||||
// Add Rego policy.
|
||||
var pkg = engine.addPolicy(
|
||||
// Associate this file name with policy
|
||||
|
||||
Reference in New Issue
Block a user