mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
Compare commits
14 Commits
regorus-v0
...
regorus-v0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2901481c51 | ||
|
|
c963e477a3 | ||
|
|
cbd772623a | ||
|
|
a07beca983 | ||
|
|
a3edb6c88c | ||
|
|
a1777fb7d3 | ||
|
|
11aaa555aa | ||
|
|
f1580a55a3 | ||
|
|
6174af1781 | ||
|
|
5fa55d7274 | ||
|
|
748c11cfa1 | ||
|
|
fb035d3d93 | ||
|
|
ba3a128e84 | ||
|
|
d955ae10a5 |
6
.github/workflows/test-ruby.yml
vendored
6
.github/workflows/test-ruby.yml
vendored
@@ -18,7 +18,9 @@ jobs:
|
||||
- name: Setup Ruby and Rust
|
||||
uses: oxidize-rb/actions/setup-ruby-and-rust@7ca44a16e287e5ff7dd72ab53f4bd41cbf34a571 #v1.26
|
||||
with:
|
||||
ruby-version: "3.3.1"
|
||||
bundler: 2.6.5
|
||||
rubygems: 3.6.5
|
||||
ruby-version: "3.4.2"
|
||||
rustup-toolchain: "stable"
|
||||
bundler-cache: true
|
||||
cargo-cache: true
|
||||
@@ -27,5 +29,7 @@ jobs:
|
||||
- name: Run ruby tests
|
||||
run: |
|
||||
cd bindings/ruby
|
||||
gem install bundler
|
||||
bundle install
|
||||
cargo clippy --all-targets --no-deps -- -Dwarnings
|
||||
bundle exec rake
|
||||
|
||||
4
.github/workflows/test-wasm.yml
vendored
4
.github/workflows/test-wasm.yml
vendored
@@ -28,5 +28,7 @@ jobs:
|
||||
cd bindings/wasm
|
||||
cargo clippy --all-targets --no-deps -- -Dwarnings
|
||||
wasm-pack build --target nodejs --release
|
||||
wasm-pack test --release --node
|
||||
# Enable when upstream issue is fixed.
|
||||
# https://github.com/microsoft/regorus/issues/371
|
||||
# wasm-pack test --release --node
|
||||
node test.js
|
||||
|
||||
19
CHANGELOG.md
19
CHANGELOG.md
@@ -6,6 +6,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.3.0](https://github.com/microsoft/regorus/compare/regorus-v0.2.8...regorus-v0.3.0) - 2025-03-10
|
||||
|
||||
### Added
|
||||
- [**breaking**] Update to OPA v1.2.0 ([#373](https://github.com/microsoft/regorus/pull/373))
|
||||
|
||||
### Other
|
||||
- *(deps)* update pyo3 requirement from 0.23.5 to 0.24.0 ([#375](https://github.com/microsoft/regorus/pull/375))
|
||||
- Update ruby binding deps, ruby gem version 0.2.3 ([#374](https://github.com/microsoft/regorus/pull/374))
|
||||
- *(deps)* update pyo3 requirement from 0.22.0 to 0.23.5 ([#372](https://github.com/microsoft/regorus/pull/372))
|
||||
- *(deps)* update rand requirement from 0.8.5 to 0.9.0 ([#370](https://github.com/microsoft/regorus/pull/370))
|
||||
- *(deps)* update cbindgen requirement from 0.27.0 to 0.28.0 ([#361](https://github.com/microsoft/regorus/pull/361))
|
||||
- Fix typo in README.md ([#366](https://github.com/microsoft/regorus/pull/366))
|
||||
- Update dependencies ([#369](https://github.com/microsoft/regorus/pull/369))
|
||||
- Fix clippy warning for result? ([#362](https://github.com/microsoft/regorus/pull/362))
|
||||
- *(deps)* update itertools requirement from 0.13.0 to 0.14.0 ([#357](https://github.com/microsoft/regorus/pull/357))
|
||||
- *(deps)* update jsonschema requirement from 0.26.1 to 0.28.1 ([#356](https://github.com/microsoft/regorus/pull/356))
|
||||
- resolve anyhow compile errors ([#355](https://github.com/microsoft/regorus/pull/355))
|
||||
- *(deps)* update prettydiff requirement from 0.7.0 to 0.8.0 ([#348](https://github.com/microsoft/regorus/pull/348))
|
||||
|
||||
## [0.2.8](https://github.com/microsoft/regorus/compare/regorus-v0.2.7...regorus-v0.2.8) - 2024-11-06
|
||||
|
||||
### Other
|
||||
|
||||
31
Cargo.toml
31
Cargo.toml
@@ -12,7 +12,7 @@ members = [
|
||||
[package]
|
||||
name = "regorus"
|
||||
description = "A fast, lightweight Rego (OPA policy language) interpreter"
|
||||
version = "0.2.8"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
license-file = "LICENSE"
|
||||
repository = "https://github.com/microsoft/regorus"
|
||||
@@ -105,35 +105,36 @@ serde_json = { version = "1.0.89", default-features = false, features = ["alloc"
|
||||
lazy_static = { version = "1.4.0", default-features = false }
|
||||
|
||||
# Crypto
|
||||
constant_time_eq = {version = "0.3.0", optional = true, default-features = false }
|
||||
constant_time_eq = {version = "0.4.0", optional = true, default-features = false }
|
||||
hmac = {version = "0.12.1", optional = true, default-features = false}
|
||||
sha2 = {version= "0.10.8", optional = true, default-features = false }
|
||||
hex = {version = "0.4.3", optional = true, default-features = false, features = ["alloc"] }
|
||||
sha1 = {version = "0.10.6", optional = true, default-features = false }
|
||||
md-5 = {version = "0.10.6", optional = true, default-features = false }
|
||||
|
||||
data-encoding = { version = "2.4.0", optional = true, default-features=false, features = ["alloc"] }
|
||||
scientific = { version = "0.5.2" }
|
||||
data-encoding = { version = "2.8.0", optional = true, default-features=false, features = ["alloc"] }
|
||||
scientific = { version = "0.5.3" }
|
||||
|
||||
regex = {version = "1.10.2", optional = true, default-features = false }
|
||||
semver = {version = "1.0.20", optional = true, default-features = false }
|
||||
regex = {version = "1.11.1", optional = true, default-features = false }
|
||||
semver = {version = "1.0.25", optional = true, default-features = false }
|
||||
wax = { version = "0.6.0", features = [], default-features = false, optional = true }
|
||||
url = { version = "2.5.0", optional = true }
|
||||
uuid = { version = "1.6.1", default-features = false, features = ["v4", "fast-rng"], optional = true }
|
||||
jsonschema = { version = "0.26.1", default-features = false, optional = true }
|
||||
chrono = { version = "0.4.31", optional = true }
|
||||
chrono-tz = { version = "0.10.0", optional = true }
|
||||
jsonwebtoken = { version = "9.2.0", optional = true }
|
||||
itertools = { version = "0.13.0", default-features = false, optional = true }
|
||||
url = { version = "2.5.4", optional = true }
|
||||
uuid = { version = "1.15.1", default-features = false, features = ["v4", "fast-rng"], optional = true }
|
||||
jsonschema = { version = "0.29.0", default-features = false, optional = true }
|
||||
chrono = { version = "0.4.40", optional = true }
|
||||
chrono-tz = { version = "0.10.1", optional = true }
|
||||
jsonwebtoken = { version = "9.3.1", optional = true }
|
||||
itertools = { version = "0.14.0", default-features = false, optional = true }
|
||||
|
||||
serde_yaml = {version = "0.9.16", default-features = false, optional = true }
|
||||
rand = { version = "0.8.5", default-features = false, optional = true }
|
||||
# Specify thread_rng for in order to use random_range
|
||||
rand = { version = "0.9.0", default-features = false, features = ["thread_rng"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.45"
|
||||
cfg-if = "1.0.0"
|
||||
clap = { version = "4.4.7", features = ["derive"] }
|
||||
prettydiff = { version = "0.7.0", default-features = false }
|
||||
prettydiff = { version = "0.8.0", default-features = false }
|
||||
serde_yaml = "0.9.16"
|
||||
test-generator = "0.3.1"
|
||||
walkdir = "2.3.2"
|
||||
|
||||
11
README.md
11
README.md
@@ -9,8 +9,8 @@
|
||||
Regorus is also
|
||||
- *cross-platform* - Written in platform-agnostic Rust.
|
||||
- *no_std compatible* - Regorus can be used in `no_std` environments too. Most of the builtins are supported.
|
||||
- *current* - We strive to keep Regorus up to date with latest OPA release. Regorus supports `import rego.v1`.
|
||||
- *compliant* - Regorus is mostly compliant with the latest [OPA release v0.70.0](https://github.com/open-policy-agent/opa/releases/tag/v0.70.0). See [OPA Conformance](#opa-conformance) for details. Note that while we behaviorally produce the same results, we don't yet support all the builtins.
|
||||
- *current* - We strive to keep Regorus up to date with latest OPA release. Regorus defaults to `v1` of the Rego language.
|
||||
- *compliant* - Regorus is mostly compliant with the latest [OPA release v1.2.0](https://github.com/open-policy-agent/opa/releases/tag/v1.2.0). See [OPA Conformance](#opa-conformance) for details. Note that while we behaviorally produce the same results, we don't yet support all the builtins.
|
||||
- *extensible* - Extend the Rego language by implementing custom stateful builtins in Rust.
|
||||
See [add_extension](https://github.com/microsoft/regorus/blob/fc68bf9c8bea36427dae9401a7d1f6ada771f7ab/src/engine.rs#L352).
|
||||
Support for extensibility using other languages coming soon.
|
||||
@@ -32,7 +32,6 @@ fn main() -> anyhow::Result<()> {
|
||||
let policy = String::from(
|
||||
r#"
|
||||
package example
|
||||
import rego.v1
|
||||
|
||||
allow if {
|
||||
## All actions are allowed for admins.
|
||||
@@ -99,7 +98,7 @@ $ cargo build -r --example regorus --no-default-features; strip target/release/e
|
||||
-rwxr-xr-x 1 anand staff 1.9M May 11 22:04 target/release/examples/regorus*
|
||||
```
|
||||
|
||||
Regorus passes the [OPA v0.70.0 test-suite](https://www.openpolicyagent.org/docs/latest/ir/#test-suite) barring a few
|
||||
Regorus passes the [OPA v1.2.0 test-suite](https://www.openpolicyagent.org/docs/latest/ir/#test-suite) barring a few
|
||||
builtins. See [OPA Conformance](#opa-conformance) below.
|
||||
|
||||
## Bindings
|
||||
@@ -276,7 +275,7 @@ Benchmark 1: opa eval -b tests/aci -d tests/aci/data.json -i tests/aci/input.jso
|
||||
```
|
||||
## OPA Conformance
|
||||
|
||||
Regorus has been verified to be compliant with [OPA v0.70.0](https://github.com/open-policy-agent/opa/releases/tag/v0.70.0)
|
||||
Regorus has been verified to be compliant with [OPA v1.2.0](https://github.com/open-policy-agent/opa/releases/tag/v1.2.0)
|
||||
using a [test driver](https://github.com/microsoft/regorus/blob/main/tests/opa.rs) that loads and runs the OPA testsuite using Regorus, and verifies that expected outputs are produced.
|
||||
|
||||
The test driver can be invoked by running:
|
||||
@@ -288,7 +287,7 @@ $ cargo test -r --test opa --features opa-testutil,serde_json/arbitrary_precisio
|
||||
Currently, Regorus passes all the non-builtin specific tests.
|
||||
See [passing tests suites](https://github.com/microsoft/regorus/blob/main/tests/opa.passing).
|
||||
|
||||
The following test suites don't pass fully due to mising builtins:
|
||||
The following test suites don't pass fully due to missing builtins:
|
||||
- `cryptoparsersaprivatekeys`
|
||||
- `cryptox509parseandverifycertificates`
|
||||
- `cryptox509parsecertificaterequest`
|
||||
|
||||
@@ -27,7 +27,7 @@ char* file_to_string(const char* file) {
|
||||
|
||||
// If regorus is built with custom-allocator, then provide implementation.
|
||||
uint8_t* regorus_aligned_alloc(size_t alignment, size_t size) {
|
||||
return aligned_alloc(alignment, size);
|
||||
return (uint8_t*) aligned_alloc(alignment, size);
|
||||
}
|
||||
|
||||
void regorus_free(uint8_t* ptr) {
|
||||
@@ -41,6 +41,11 @@ int main() {
|
||||
RegorusResult r;
|
||||
char* buffer = NULL;
|
||||
|
||||
// Turn on rego v0 since policy uses v0.
|
||||
r = regorus_engine_set_rego_v0(engine, true);
|
||||
if (r.status != RegorusStatusOk)
|
||||
goto error;
|
||||
|
||||
// Load policies.
|
||||
r = regorus_engine_add_policy(engine, "framework.rego", (buffer = file_to_string("../../../tests/aci/framework.rego")));
|
||||
free(buffer);
|
||||
|
||||
@@ -6,6 +6,11 @@ int main() {
|
||||
RegorusEngine* engine = regorus_engine_new();
|
||||
RegorusResult r;
|
||||
|
||||
// Turn on rego v0 since policy uses v0.
|
||||
r = regorus_engine_set_rego_v0(engine, true);
|
||||
if (r.status != RegorusStatusOk)
|
||||
goto error;
|
||||
|
||||
// Load policies.
|
||||
r = regorus_engine_add_policy_from_file(engine, "../../../tests/aci/framework.rego");
|
||||
if (r.status != RegorusStatusOk)
|
||||
|
||||
@@ -6,6 +6,7 @@ void example()
|
||||
// Create engine
|
||||
regorus::Engine engine;
|
||||
|
||||
engine.set_rego_v0(true);
|
||||
engine.set_enable_coverage(true);
|
||||
|
||||
// Add policies.
|
||||
@@ -83,6 +84,7 @@ int main() {
|
||||
|
||||
// Create engine.
|
||||
regorus::Engine engine;
|
||||
engine.set_rego_v0(true);
|
||||
|
||||
|
||||
// Load policies.
|
||||
|
||||
@@ -54,6 +54,9 @@ namespace regorus {
|
||||
return std::unique_ptr<Engine>(new Engine(regorus_engine_clone(engine)));
|
||||
}
|
||||
|
||||
Result set_rego_v0(bool enable) {
|
||||
return Result(regorus_engine_set_rego_v0(engine, enable));
|
||||
}
|
||||
|
||||
Result add_policy(const char* path, const char* policy) {
|
||||
return Result(regorus_engine_add_policy(engine, path, policy));
|
||||
|
||||
@@ -62,6 +62,13 @@ namespace Microsoft.WindowsAzure.Regorus.IaaS
|
||||
{
|
||||
return Encoding.UTF8.GetBytes(s + char.MinValue);
|
||||
}
|
||||
public void SetRegoV0(bool enable)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
CheckAndDropResult(RegorusFFI.API.regorus_engine_set_rego_v0(E, enable));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddPolicy(string path, string rego)
|
||||
{
|
||||
|
||||
@@ -23,6 +23,7 @@ var w = new Stopwatch();
|
||||
w.Restart();
|
||||
|
||||
var engine = new Regorus.Engine();
|
||||
engine.SetRegoV0(true);
|
||||
|
||||
w.Stop();
|
||||
var newEngineTicks = w.ElapsedTicks;
|
||||
|
||||
@@ -51,6 +51,14 @@ namespace Regorus
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRegoV0(bool enable)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
CheckAndDropResult(RegorusFFI.API.regorus_engine_set_rego_v0(E, enable));
|
||||
}
|
||||
}
|
||||
|
||||
public string AddPolicyFromFile(string path)
|
||||
{
|
||||
var pathBytes = NullTerminatedUTF8Bytes(path);
|
||||
|
||||
@@ -10,7 +10,7 @@ crate-type = ["cdylib", "staticlib"]
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
regorus = { path = "../..", default-features = false }
|
||||
serde_json = "1.0.113"
|
||||
serde_json = "1.0.140"
|
||||
|
||||
[features]
|
||||
default = ["ast", "std", "coverage", "regorus/arc", "regorus/full-opa"]
|
||||
@@ -20,5 +20,5 @@ coverage = ["regorus/coverage"]
|
||||
custom_allocator = []
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = "0.27.0"
|
||||
cbindgen = "0.28.0"
|
||||
csbindgen = "=1.9.3"
|
||||
|
||||
@@ -222,7 +222,7 @@ pub extern "C" fn regorus_engine_add_data_from_json_file(
|
||||
|
||||
/// Clear policy data.
|
||||
///
|
||||
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.clear_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<()> {
|
||||
@@ -233,7 +233,7 @@ pub extern "C" fn regorus_engine_clear_data(engine: *mut RegorusEngine) -> Regor
|
||||
|
||||
/// Set input.
|
||||
///
|
||||
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.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(
|
||||
@@ -264,7 +264,7 @@ pub extern "C" fn regorus_engine_set_input_from_json_file(
|
||||
|
||||
/// Evaluate query.
|
||||
///
|
||||
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.eval_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(
|
||||
@@ -289,7 +289,7 @@ pub extern "C" fn regorus_engine_eval_query(
|
||||
|
||||
/// Evaluate specified rule.
|
||||
///
|
||||
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.eval_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(
|
||||
@@ -314,7 +314,7 @@ pub extern "C" fn regorus_engine_eval_rule(
|
||||
|
||||
/// Enable/disable coverage.
|
||||
///
|
||||
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.set_enable_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")]
|
||||
@@ -330,7 +330,7 @@ pub extern "C" fn regorus_engine_set_enable_coverage(
|
||||
|
||||
/// Get coverage report.
|
||||
///
|
||||
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.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 {
|
||||
@@ -375,7 +375,7 @@ pub extern "C" fn regorus_engine_get_coverage_report_pretty(
|
||||
|
||||
/// Clear coverage data.
|
||||
///
|
||||
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.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 {
|
||||
@@ -387,7 +387,7 @@ pub extern "C" fn regorus_engine_clear_coverage_data(engine: *mut RegorusEngine)
|
||||
|
||||
/// Whether to gather output of print statements.
|
||||
///
|
||||
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.set_gather_prints
|
||||
/// 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(
|
||||
@@ -402,7 +402,7 @@ pub extern "C" fn regorus_engine_set_gather_prints(
|
||||
|
||||
/// Take all the gathered print statements.
|
||||
///
|
||||
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.take_prints
|
||||
/// 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> {
|
||||
@@ -437,6 +437,28 @@ pub extern "C" fn regorus_engine_get_ast_as_json(engine: *mut RegorusEngine) ->
|
||||
}
|
||||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
@@ -16,8 +16,11 @@ func main() {
|
||||
// Create new engine
|
||||
engine := regorus.NewEngine()
|
||||
defer engine.Close()
|
||||
elapsed1 := time.Since(t)
|
||||
|
||||
engine.SetRegoV0(true)
|
||||
elapsed1 := time.Since(t)
|
||||
|
||||
|
||||
t = time.Now()
|
||||
// Add policies and data.
|
||||
policies := []string{
|
||||
|
||||
@@ -28,6 +28,17 @@ func (e *Engine) Clone() *Engine {
|
||||
return c
|
||||
}
|
||||
|
||||
func (e *Engine) SetRegoV0(enable bool) (error) {
|
||||
result := C.regorus_engine_set_rego_v0(e.e, C.bool(enable))
|
||||
defer C.regorus_result_drop(result)
|
||||
|
||||
if result.status != C.RegorusStatusOk {
|
||||
return fmt.Errorf("%s", C.GoString(result.error_message))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Engine) AddPolicy(path string, rego string) (string, error) {
|
||||
path_c := C.CString(path)
|
||||
defer C.free(unsafe.Pointer(path_c))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "regorus-java"
|
||||
version = "0.2.2"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/microsoft/regorus/bindings/java"
|
||||
description = "Java bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
|
||||
|
||||
@@ -4,39 +4,45 @@
|
||||
import com.microsoft.regorus.Engine;
|
||||
|
||||
public class Test {
|
||||
|
||||
public static void main(String[] args) {
|
||||
try (Engine engine = new Engine()) {
|
||||
String pkg = engine.addPolicy(
|
||||
"hello.rego",
|
||||
"package test\nx=1\nmessage = concat(\", \", [input.message, data.message])"
|
||||
"hello.rego",
|
||||
"package test\nx=1\nmessage = concat(\", \", [input.message, data.message])"
|
||||
);
|
||||
System.out.println("Loaded package " + pkg);
|
||||
System.out.println("Loaded package " + pkg);
|
||||
|
||||
|
||||
engine.addDataJson("{\"message\":\"World!\"}");
|
||||
engine.setInputJson("{\"message\":\"Hello\"}");
|
||||
|
||||
// Evaluate query.
|
||||
String resJson = engine.evalQuery("data.test.message");
|
||||
// Evaluate query.
|
||||
String resJson = engine.evalQuery("data.test.message");
|
||||
System.out.println(resJson);
|
||||
|
||||
// Enable coverage.
|
||||
engine.setEnableCoverage(true);
|
||||
// Enable coverage.
|
||||
engine.setEnableCoverage(true);
|
||||
|
||||
// Evaluate rule.
|
||||
String valueJson = engine.evalRule("data.test.message");
|
||||
// Evaluate rule.
|
||||
String valueJson = engine.evalRule("data.test.message");
|
||||
System.out.println(valueJson);
|
||||
|
||||
String coverageJson = engine.getCoverageReport();
|
||||
System.out.println(coverageJson);
|
||||
String coverageJson = engine.getCoverageReport();
|
||||
System.out.println(coverageJson);
|
||||
|
||||
System.out.println(engine.getCoverageReportPretty());
|
||||
System.out.println(engine.getCoverageReportPretty());
|
||||
|
||||
String packagesJson = engine.getPackages();
|
||||
System.out.println(packagesJson);
|
||||
String packagesJson = engine.getPackages();
|
||||
System.out.println(packagesJson);
|
||||
|
||||
String policiesJson = engine.getPolicies();
|
||||
System.out.println(policiesJson);
|
||||
String policiesJson = engine.getPolicies();
|
||||
System.out.println(policiesJson);
|
||||
|
||||
engine.setRegoV0(true);
|
||||
engine.addPolicy(
|
||||
"world.rego",
|
||||
"package world\nx { true }"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,20 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeClone(
|
||||
Box::into_raw(Box::new(c)) as jlong
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetRegoV0(
|
||||
env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
enable: bool,
|
||||
) {
|
||||
let _ = throw_err(env, |_env| {
|
||||
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
|
||||
engine.set_rego_v0(enable);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddPolicy(
|
||||
env: JNIEnv,
|
||||
|
||||
@@ -8,10 +8,8 @@ package com.microsoft.regorus;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Regorus Engine.
|
||||
@@ -23,6 +21,7 @@ public class Engine implements AutoCloseable, Cloneable {
|
||||
// if you update the native API.
|
||||
private static native long nativeNewEngine();
|
||||
private static native long nativeClone(long enginePtr);
|
||||
private static native void nativeSetRegoV0(long enginePtr, boolean enable);
|
||||
private static native String nativeAddPolicy(long enginePtr, String path, String rego);
|
||||
private static native String nativeAddPolicyFromFile(long enginePtr, String path);
|
||||
private static native String nativeGetPackages(long enginePtr);
|
||||
@@ -55,7 +54,7 @@ public class Engine implements AutoCloseable, Cloneable {
|
||||
|
||||
|
||||
Engine(long ptr) {
|
||||
enginePtr = ptr;
|
||||
enginePtr = ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,6 +64,16 @@ public class Engine implements AutoCloseable, Cloneable {
|
||||
return new Engine(nativeClone(enginePtr));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable Rego v0.
|
||||
*
|
||||
* @param enable Whether to enable v0 or not.
|
||||
*
|
||||
*/
|
||||
public void setRegoV0(boolean enable) {
|
||||
nativeSetRegoV0(enginePtr, enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an inline Rego policy.
|
||||
*
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "regoruspy"
|
||||
version = "0.2.2"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/microsoft/regorus/bindings/python"
|
||||
description = "Python bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
|
||||
@@ -18,8 +18,8 @@ coverage = ["regorus/coverage"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
ordered-float = "4.2.0"
|
||||
pyo3 = {version = "0.22.0", features = ["anyhow", "extension-module"] }
|
||||
ordered-float = "5.0.0"
|
||||
pyo3 = {version = "0.24.0", features = ["anyhow", "extension-module"] }
|
||||
regorus = { path = "../..", default-features = false, features = ["arc"] }
|
||||
serde_json = "1.0.112"
|
||||
serde_json = "1.0.140"
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ use anyhow::{anyhow, Result};
|
||||
use pyo3::exceptions::PyTypeError;
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::*;
|
||||
use pyo3::IntoPyObjectExt;
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
@@ -94,7 +95,7 @@ fn from(ob: &Bound<'_, PyAny>) -> Result<Value, PyErr> {
|
||||
let mut map = BTreeMap::new();
|
||||
let keys = pmap.keys()?;
|
||||
let values = pmap.values()?;
|
||||
for i in 0..keys.len()? {
|
||||
for i in 0..keys.len() {
|
||||
let key = keys.get_item(i)?;
|
||||
let value = values.get_item(i)?;
|
||||
map.insert(from(&key)?, from(&value)?);
|
||||
@@ -108,49 +109,53 @@ fn from(ob: &Bound<'_, PyAny>) -> Result<Value, PyErr> {
|
||||
}
|
||||
|
||||
fn to(mut v: Value, py: Python<'_>) -> Result<PyObject> {
|
||||
Ok(match v {
|
||||
Value::Null => None::<u64>.to_object(py),
|
||||
let obj = match v {
|
||||
Value::Null => None::<u64>.into_bound_py_any(py),
|
||||
|
||||
// TODO: Revisit this mapping
|
||||
Value::Undefined => None::<u64>.to_object(py),
|
||||
Value::Undefined => None::<u64>.into_bound_py_any(py),
|
||||
|
||||
Value::Bool(b) => b.to_object(py),
|
||||
Value::String(s) => s.to_object(py),
|
||||
Value::Bool(b) => b.into_bound_py_any(py),
|
||||
Value::String(s) => s.into_bound_py_any(py),
|
||||
|
||||
Value::Number(_) => {
|
||||
if let Ok(f) = v.as_f64() {
|
||||
f.to_object(py)
|
||||
f.into_bound_py_any(py)
|
||||
} else if let Ok(u) = v.as_u64() {
|
||||
u.to_object(py)
|
||||
u.into_bound_py_any(py)
|
||||
} else {
|
||||
v.as_i64()?.to_object(py)
|
||||
v.as_i64()?.into_bound_py_any(py)
|
||||
}
|
||||
}
|
||||
|
||||
Value::Array(_) => {
|
||||
let list = PyList::empty_bound(py);
|
||||
let list = PyList::empty(py);
|
||||
for v in std::mem::take(v.as_array_mut()?) {
|
||||
list.append(to(v, py)?)?;
|
||||
}
|
||||
list.into()
|
||||
list.into_bound_py_any(py)
|
||||
}
|
||||
|
||||
Value::Set(_) => {
|
||||
let set = PySet::empty_bound(py)?;
|
||||
let set = PySet::empty(py)?;
|
||||
for v in std::mem::take(v.as_set_mut()?) {
|
||||
set.add(to(v, py)?)?;
|
||||
}
|
||||
set.into()
|
||||
set.into_bound_py_any(py)
|
||||
}
|
||||
|
||||
Value::Object(_) => {
|
||||
let dict = PyDict::new_bound(py);
|
||||
let dict = PyDict::new(py);
|
||||
for (k, v) in std::mem::take(v.as_object_mut()?) {
|
||||
dict.set_item(to(k, py)?, to(v, py)?)?;
|
||||
}
|
||||
dict.into()
|
||||
dict.into_bound_py_any(py)
|
||||
}
|
||||
})
|
||||
};
|
||||
match obj {
|
||||
Ok(v) => Ok(v.into()),
|
||||
Err(e) => Err(anyhow!("{e}")),
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
@@ -163,6 +168,15 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
|
||||
/// Turn on rego v0.
|
||||
///
|
||||
/// Regorus now defaults to v1.
|
||||
///
|
||||
/// * `enable`: Whether to enable/disable v0.
|
||||
pub fn set_rego_v0(&mut self, enable: bool) {
|
||||
self.engine.set_rego_v0(enable)
|
||||
}
|
||||
|
||||
/// Add a policy
|
||||
///
|
||||
/// The policy is parsed into AST.
|
||||
@@ -261,30 +275,30 @@ impl Engine {
|
||||
pub fn eval_query(&mut self, query: String, py: Python<'_>) -> Result<PyObject> {
|
||||
let results = self.engine.eval_query(query, false)?;
|
||||
|
||||
let rlist = PyList::empty_bound(py);
|
||||
let rlist = PyList::empty(py);
|
||||
for result in results.result.into_iter() {
|
||||
let rdict = PyDict::new_bound(py);
|
||||
let rdict = PyDict::new(py);
|
||||
|
||||
let elist = PyList::empty_bound(py);
|
||||
let elist = PyList::empty(py);
|
||||
for expr in result.expressions.into_iter() {
|
||||
let edict = PyDict::new_bound(py);
|
||||
edict.set_item("value".to_object(py), to(expr.value, py)?)?;
|
||||
edict.set_item("text".to_object(py), expr.text.as_ref().to_object(py))?;
|
||||
let edict = PyDict::new(py);
|
||||
edict.set_item("value", to(expr.value, py)?)?;
|
||||
edict.set_item("text", expr.text.as_ref())?;
|
||||
|
||||
let ldict = PyDict::new_bound(py);
|
||||
ldict.set_item("row".to_object(py), expr.location.row.to_object(py))?;
|
||||
ldict.set_item("col".to_object(py), expr.location.col.to_object(py))?;
|
||||
let ldict = PyDict::new(py);
|
||||
ldict.set_item("row", expr.location.row)?;
|
||||
ldict.set_item("col", expr.location.col)?;
|
||||
|
||||
edict.set_item("location".to_object(py), ldict)?;
|
||||
edict.set_item("location", ldict)?;
|
||||
elist.append(edict)?;
|
||||
}
|
||||
|
||||
rdict.set_item("expressions".to_object(py), elist)?;
|
||||
rdict.set_item("bindings".to_object(py), to(result.bindings, py)?)?;
|
||||
rdict.set_item("expressions", elist)?;
|
||||
rdict.set_item("bindings", to(result.bindings, py)?)?;
|
||||
rlist.append(rdict)?;
|
||||
}
|
||||
let dict = PyDict::new_bound(py);
|
||||
dict.set_item("result".to_object(py), rlist)?;
|
||||
let dict = PyDict::new(py);
|
||||
dict.set_item("result", rlist)?;
|
||||
Ok(dict.into())
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import regorus
|
||||
# Create engine
|
||||
engine = regorus.Engine()
|
||||
|
||||
engine.set_rego_v0(True)
|
||||
|
||||
# Load policies
|
||||
pkg = engine.add_policy_from_file('../../tests/aci/framework.rego')
|
||||
print(' Loaded package %s' % pkg)
|
||||
|
||||
@@ -3,7 +3,7 @@ require:
|
||||
- rubocop-rake
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: 3.0
|
||||
TargetRubyVersion: 3.4
|
||||
NewCops: enable
|
||||
|
||||
Layout/LineLength:
|
||||
|
||||
@@ -1 +1 @@
|
||||
ruby 3.3.1
|
||||
ruby 3.4.2
|
||||
|
||||
@@ -7,10 +7,10 @@ gemspec
|
||||
|
||||
# These gems are required for local development and testing,
|
||||
# but won't be included in the published gem
|
||||
gem "minitest", "~> 5.23"
|
||||
gem "minitest", "~> 5.25"
|
||||
gem "rake", "~> 13.2"
|
||||
gem "rake-compiler"
|
||||
gem "rake-compiler-dock"
|
||||
gem "rubocop", "~> 1.64", require: false
|
||||
gem "rubocop-minitest", require: false
|
||||
gem "rubocop-rake", require: false
|
||||
gem "rake-compiler", "~> 1.2"
|
||||
gem "rake-compiler-dock", "~> 1.9"
|
||||
gem "rubocop", "~> 1.73", require: false
|
||||
gem "rubocop-minitest", "~> 0.37.1", require: false
|
||||
gem "rubocop-rake", "~> 0.7.1", require: false
|
||||
|
||||
@@ -1,63 +1,68 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
regorusrb (0.2.1)
|
||||
rb_sys (~> 0.9.97)
|
||||
regorusrb (0.3.0)
|
||||
rb_sys (~> 0.9.111)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
ast (2.4.2)
|
||||
json (2.7.2)
|
||||
language_server-protocol (3.17.0.3)
|
||||
minitest (5.24.1)
|
||||
parallel (1.25.1)
|
||||
parser (3.3.4.0)
|
||||
json (2.10.1)
|
||||
language_server-protocol (3.17.0.4)
|
||||
lint_roller (1.1.0)
|
||||
minitest (5.25.4)
|
||||
parallel (1.26.3)
|
||||
parser (3.3.7.1)
|
||||
ast (~> 2.4.1)
|
||||
racc
|
||||
racc (1.8.0)
|
||||
racc (1.8.1)
|
||||
rainbow (3.1.1)
|
||||
rake (13.2.1)
|
||||
rake-compiler (1.2.7)
|
||||
rake-compiler (1.2.9)
|
||||
rake
|
||||
rake-compiler-dock (1.5.1)
|
||||
rb_sys (0.9.99)
|
||||
regexp_parser (2.9.2)
|
||||
rexml (3.3.9)
|
||||
rubocop (1.65.0)
|
||||
rake-compiler-dock (1.9.1)
|
||||
rb_sys (0.9.111)
|
||||
rake-compiler-dock (= 1.9.1)
|
||||
regexp_parser (2.10.0)
|
||||
rubocop (1.73.2)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (>= 3.17.0)
|
||||
language_server-protocol (~> 3.17.0.2)
|
||||
lint_roller (~> 1.1.0)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.3.0.2)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 2.4, < 3.0)
|
||||
rexml (>= 3.2.5, < 4.0)
|
||||
rubocop-ast (>= 1.31.1, < 2.0)
|
||||
regexp_parser (>= 2.9.3, < 3.0)
|
||||
rubocop-ast (>= 1.38.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 2.4.0, < 3.0)
|
||||
rubocop-ast (1.31.3)
|
||||
unicode-display_width (>= 2.4.0, < 4.0)
|
||||
rubocop-ast (1.38.1)
|
||||
parser (>= 3.3.1.0)
|
||||
rubocop-minitest (0.35.1)
|
||||
rubocop (>= 1.61, < 2.0)
|
||||
rubocop-ast (>= 1.31.1, < 2.0)
|
||||
rubocop-rake (0.6.0)
|
||||
rubocop (~> 1.0)
|
||||
rubocop-minitest (0.37.1)
|
||||
lint_roller (~> 1.1)
|
||||
rubocop (>= 1.72.1, < 2.0)
|
||||
rubocop-ast (>= 1.38.0, < 2.0)
|
||||
rubocop-rake (0.7.1)
|
||||
lint_roller (~> 1.1)
|
||||
rubocop (>= 1.72.1)
|
||||
ruby-progressbar (1.13.0)
|
||||
unicode-display_width (2.5.0)
|
||||
unicode-display_width (3.1.4)
|
||||
unicode-emoji (~> 4.0, >= 4.0.4)
|
||||
unicode-emoji (4.0.4)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
minitest (~> 5.23)
|
||||
minitest (~> 5.25)
|
||||
rake (~> 13.2)
|
||||
rake-compiler
|
||||
rake-compiler-dock
|
||||
rake-compiler (~> 1.2)
|
||||
rake-compiler-dock (~> 1.9)
|
||||
regorusrb!
|
||||
rubocop (~> 1.64)
|
||||
rubocop-minitest
|
||||
rubocop-rake
|
||||
rubocop (~> 1.73)
|
||||
rubocop-minitest (~> 0.37.1)
|
||||
rubocop-rake (~> 0.7.1)
|
||||
|
||||
BUNDLED WITH
|
||||
2.5.13
|
||||
2.6.5
|
||||
|
||||
@@ -43,6 +43,9 @@ require "regorus"
|
||||
|
||||
engine = Regorus::Engine.new
|
||||
|
||||
# Policy is old-style.
|
||||
engine.set_rego_v0(true)
|
||||
|
||||
engine.add_policy_from_file('../../tests/aci/framework.rego')
|
||||
engine.add_policy_from_file('../../tests/aci/api.rego')
|
||||
engine.add_policy_from_file('../../tests/aci/policy.rego')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "regorusrb"
|
||||
version = "0.2.2"
|
||||
edition = "2021"
|
||||
version = "0.3.0"
|
||||
edition = "2024"
|
||||
description = "Ruby bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
|
||||
publish = false
|
||||
|
||||
@@ -15,7 +15,7 @@ ast = ["regorus/ast"]
|
||||
coverage = ["regorus/coverage"]
|
||||
|
||||
[dependencies]
|
||||
magnus = { version = "0.6.4" }
|
||||
magnus = { version = "0.7.1" }
|
||||
regorus = { path = "../../../..", default-features = false, features = ["arc"] }
|
||||
serde_json = "1.0.117"
|
||||
serde_magnus = "0.8.1"
|
||||
serde_json = "1.0.140"
|
||||
serde_magnus = "0.9.0"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use magnus::{exception::runtime_error, method, module, prelude::*, Error, Ruby};
|
||||
use magnus::{Error, Ruby, exception::runtime_error, method, module, prelude::*};
|
||||
use regorus::Engine as RegorusEngine;
|
||||
use std::cell::RefCell;
|
||||
use std::cmp::Ordering;
|
||||
@@ -36,6 +36,11 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_rego_v0(&self, enable: bool) -> Result<(), Error> {
|
||||
self.engine.borrow_mut().set_rego_v0(enable);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_policy(&self, path: String, rego: String) -> Result<String, Error> {
|
||||
self.engine
|
||||
.borrow_mut()
|
||||
@@ -297,6 +302,9 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
||||
// defines <, <=, >, >=, and == based on <=>
|
||||
engine_class.include_module(module::comparable())?;
|
||||
|
||||
// rego language configuration
|
||||
engine_class.define_method("set_rego_v0", method!(Engine::set_rego_v0, 1))?;
|
||||
|
||||
// policy operations
|
||||
engine_class.define_method("add_policy", method!(Engine::add_policy, 2))?;
|
||||
engine_class.define_method(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Regorus
|
||||
VERSION = "0.2.1"
|
||||
VERSION = "0.3.0"
|
||||
end
|
||||
|
||||
@@ -10,8 +10,8 @@ Gem::Specification.new do |spec|
|
||||
spec.summary = "Ruby bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
|
||||
spec.homepage = "https://github.com/microsoft/regorus/blob/main/bindings/ruby"
|
||||
spec.license = "MIT"
|
||||
spec.required_ruby_version = ">= 3.0.0"
|
||||
spec.required_rubygems_version = ">= 3.3.11"
|
||||
spec.required_ruby_version = ">= 3.4.2"
|
||||
spec.required_rubygems_version = ">= 3.6.5"
|
||||
|
||||
spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
|
||||
|
||||
@@ -26,5 +26,5 @@ Gem::Specification.new do |spec|
|
||||
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
||||
spec.require_paths = ["lib"]
|
||||
spec.extensions = ["ext/regorusrb/extconf.rb"]
|
||||
spec.add_dependency "rb_sys", "~> 0.9.97"
|
||||
spec.add_dependency "rb_sys", "~> 0.9.111"
|
||||
end
|
||||
|
||||
@@ -17,11 +17,11 @@ class TestRegorus < Minitest::Test
|
||||
def example_policy
|
||||
<<~REGO
|
||||
package regorus_test
|
||||
is_manager {
|
||||
is_manager if {
|
||||
input.name == data.managers[_]
|
||||
}
|
||||
|
||||
is_employee {
|
||||
is_employee if {
|
||||
input.name == data.employees[_]
|
||||
}
|
||||
|
||||
@@ -29,11 +29,11 @@ class TestRegorus < Minitest::Test
|
||||
default is_manager_bool = false
|
||||
default is_employee_bool = false
|
||||
|
||||
is_manager_bool {
|
||||
is_manager_bool if {
|
||||
is_manager
|
||||
}
|
||||
|
||||
is_employee_bool {
|
||||
is_employee_bool if {
|
||||
is_employee
|
||||
}
|
||||
REGO
|
||||
|
||||
2
bindings/wasm/.cargo/config.toml
Normal file
2
bindings/wasm/.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[target.wasm32-unknown-unknown]
|
||||
rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""]
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "regorusjs"
|
||||
version = "0.2.2"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/microsoft/regorus/bindings/wasm"
|
||||
description = "WASM bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
|
||||
@@ -17,8 +17,16 @@ coverage = ["regorus/coverage"]
|
||||
|
||||
[dependencies]
|
||||
regorus = { path = "../..", default-features = false, features = ["arc"] }
|
||||
serde_json = "1.0.111"
|
||||
wasm-bindgen = "=0.2.93"
|
||||
serde_json = "1.0.140"
|
||||
wasm-bindgen = "0.2.100"
|
||||
# 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"]}
|
||||
# Enable wasm_js. See https://docs.rs/getrandom/latest/getrandom/#webassembly-support
|
||||
getrandom = { version = "0.3", features = ["std", "wasm_js"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.40"
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(wasm_bindgen_unstable_test_coverage)'] }
|
||||
|
||||
@@ -45,6 +45,15 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
|
||||
/// Turn on rego v0.
|
||||
///
|
||||
/// Regorus defaults to rego v1.
|
||||
///
|
||||
/// * `enable`: Whether to enable or disable rego v0.
|
||||
pub fn setRegoV0(&mut self, enable: bool) {
|
||||
self.engine.set_rego_v0(enable)
|
||||
}
|
||||
|
||||
/// Add a policy
|
||||
///
|
||||
/// The policy is parsed into AST.
|
||||
|
||||
@@ -42,7 +42,7 @@ fn rego_eval(
|
||||
enable_tracing: bool,
|
||||
non_strict: bool,
|
||||
#[cfg(feature = "coverage")] coverage: bool,
|
||||
v1: bool,
|
||||
v0: bool,
|
||||
) -> Result<()> {
|
||||
// Create engine.
|
||||
let mut engine = regorus::Engine::new();
|
||||
@@ -52,7 +52,7 @@ fn rego_eval(
|
||||
#[cfg(feature = "coverage")]
|
||||
engine.set_enable_coverage(coverage);
|
||||
|
||||
engine.set_rego_v1(v1);
|
||||
engine.set_rego_v0(v0);
|
||||
|
||||
// Load files from given bundles.
|
||||
for dir in bundles.iter() {
|
||||
@@ -238,9 +238,9 @@ enum RegorusCommand {
|
||||
#[arg(long, short)]
|
||||
coverage: bool,
|
||||
|
||||
/// Turn on rego.v1
|
||||
/// Turn on Rego language v0.
|
||||
#[arg(long)]
|
||||
v1: bool,
|
||||
v0: bool,
|
||||
},
|
||||
|
||||
/// Tokenize a Rego policy.
|
||||
@@ -282,7 +282,7 @@ fn main() -> Result<()> {
|
||||
non_strict,
|
||||
#[cfg(feature = "coverage")]
|
||||
coverage,
|
||||
v1,
|
||||
v0,
|
||||
} => rego_eval(
|
||||
&bundles,
|
||||
&data,
|
||||
@@ -292,7 +292,7 @@ fn main() -> Result<()> {
|
||||
non_strict,
|
||||
#[cfg(feature = "coverage")]
|
||||
coverage,
|
||||
v1,
|
||||
v0,
|
||||
),
|
||||
RegorusCommand::Lex { file, verbose } => rego_lex(file, verbose),
|
||||
RegorusCommand::Parse { file } => rego_parse(file),
|
||||
|
||||
@@ -2,22 +2,22 @@ package example
|
||||
|
||||
default allow := false # unless otherwise defined, allow is false
|
||||
|
||||
allow := true { # allow is true if...
|
||||
allow := true if { # allow is true if...
|
||||
count(violation) == 0 # there are zero violations.
|
||||
}
|
||||
|
||||
violation[server.id] { # a server is in the violation set if...
|
||||
violation[server.id] if { # a server is in the violation set if...
|
||||
some server
|
||||
public_server[server] # it exists in the 'public_server' set and...
|
||||
server.protocols[_] == "http" # it contains the insecure "http" protocol.
|
||||
}
|
||||
|
||||
violation[server.id] { # a server is in the violation set if...
|
||||
violation[server.id] if { # a server is in the violation set if...
|
||||
server := input.servers[_] # it exists in the input.servers collection and...
|
||||
server.protocols[_] == "telnet" # it contains the "telnet" protocol.
|
||||
}
|
||||
|
||||
public_server[server] { # a server exists in the public_server set if...
|
||||
public_server[server]if { # a server exists in the public_server set if...
|
||||
some i, j
|
||||
server := input.servers[_] # it exists in the input.servers collection and...
|
||||
server.ports[_] == input.ports[i].id # it references a port in the input.ports collection and...
|
||||
|
||||
@@ -32,8 +32,8 @@ if [ -f Cargo.toml ]; then
|
||||
|
||||
# Ensure that all tests pass with extensions
|
||||
cargo test -r --features rego-extensions
|
||||
cargo test -r --test aci rego-extensions
|
||||
cargo test -r --test kata rego-extensions
|
||||
cargo test -r --test aci --features rego-extensions
|
||||
cargo test -r --test kata --features rego-extensions
|
||||
|
||||
# Ensure that OPA conformance tests don't regress.
|
||||
cargo test -r --features opa-testutil,serde_json/arbitrary_precision,rego-extensions --test opa -- $(tr '\n' ' ' < tests/opa.passing)
|
||||
|
||||
@@ -337,11 +337,11 @@ fn yaml_is_valid(
|
||||
fn yaml_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "yaml.marshal";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
Ok(Value::String(
|
||||
serde_yaml::to_string(&args[0])
|
||||
.with_context(|| span.error("could not serialize to yaml"))?
|
||||
.into(),
|
||||
))
|
||||
|
||||
let serialized = serde_yaml::to_string(&args[0])
|
||||
.map_err(|err| span.error(&format!("could not serialize to yaml: {}", err)))?;
|
||||
|
||||
Ok(Value::String(serialized.into()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::*;
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use rand::{thread_rng, Rng};
|
||||
use rand::Rng;
|
||||
|
||||
pub fn register(m: &mut builtins::BuiltinsMap<&'static str, builtins::BuiltinFcn>) {
|
||||
m.insert("abs", (abs, 1));
|
||||
@@ -169,8 +169,7 @@ fn intn(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Res
|
||||
Some(0) => Value::from(0u64),
|
||||
Some(n) => {
|
||||
// TODO: bounds checking; arbitrary precision
|
||||
let mut rng = thread_rng();
|
||||
let v = rng.gen_range(0..n);
|
||||
let v = rand::rng().random_range(0..n);
|
||||
Value::from(v)
|
||||
}
|
||||
_ => Value::Undefined,
|
||||
|
||||
@@ -439,7 +439,8 @@ fn json_match_schema(
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
// The following is expected to succeed.
|
||||
let document: serde_json::Value = serde_json::from_str(&args[0].to_json_str()?)?;
|
||||
let document: serde_json::Value = serde_json::from_str(&args[0].to_json_str()?)
|
||||
.map_err(|err| span.error(&format!("Failed to parse JSON: {}", err)))?;
|
||||
|
||||
Ok(Value::from_array(
|
||||
match compile_json_schema(¶ms[1], &args[1]) {
|
||||
|
||||
@@ -25,7 +25,11 @@ fn sleep(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Re
|
||||
let dur = time::compat::parse_duration(val.as_ref())
|
||||
.map_err(|e| params[0].span().error(&format!("{e}")))?;
|
||||
|
||||
thread::sleep(dur.to_std()?);
|
||||
let std_dur = dur
|
||||
.to_std()
|
||||
.map_err(|err| anyhow::anyhow!("Failed to convert to std::time::Duration: {err}"))?;
|
||||
|
||||
thread::sleep(std_dur);
|
||||
|
||||
Ok(Value::Null)
|
||||
}
|
||||
|
||||
@@ -158,7 +158,8 @@ fn parse_ns(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) ->
|
||||
let layout = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
let value = ensure_string(name, ¶ms[1], &args[1])?;
|
||||
|
||||
let datetime = compat::parse(layout_with_predefined_formats(&layout), &value)?;
|
||||
let datetime = compat::parse(layout_with_predefined_formats(&layout), &value)
|
||||
.map_err(|err| anyhow::anyhow!("Failed to parse datetime: {}", err))?;
|
||||
safe_timestamp_nanos(span, strict, datetime.timestamp_nanos_opt())
|
||||
}
|
||||
|
||||
@@ -173,7 +174,8 @@ fn parse_rfc3339_ns(
|
||||
|
||||
let value = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
|
||||
let datetime = DateTime::parse_from_rfc3339(&value)?;
|
||||
let datetime = DateTime::parse_from_rfc3339(&value)
|
||||
.map_err(|err| anyhow::anyhow!("Failed to parse datetime: {}", err))?;
|
||||
safe_timestamp_nanos(span, strict, datetime.timestamp_nanos_opt())
|
||||
}
|
||||
|
||||
|
||||
@@ -266,7 +266,7 @@ struct GoTimeFormatItems<'a> {
|
||||
mode: GoTimeFormatItemsMode,
|
||||
}
|
||||
|
||||
impl<'a> GoTimeFormatItems<'a> {
|
||||
impl GoTimeFormatItems<'_> {
|
||||
fn parse(reminder: &str) -> GoTimeFormatItems {
|
||||
GoTimeFormatItems {
|
||||
reminder,
|
||||
|
||||
@@ -83,8 +83,8 @@ fn parse(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Re
|
||||
bail!(span.error("spaces not allowed in resource strings"));
|
||||
}
|
||||
|
||||
let (number_part, suffix) = match string.find(|c: char| c.is_alphabetic()) {
|
||||
Some(p) => (&string[0..p], &string[p..]),
|
||||
let (number_part, suffix) = match string.rfind(|c: char| c.is_ascii_digit()) {
|
||||
Some(p) => (&string[0..p + 1], &string[p + 1..]),
|
||||
_ => (string, ""),
|
||||
};
|
||||
|
||||
@@ -161,8 +161,8 @@ fn parse_bytes(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool)
|
||||
bail!(span.error("spaces not allowed in resource strings"));
|
||||
}
|
||||
|
||||
let (number_part, suffix) = match string.find(|c: char| c.is_alphabetic()) {
|
||||
Some(p) => (&string[0..p], &string[p..]),
|
||||
let (number_part, suffix) = match string.rfind(|c: char| c.is_ascii_digit()) {
|
||||
Some(p) => (&string[0..p + 1], &string[p + 1..]),
|
||||
_ => (string, ""),
|
||||
};
|
||||
|
||||
|
||||
@@ -132,16 +132,16 @@ fn timestamp(uuid: &Uuid) -> Option<Timestamp> {
|
||||
const fn decode_rfc4122_timestamp(uuid: &Uuid) -> (u64, u16) {
|
||||
let bytes = uuid.as_bytes();
|
||||
|
||||
let ticks: u64 = ((bytes[6] & 0x0F) as u64) << 56
|
||||
| (bytes[7] as u64) << 48
|
||||
| (bytes[4] as u64) << 40
|
||||
| (bytes[5] as u64) << 32
|
||||
| (bytes[0] as u64) << 24
|
||||
| (bytes[1] as u64) << 16
|
||||
| (bytes[2] as u64) << 8
|
||||
let ticks: u64 = (((bytes[6] & 0x0F) as u64) << 56)
|
||||
| ((bytes[7] as u64) << 48)
|
||||
| ((bytes[4] as u64) << 40)
|
||||
| ((bytes[5] as u64) << 32)
|
||||
| ((bytes[0] as u64) << 24)
|
||||
| ((bytes[1] as u64) << 16)
|
||||
| ((bytes[2] as u64) << 8)
|
||||
| (bytes[3] as u64);
|
||||
|
||||
let counter: u16 = ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16);
|
||||
let counter: u16 = (((bytes[8] & 0x3F) as u16) << 8) | (bytes[9] as u16);
|
||||
|
||||
(ticks, counter)
|
||||
}
|
||||
|
||||
@@ -37,33 +37,37 @@ impl Engine {
|
||||
modules: vec![],
|
||||
interpreter: Interpreter::new(),
|
||||
prepared: false,
|
||||
rego_v1: false,
|
||||
rego_v1: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Turn rego.v1 on/off for subsequently added policies.
|
||||
///
|
||||
/// Explicit import rego.v1 is not needed if set.
|
||||
/// Enable rego v0.
|
||||
///
|
||||
/// Note that regorus now defaults to v1.
|
||||
/// ```
|
||||
/// # use regorus::*;
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
/// engine.set_rego_v1(true);
|
||||
/// // Enable v0 for old style policies.
|
||||
/// engine.set_rego_v0(true);
|
||||
///
|
||||
/// engine.add_policy(
|
||||
/// "test.rego".to_string(),
|
||||
/// r#"
|
||||
/// package test
|
||||
/// allow if true # if keyword is automatically imported
|
||||
///
|
||||
/// allow { # v0 syntax does not require if keyword
|
||||
/// 1 < 2
|
||||
/// }
|
||||
/// "#.to_string())?;
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
pub fn set_rego_v1(&mut self, rego_v1: bool) {
|
||||
self.rego_v1 = rego_v1;
|
||||
pub fn set_rego_v0(&mut self, rego_v0: bool) {
|
||||
self.rego_v1 = !rego_v0;
|
||||
}
|
||||
|
||||
/// Add a policy.
|
||||
@@ -114,6 +118,8 @@ impl Engine {
|
||||
/// # use regorus::*;
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// let mut engine = Engine::new();
|
||||
/// // framework.rego does not conform to v1.
|
||||
/// engine.set_rego_v0(true);
|
||||
///
|
||||
/// let package = engine.add_policy_from_file("tests/aci/framework.rego")?;
|
||||
///
|
||||
@@ -139,6 +145,8 @@ impl Engine {
|
||||
/// # use regorus::*;
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// let mut engine = Engine::new();
|
||||
/// // framework.rego does not conform to v1.
|
||||
/// engine.set_rego_v0(true);
|
||||
///
|
||||
/// let _ = engine.add_policy_from_file("tests/aci/framework.rego")?;
|
||||
///
|
||||
@@ -414,6 +422,7 @@ impl Engine {
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
/// // Add policies
|
||||
/// engine.set_rego_v0(true);
|
||||
/// engine.add_policy_from_file("tests/aci/framework.rego")?;
|
||||
/// engine.add_policy_from_file("tests/aci/api.rego")?;
|
||||
/// engine.add_policy_from_file("tests/aci/policy.rego")?;
|
||||
@@ -739,7 +748,7 @@ impl Engine {
|
||||
/// engine.add_policy(
|
||||
/// "policy.rego".to_string(),
|
||||
/// r#"package invalid
|
||||
/// x = y {
|
||||
/// x = y if {
|
||||
/// # y = do_magic(2)
|
||||
/// do_magic(2, y) # y is supplied as an out parameter.
|
||||
/// }
|
||||
@@ -775,7 +784,7 @@ impl Engine {
|
||||
/// r#"
|
||||
/// package test # Line 2
|
||||
///
|
||||
/// x = y { # Line 4
|
||||
/// x = y if { # Line 4
|
||||
/// input.a > 2 # Line 5
|
||||
/// y = 5 # Line 6
|
||||
/// }
|
||||
|
||||
@@ -2970,10 +2970,7 @@ impl Interpreter {
|
||||
_ => bail!("internal error: rule's context already popped"),
|
||||
};
|
||||
|
||||
let result = match result {
|
||||
Ok(r) => r,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
let result = result?;
|
||||
|
||||
assert_eq!(self.scopes.len(), n_scopes);
|
||||
|
||||
|
||||
@@ -358,7 +358,7 @@ where
|
||||
}
|
||||
|
||||
/// Implement clone for a boxed extension using [`Extension::clone_box`].
|
||||
impl<'a> Clone for Box<dyn 'a + Extension> {
|
||||
impl Clone for Box<dyn '_ + Extension> {
|
||||
fn clone(&self) -> Self {
|
||||
(**self).clone_box()
|
||||
}
|
||||
@@ -405,7 +405,6 @@ pub mod coverage {
|
||||
/// Lines that are not covered are red.
|
||||
///
|
||||
/// <img src="https://github.com/microsoft/regorus/blob/main/docs/coverage.png?raw=true">
|
||||
|
||||
pub fn to_string_pretty(&self) -> anyhow::Result<String> {
|
||||
let mut s = String::default();
|
||||
s.push_str("COVERAGE REPORT:\n");
|
||||
|
||||
@@ -19,7 +19,7 @@ pub struct Parser<'source> {
|
||||
tok: Token,
|
||||
line: u32,
|
||||
end: u32,
|
||||
future_keywords: BTreeMap<String, Span>,
|
||||
future_keywords: BTreeMap<String, Option<Span>>,
|
||||
rego_v1: bool,
|
||||
}
|
||||
|
||||
@@ -41,13 +41,13 @@ impl<'source> Parser<'source> {
|
||||
}
|
||||
|
||||
pub fn enable_rego_v1(&mut self) -> Result<()> {
|
||||
self.turn_on_rego_v1(self.tok.1.clone())
|
||||
self.turn_on_rego_v1(&None)
|
||||
}
|
||||
|
||||
fn turn_on_rego_v1(&mut self, span: Span) -> Result<()> {
|
||||
fn turn_on_rego_v1(&mut self, span: &Option<Span>) -> Result<()> {
|
||||
self.rego_v1 = true;
|
||||
for kw in FUTURE_KEYWORDS {
|
||||
self.set_future_keyword(kw, &span)?;
|
||||
self.set_future_keyword(kw, span)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -97,9 +97,9 @@ impl<'source> Parser<'source> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_future_keyword(&mut self, kw: &str, span: &Span) -> Result<()> {
|
||||
match &self.future_keywords.get(kw) {
|
||||
Some(s) if self.rego_v1 => Err(self.source.error(
|
||||
pub fn set_future_keyword(&mut self, kw: &str, span: &Option<Span>) -> Result<()> {
|
||||
match (span, self.future_keywords.get(kw)) {
|
||||
(Some(span), Some(Some(s))) if self.rego_v1 => Err(self.source.error(
|
||||
span.line,
|
||||
span.col,
|
||||
format!(
|
||||
@@ -155,11 +155,11 @@ impl<'source> Parser<'source> {
|
||||
fn handle_import_future_keywords(&mut self, comps: &[Span]) -> Result<bool> {
|
||||
if comps.len() >= 2 && comps[0].text() == "future" && comps[1].text() == "keywords" {
|
||||
match comps.len() - 2 {
|
||||
1 => self.set_future_keyword(comps[2].text(), &comps[2])?,
|
||||
1 => self.set_future_keyword(comps[2].text(), &Some(comps[2].clone()))?,
|
||||
0 => {
|
||||
let span = &comps[1];
|
||||
for kw in FUTURE_KEYWORDS.iter() {
|
||||
self.set_future_keyword(kw, span)?;
|
||||
self.set_future_keyword(kw, &Some(span.clone()))?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@@ -1709,7 +1709,7 @@ impl<'source> Parser<'source> {
|
||||
|
||||
let is_future_kw =
|
||||
if comps.len() == 2 && comps[0].text() == "rego" && comps[1].text() == "v1" {
|
||||
self.turn_on_rego_v1(span.clone())?;
|
||||
self.turn_on_rego_v1(&Some(span.clone()))?;
|
||||
true
|
||||
} else {
|
||||
self.handle_import_future_keywords(&comps)?
|
||||
|
||||
@@ -141,6 +141,7 @@ pub fn eval_file(
|
||||
strict: bool,
|
||||
) -> Result<(Vec<Value>, Vec<String>)> {
|
||||
let mut engine: Engine = Engine::new();
|
||||
engine.set_rego_v0(true);
|
||||
engine.set_strict_builtin_errors(strict);
|
||||
engine.set_gather_prints(true);
|
||||
|
||||
|
||||
@@ -405,7 +405,9 @@ impl Value {
|
||||
#[cfg(feature = "yaml")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
||||
pub fn from_yaml_str(yaml: &str) -> Result<Value> {
|
||||
Ok(serde_yaml::from_str(yaml)?)
|
||||
let value = serde_yaml::from_str(yaml)
|
||||
.map_err(|err| anyhow::anyhow!("Failed to parse YAML: {}", err))?;
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
/// Deserialize a value from a file containing YAML.
|
||||
|
||||
@@ -27,6 +27,7 @@ struct YamlTest {
|
||||
|
||||
fn eval_test_case(dir: &Path, case: &TestCase) -> Result<Value> {
|
||||
let mut engine = Engine::new();
|
||||
engine.set_rego_v0(true);
|
||||
|
||||
engine.add_data(case.data.clone())?;
|
||||
engine.set_input(case.input.clone());
|
||||
@@ -116,6 +117,7 @@ fn run_aci_tests(dir: &Path) -> Result<()> {
|
||||
#[cfg(feature = "coverage")]
|
||||
fn run_aci_tests_coverage(dir: &Path) -> Result<()> {
|
||||
let mut engine = Engine::new();
|
||||
engine.set_rego_v0(true);
|
||||
engine.set_enable_coverage(true);
|
||||
|
||||
let mut added = std::collections::BTreeSet::new();
|
||||
|
||||
@@ -45,6 +45,7 @@ fn yaml_test_impl(file: &str) -> Result<()> {
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_enable_coverage(true);
|
||||
engine.set_rego_v0(true);
|
||||
|
||||
for (idx, rego) in case.modules.iter().enumerate() {
|
||||
engine.add_policy(format!("rego_{idx}"), rego.clone())?;
|
||||
|
||||
@@ -9,6 +9,16 @@ use anyhow::{bail, Result};
|
||||
use clap::Parser;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
fn normalize_printed_paths(mut prints: Vec<String>) -> Vec<String> {
|
||||
prints.iter_mut().for_each(|p| {
|
||||
*p = p
|
||||
.replace("\\", "/")
|
||||
.replace("//", "/")
|
||||
.replace("\r\n", "\n");
|
||||
});
|
||||
prints
|
||||
}
|
||||
|
||||
fn run_kata_tests(
|
||||
tests_dir: &Path,
|
||||
name: &Option<String>,
|
||||
@@ -43,6 +53,7 @@ fn run_kata_tests(
|
||||
let prints_file = path.join("prints.json");
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_rego_v0(true);
|
||||
engine.add_policy_from_file(&policy_file)?;
|
||||
engine.set_gather_prints(true);
|
||||
engine.set_strict_builtin_errors(false);
|
||||
@@ -110,14 +121,14 @@ fn run_kata_tests(
|
||||
|
||||
if generate {
|
||||
results.push(r);
|
||||
prints.push(engine.take_prints()?);
|
||||
prints.push(normalize_printed_paths(engine.take_prints()?));
|
||||
} else {
|
||||
let expected = results.pop().unwrap();
|
||||
assert_eq!(r, expected, "{lineno} failed in {}", inputs_file.display());
|
||||
|
||||
let p = engine.take_prints()?;
|
||||
assert_eq!(p, new_engine.take_prints()?);
|
||||
assert_eq!(p, prints.pop().unwrap());
|
||||
let p = normalize_printed_paths(engine.take_prints()?);
|
||||
assert_eq!(p, normalize_printed_paths(new_engine.take_prints()?));
|
||||
assert_eq!(p, normalize_printed_paths(prints.pop().unwrap()));
|
||||
}
|
||||
|
||||
num_queries += 2;
|
||||
@@ -179,7 +190,6 @@ fn stateful_policy_test() -> Result<()> {
|
||||
let policy = String::from(
|
||||
r#"
|
||||
package example
|
||||
import rego.v1
|
||||
|
||||
default allow := false
|
||||
|
||||
|
||||
16
tests/opa.rs
16
tests/opa.rs
@@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
const OPA_REPO: &str = "https://github.com/open-policy-agent/opa";
|
||||
const OPA_BRANCH: &str = "v0.70.0";
|
||||
const OPA_BRANCH: &str = "v1.2.0";
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
@@ -51,13 +51,13 @@ struct YamlTest {
|
||||
cases: Vec<TestCase>,
|
||||
}
|
||||
|
||||
fn eval_test_case(case: &TestCase, is_rego_v1_test: bool) -> Result<Value> {
|
||||
fn eval_test_case(case: &TestCase, is_rego_v0_test: bool) -> Result<Value> {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
#[cfg(feature = "coverage")]
|
||||
engine.set_enable_coverage(true);
|
||||
|
||||
engine.set_rego_v1(is_rego_v1_test);
|
||||
engine.set_rego_v0(is_rego_v0_test);
|
||||
|
||||
if let Some(data) = &case.data {
|
||||
engine.add_data(data.clone())?;
|
||||
@@ -174,7 +174,7 @@ fn run_opa_tests(opa_tests_dir: String, folders: &[String]) -> Result<()> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let is_rego_v1_test = path_dir_str.starts_with("v1/") || path_dir.starts_with("v1\\");
|
||||
let is_rego_v0_test = path_dir_str.starts_with("v0/") || path_dir.starts_with("v0\\");
|
||||
let entry = status.entry(path_dir_str).or_insert((0, 0, 0));
|
||||
|
||||
let yaml_str = std::fs::read_to_string(&path_str)?;
|
||||
@@ -219,7 +219,7 @@ fn run_opa_tests(opa_tests_dir: String, folders: &[String]) -> Result<()> {
|
||||
|
||||
print!("{:4}: {:90}", entry.2, case.note);
|
||||
entry.2 += 1;
|
||||
match (eval_test_case(&case, is_rego_v1_test), &case.want_result) {
|
||||
match (eval_test_case(&case, is_rego_v0_test), &case.want_result) {
|
||||
(Ok(actual), Some(expected))
|
||||
if is_json_schema_test && json_schema_tests_check(&actual, &expected) =>
|
||||
{
|
||||
@@ -294,6 +294,10 @@ fn run_opa_tests(opa_tests_dir: String, folders: &[String]) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
if is_rego_v0_test {
|
||||
cmd += " -v0";
|
||||
}
|
||||
|
||||
std::fs::write(path.join(format!("query{n}.text")), case.query.as_bytes())?;
|
||||
cmd += format!(" \"{}\"", &case.query).as_str();
|
||||
|
||||
@@ -394,7 +398,7 @@ fn main() -> Result<()> {
|
||||
bail!("failed to clone OPA repository");
|
||||
}
|
||||
}
|
||||
format!("{branch_dir}/test/cases/testdata")
|
||||
format!("{branch_dir}/v1/test/cases/testdata")
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user