Compare commits

...

14 Commits

Author SHA1 Message Date
Anand Krishnamoorthi
2901481c51 chore: release (#376)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-10 12:33:59 -07:00
Anand Krishnamoorthi
c963e477a3 feat: Update to OPA v1.2.0 (#373)
Regorus now defaults to rego v1. `import rego.v1` is no longer needed.
Additionally, `future` keywords are automatically imported.

See
https://www.openpolicyagent.org/docs/latest/v0-upgrade/#changes-to-rego-in-opa-v10
to understand the differences between rego v1 and v0.

BREAKING CHANGE:

v0 style policies will error out by default. To enable v0 behavior, call engine.set_rego_v0(true) before
loading policies.

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2025-03-10 11:56:01 -07:00
dependabot[bot]
cbd772623a build(deps): update pyo3 requirement from 0.23.5 to 0.24.0 (#375)
Updates the requirements on [pyo3](https://github.com/pyo3/pyo3) to permit the latest version.
- [Release notes](https://github.com/pyo3/pyo3/releases)
- [Changelog](https://github.com/PyO3/pyo3/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pyo3/pyo3/compare/v0.23.5...v0.24.0)

---
updated-dependencies:
- dependency-name: pyo3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-09 20:25:44 -07:00
thedavemarshall
a07beca983 Update ruby binding deps, ruby gem version 0.2.3 (#374)
* Update ruby binding deps, ruby gem version 0.2.3

* and gem version to 0.2.3

* specify bunlder and rubygems version for CI
2025-03-07 16:24:29 -08:00
Anand Krishnamoorthi
a3edb6c88c build(deps): update pyo3 requirement from 0.22.0 to 0.23.5 (#372)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2025-03-04 15:44:24 -08:00
Anand Krishnamoorthi
a1777fb7d3 build(deps): update rand requirement from 0.8.5 to 0.9.0 (#370)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2025-03-04 07:06:43 -08:00
dependabot[bot]
11aaa555aa build(deps): update cbindgen requirement from 0.27.0 to 0.28.0 (#361)
Updates the requirements on [cbindgen](https://github.com/mozilla/cbindgen) to permit the latest version.
- [Release notes](https://github.com/mozilla/cbindgen/releases)
- [Changelog](https://github.com/mozilla/cbindgen/blob/master/CHANGES)
- [Commits](https://github.com/mozilla/cbindgen/compare/v0.27.0...0.28.0)

---
updated-dependencies:
- dependency-name: cbindgen
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-03 16:23:44 -08:00
Amaury Chamayou
f1580a55a3 Fix typo in README.md (#366)
Force merging since it is only a typo fix.

Signed-off-by: Amaury Chamayou <amaury@xargs.fr>
2025-03-03 15:58:31 -08:00
Anand Krishnamoorthi
6174af1781 Update dependencies (#369)
Specify `js` feature for `uuid` when building wasm by
specifying it as a non-optional dependency in wasm binding's Cargo.toml.

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2025-03-03 15:53:47 -08:00
thedavemarshall
5fa55d7274 Fix clippy warning for result? (#362) 2025-01-21 08:37:54 -08:00
dependabot[bot]
748c11cfa1 build(deps): update itertools requirement from 0.13.0 to 0.14.0 (#357)
Updates the requirements on [itertools](https://github.com/rust-itertools/itertools) to permit the latest version.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-06 10:35:00 -08:00
dependabot[bot]
fb035d3d93 build(deps): update jsonschema requirement from 0.26.1 to 0.28.1 (#356)
Updates the requirements on [jsonschema](https://github.com/Stranger6667/jsonschema) to permit the latest version.
- [Release notes](https://github.com/Stranger6667/jsonschema/releases)
- [Changelog](https://github.com/Stranger6667/jsonschema/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stranger6667/jsonschema/compare/rust-v0.26.1...rust-v0.28.1)

---
updated-dependencies:
- dependency-name: jsonschema
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-06 10:01:06 -08:00
thedavemarshall
ba3a128e84 resolve anyhow compile errors (#355) 2025-01-06 09:13:01 -08:00
dependabot[bot]
d955ae10a5 build(deps): update prettydiff requirement from 0.7.0 to 0.8.0 (#348)
---
updated-dependencies:
- dependency-name: prettydiff
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-25 09:39:27 -08:00
57 changed files with 427 additions and 224 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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`

View File

@@ -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);

View File

@@ -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)

View File

@@ -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.

View File

@@ -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));

View File

@@ -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)
{

View File

@@ -23,6 +23,7 @@ var w = new Stopwatch();
w.Restart();
var engine = new Regorus.Engine();
engine.SetRegoV0(true);
w.Stop();
var newEngineTicks = w.ElapsedTicks;

View File

@@ -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);

View File

@@ -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"

View File

@@ -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;

View File

@@ -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{

View File

@@ -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))

View File

@@ -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"

View File

@@ -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 }"
);
}
}
}

View File

@@ -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,

View File

@@ -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.
*

View File

@@ -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"

View File

@@ -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())
}

View File

@@ -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)

View File

@@ -3,7 +3,7 @@ require:
- rubocop-rake
AllCops:
TargetRubyVersion: 3.0
TargetRubyVersion: 3.4
NewCops: enable
Layout/LineLength:

View File

@@ -1 +1 @@
ruby 3.3.1
ruby 3.4.2

View File

@@ -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

View File

@@ -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

View File

@@ -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')

View File

@@ -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"

View File

@@ -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(

View File

@@ -1,5 +1,5 @@
# frozen_string_literal: true
module Regorus
VERSION = "0.2.1"
VERSION = "0.3.0"
end

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,2 @@
[target.wasm32-unknown-unknown]
rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""]

View File

@@ -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)'] }

View File

@@ -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.

View File

@@ -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),

View 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...

View File

@@ -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)

View File

@@ -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")]

View File

@@ -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,

View File

@@ -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(&params[1], &args[1]) {

View File

@@ -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)
}

View File

@@ -158,7 +158,8 @@ fn parse_ns(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) ->
let layout = ensure_string(name, &params[0], &args[0])?;
let value = ensure_string(name, &params[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, &params[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())
}

View File

@@ -266,7 +266,7 @@ struct GoTimeFormatItems<'a> {
mode: GoTimeFormatItemsMode,
}
impl<'a> GoTimeFormatItems<'a> {
impl GoTimeFormatItems<'_> {
fn parse(reminder: &str) -> GoTimeFormatItems {
GoTimeFormatItems {
reminder,

View File

@@ -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, ""),
};

View File

@@ -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)
}

View File

@@ -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
/// }

View File

@@ -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);

View File

@@ -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");

View File

@@ -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)?

View File

@@ -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);

View File

@@ -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.

View File

@@ -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();

View File

@@ -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())?;

View File

@@ -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

View File

@@ -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")
}
};