Compare commits

...

97 Commits

Author SHA1 Message Date
Anand Krishnamoorthi
658f34753b chore: release (#260)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-30 06:35:12 -07:00
Anand Krishnamoorthi
5eda76fd00 Add release-plz config to publish only regorus package (#259)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-05-30 06:26:10 -07:00
Anand Krishnamoorthi
97914d5596 Revert "chore: release v0.2.0 (#257)" (#258)
This reverts commit ffb79f1b30.
2024-05-30 06:09:47 -07:00
Anand Krishnamoorthi
ffb79f1b30 chore: release v0.2.0 (#257)
v0.2.0

Signed-off-by: Anand Krishnamoorthi <35780660+anakrish@users.noreply.github.com>

---------

Signed-off-by: Anand Krishnamoorthi <35780660+anakrish@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-30 05:45:43 -07:00
Anand Krishnamoorthi
536c6ede5c Fix release-plz hash (#256)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-05-30 05:12:24 -07:00
Anand Krishnamoorthi
ff0adf054e every: non collections should evaluate to false (#253)
See https://github.com/open-policy-agent/opa/pull/6763

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-05-28 06:25:13 -07:00
Anand Krishnamoorthi
2b82f2842f Fix merge issue (#252)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-05-25 14:01:12 -07:00
Anand Krishnamoorthi
d09c445add Update bindings to include newer APIs (#250)
- c, cpp
- csharp
- ffi
- go
- Java
- Python
- WASM

`arc` feature is turned on for all bindings
Use pretty string instead of colored string.

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-05-25 10:24:06 -07:00
thedavemarshall
33fe9d5039 update ruby bindings version to 0.1.5, bump deps (#251)
* update ruby bindings version to 0.1.5, bump deps

* update ruby bindings for coverage and print methods
2024-05-24 07:57:39 -07:00
Anand Krishnamoorthi
3d08f6eef9 Use correct docsrs feature annotation (#248)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-05-23 08:31:09 -07:00
Anand Krishnamoorthi
3ac9b1121e Lockdown kata test prints as well as prints of various values (#249)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-05-23 08:30:41 -07:00
Anand Krishnamoorthi
9894f00829 Fix bindings and add CI tests (#247)
Also update version numbers of binding Rust projects to match Regorus

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-05-22 08:15:42 -07:00
thedavemarshall
495e91c75a Add test-ruby CI for github actions (#244)
https://github.com/oxidize-rb/actions/blob/main/setup-ruby-and-rust/readme.md
2024-05-20 15:53:36 -07:00
Burak
25ac1b6c95 Update README.md for Java bindings to mention we don't publish to (#246)
Maven Central currently
2024-05-20 08:15:39 -07:00
dependabot[bot]
209ece9dee Update itertools requirement from 0.12.1 to 0.13.0 (#245)
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.12.1...v0.13.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>
2024-05-19 20:33:23 -07:00
thedavemarshall
0952d42432 Update ruby bindings for add_policy and add_policy_from_file to return package name (#240) 2024-05-18 08:18:36 -07:00
Anand Krishnamoorthi
56bd8841d0 Provide a way to obtain package names of loaded policies (#239)
Engine::
- add_policy and add_policy_from_file return the package name.
- get_packages returns the names of loaded packages.

closes #229

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-05-18 08:02:54 -07:00
Anand Krishnamoorthi
67f65c0561 c_no_std binding to show use in C freestanding environments. (#238)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-05-17 18:31:11 -07:00
dependabot[bot]
5422bde391 Bump rexml in /bindings/ruby in the bundler group across 1 directory (#236)
Bumps the bundler group with 1 update in the /bindings/ruby directory: [rexml](https://github.com/ruby/rexml).


Updates `rexml` from 3.2.6 to 3.2.8
- [Release notes](https://github.com/ruby/rexml/releases)
- [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md)
- [Commits](https://github.com/ruby/rexml/compare/v3.2.6...v3.2.8)

---
updated-dependencies:
- dependency-name: rexml
  dependency-type: indirect
  dependency-group: bundler
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-17 07:25:21 -07:00
dependabot[bot]
68a0f8fcce Update prettydiff requirement from 0.6.4 to 0.7.0 (#234)
---
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-05-13 07:09:58 -07:00
dependabot[bot]
97b70a1c1b Update jsonschema requirement from 0.17.1 to 0.18.0 (#235)
Updates the requirements on [jsonschema](https://github.com/Stranger6667/jsonschema-rs) to permit the latest version.
- [Release notes](https://github.com/Stranger6667/jsonschema-rs/releases)
- [Changelog](https://github.com/Stranger6667/jsonschema-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stranger6667/jsonschema-rs/compare/rust-v0.17.1...rust-v0.18.0)

---
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>
2024-05-13 06:54:07 -07:00
Anand Krishnamoorthi
e86b590f91 no_std support (#232)
- Disable default features in dependencies
- Use anyhow::Error::msg to map errors. Note: anyhow will itself be removed later.
- lazy_static/spin_no_std used in no_std environments
- ensure_no_std binary is built to target  thumbv7m-none-eabi to ensure that
  there are no std dependencies.  thumbv7m-none-eabi target has no std support.
- The opa-no-std feature enables only those Regorus features that work with no_std.
- Enable tests with no_std
- Update sizes of regorus binary in  README.md
- Ensure that regorus example can be built with only std
- Ensure that regorus example can be built with no_std

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-05-13 06:42:35 -07:00
Anand Krishnamoorthi
01fc234a33 add std feature (#231)
- `std` feature is enabled by default
- By default enable #![no_std] compilation
- Import std create if `std` feature is enabled or if testing
- Use core, alloc types
- Make it clear where std types are being used
- In no std, use BTreeMap in place of HashMap.
   HashMap is not available in no std due to lack of a
   secure random number generator

Note: The project does not yet compile without std feature being specified.
But it's really close to being able to do so.

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-05-09 11:28:42 -07:00
Anand Krishnamoorthi
e9cd6d6afc Tests from MSFT fork of kata-containers (#230)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-05-08 17:38:50 -07:00
Anand Krishnamoorthi
69d6426663 Use alloc, core instead of std (#225)
- Replace std with alloc, core in most places in src
  Tests, bindings aren't changed.
- Introduce BuiltinsMap type alias inplace of HashMap.
  In no_std case, this could be aliases to BTreeMap
- Fix clippy warnings

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-05-07 18:41:09 -07:00
Anand Krishnamoorthi
0a39e434db chore: release (#226)
* chore: release
2024-05-07 18:26:50 -07:00
Anand Krishnamoorthi
9832a297ed Improve example in readme (#224)
- Use eval_rule
- Show functions add_policy, add_data, set_input
- Show Engine, Value types

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-05-02 12:46:01 -07:00
Anand Krishnamoorthi
c6fb8cf044 Add tests for kata containers policies (#221)
closes #220

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-04-27 06:31:09 -07:00
Anand Krishnamoorthi
55abbb2b42 Update to OPA v0.64.0 (#219)
Implement json.marshal_with_options builtin

closes #215, closes #218

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-04-26 04:52:11 -07:00
Anand Krishnamoorthi
3743f32edc Enable policy files greater than 64KB in size (#217)
fixes #214

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-04-26 01:04:21 -07:00
Anand Krishnamoorthi
744dad6126 OPA Conformance: Do not interpret # within regular string (#216)
fixes #213

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-04-25 23:51:52 -07:00
Anand Krishnamoorthi
7fde3382f6 chore: release (#210)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-04-22 15:09:01 -07:00
Anand Krishnamoorthi
316f3a7692 early return (#189)
If a rule is written to produce a constant value, then not all iterations of loops
within it need to be executed. Execution can stop via early return once the first iteration
that produces a value has been executed.

This brings forth the question : What if one of the subsequent iterations would have resulted
in an error?
e.g:
x {
  [1, "hello"][_] + 1
}

Such errors are not raised; consistent with OPA.

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-04-22 07:41:35 -07:00
Anand Krishnamoorthi
de56cce7cb Fix anyhow dependency issues (#208)
- Do not require backtrace feature
- Starting version 1.0.77, anyhow gathers backtrace is std feature (enabled by default)
  is specified even if backtrace feature is not enabled.
  Therefore specify default features as false.
- Specify version 1.0.45 since that is the minimul version required to successfully
  compile regorus

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-04-20 09:57:52 -07:00
Anand Krishnamoorthi
72ced23366 build: remove unused compact-rc dependency (#207)
Remove unused compact-rc dependency, to avoid a build error:

error[E0658]: use of unstable library feature 'ptr_addr_eq'
<...>/registry/src/index.crates.io-6f17d22bba15001f/compact-rc-0.5.4/src/base.rs:319:9
    |
319 |         std::ptr::addr_eq(Self::as_ptr(this), Self::as_ptr(other))

Signed-off-by: Dan Mihai <dmihai@microsoft.com>
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
Co-authored-by: Dan Mihai <dmihai@microsoft.com>
2024-04-20 06:47:18 -07:00
Anand Krishnamoorthi
3fa2847e6f chore: release (#205)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-04-11 07:03:10 -07:00
Anand Krishnamoorthi
82c86437cb Add a note in example to prefer eval_rule over eval_query (#204)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-04-11 06:38:45 -07:00
Anand Krishnamoorthi
05e91da06e Do not enable serde_json/arbitrary_precision by default (#203)
The feature does not interoperate well with other serde_json features like untagged enums.

Fixes #199

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-04-11 06:23:14 -07:00
Anand Krishnamoorthi
8c69dd491b Rewrite so that code compiles with chrono_tz 0.8.5 and 0.9.0 (#201)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-04-11 04:37:38 +05:30
thedavemarshall
6a167143cb update ruby bindings (#200) 2024-04-10 07:28:25 +05:30
Anand Krishnamoorthi
d2049d07f3 Store Value instances in AST for strings, numbers and idents (#197)
This avoids having to create value instances during evaluation

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-04-07 18:21:32 +05:30
Anand Krishnamoorthi
e326f3c629 From<serde_json::Value> and From<serde_yaml::Value> (#196)
Provide wrappers around serde_json::from_value and serde_yaml::from_value since
they may not be apparent and the user may end up serializing to json/yaml and
rereading as a regorus::Value

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-04-06 16:40:57 +05:30
Anand Krishnamoorthi
3c7674e7c2 Build dependency on git only if opa.runtime feature is enabled. (#194)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-04-03 10:17:51 +05:30
Anand Krishnamoorthi
947c9490fa Update to opa v0.63.0 (#192)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-04-03 09:28:55 +05:30
dependabot[bot]
0ebcb568cc Update pyo3 requirement from 0.20.2 to 0.21.0 (#190)
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.20.2...v0.21.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>
2024-04-02 08:14:50 +05:30
thedavemarshall
e86801bdd9 Ruby bindings for existing FFI methods, plus eval_rule() (#188)
* Ruby binding

* use relative regorus crate in ruby instead of a published version, also cargo fmt

* remove unnecessary Cargo.toml, include the top level Cargo.lock in ruby gem

* ruby bindings continued- add eval_rule, fix _json methods, update README.md

also added rubocop-minitest and rubocop-rake, and added more test coverage

* update README.md to include Ruby bindings

Closes #191
2024-04-02 08:14:29 +05:30
Anand Krishnamoorthi
3d98c3b12e eval_rule: Evaluate rules directly instead of queries (#186)
closes #185

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-03-26 05:24:21 +05:30
Anand Krishnamoorthi
330a6dff72 Remove cruft. (#184)
Logging wasn't implemented fully nor getting used much.

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-03-24 18:37:24 +05:30
Anand Krishnamoorthi
b80ef2d015 chore: release (#183)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-03-22 23:08:46 +05:30
Anand Krishnamoorthi
7e3fc08a14 Handle non simple refs in chained expressions (#182)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-03-15 07:13:54 -07:00
Anand Krishnamoorthi
48982222c5 Ability to gather print statements (#179)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-03-11 16:44:05 -07:00
Anand Krishnamoorthi
90757210bc Top-down evaluation (#177)
When executing a query, only those rules that are used
by the query will be evaluated.

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-03-10 19:46:11 -07:00
Anand Krishnamoorthi
7bc9a50a52 Make unary - operator OPA compatible. (#175)
OPA supports unary - operator only in the following cases:
-\s+numeric literal

We match OPAs behavior for now. This can be revisited later.
2024-03-10 09:03:11 -07:00
Burak
08f3007b5c Don't use deprecated chrono Duration methods (#173)
Some panicking methods on `Duration` are deprecated
as part of chrono `0.4.35`. We switched to use `Duration::try_*`
of those APIs.
2024-03-09 14:35:08 -08:00
Anand Krishnamoorthi
863601c2d5 Propagate Undefined in object expressions (#171)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-03-05 09:56:15 -08:00
Anand Krishnamoorthi
976c04be8a Bump to OPA v0.62.0 (#169)
No code changes seem to be needed

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-03-01 09:24:14 -08:00
Anand Krishnamoorthi
fbfed6b49c Fix regression (#164)
Second lookup of an object rule without fully qualified path, resulted
in returning the object instead of the requested field.

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-26 17:31:48 -08:00
Anand Krishnamoorthi
595f9d34d5 Separately keep track of whether rules have been evaluated or not (#163)
Previously we used to rely on whether there was a value in the
data document for a given rule path. This approach cannot handle
the case of evaluating a.b when a.b.c has been evaluated but
a.b.d has not been evaluated. Upon evaluating a.b.c, the data document
will already have a value of a.b even though a.b.d has not yet
been evaluated.

Hence we need to keep track of evaluated rules separately.

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-25 23:36:48 -08:00
Burak
a8c0588426 bindings/java: Link Linux libraries against glibc 2.17 using cargo-zigbuild (#158) 2024-02-23 10:25:32 -08:00
Anand Krishnamoorthi
0e053832db chore: release (#157)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-02-22 21:18:49 -08:00
Anand Krishnamoorthi
f51731e584 Handle else block without body (#155)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-22 21:04:20 -08:00
Anand Krishnamoorthi
10f2caf0c0 Ignore errors from builtin functions in non strict mode (#154)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-22 15:46:51 -08:00
Burak
22047287b4 Java publishing (#151)
* bindings/java: Add prefix to native methods

* bindings/java: Add javadocs and missing methods to Engine

* Setup publishing uber-JAR via GitHub workflow

* bindings/java: Update README

* bindings/java: Improve native library loading from JAR

* bindings/java: Fix usage of `working-directory`

* bindings/java: Pass required `distribution` parameter to `actions/setup-java@v4`

* bindings/java: Use Corretto distribution

This is because Microsoft doesn't provide JDK8,
see https://learn.microsoft.com/en-us/java/openjdk/download#openjdk-8.

* bindings/java: Install GCC toolchain for `aarch64-unknown-linux-gnu`

* bindings/java: Upload artifacts with different names from each step

* bindings/java: Upload built JARs to GitHub
2024-02-22 15:00:07 -08:00
Anand Krishnamoorthi
3a86c83827 Document coverage feature; Convenience query functions (#152)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-22 14:59:06 -08:00
Anand Krishnamoorthi
d3d5367fd4 Policy Coverage (#149)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-19 19:10:13 -08:00
Anand Krishnamoorthi
f3d9652a73 Initial implementation of policy coverage (#146)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-18 22:16:53 -08:00
Burak
bdb2aba596 Java bindings (#147) 2024-02-18 08:21:58 -08:00
Anand Krishnamoorthi
8d282f1ffd Preserve false in single-expression queries (#145)
Note: 1 = 2 is different from 1 == 2
See issue for details

fixes #144

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-16 06:11:07 -08:00
Anand Krishnamoorthi
7d32bd9377 Create rust-clippy.yml (#143) 2024-02-14 19:31:35 -08:00
Anand Krishnamoorthi
53b990f97d arc feature to enable using Engine and other data structures from multiple threads (#142)
* `arc` feature to make engine usable from multiple threads.

`arc` is turned on by default. When enabled, std::sync::Arc
will be used instead of std::rc::Rc. The former makes regorus
types like Engine, Value, ast nodes etc Send, allowing for
usability from multiple threads.
Arc would add a performance overhead though since the reference
counting will now become atomic.

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

* Make engine and related types Debug

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

* Input, Data as json. Evaluate bool queries.

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

---------

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-13 10:23:19 -08:00
Anand Krishnamoorthi
13eb06e4be genpolicy tweaks (#141)
Allow `import input` instead of erroring out.
This import is redundant and has no effect.

Emit `print` messages to stderr onstead of stdout.

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-11 16:52:12 -08:00
Anand Krishnamoorthi
3b2e639918 io.jwt.decode (#140)
* io.jwt.decode

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

* Update README

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

* Install musl-tools to compile ring crate

ring crate is a dependency of jsonwebtoken

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

---------

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-09 23:36:16 -08:00
Anand Krishnamoorthi
a381c38a90 Use compact_rc (#139)
There is not much use for weak_counts in Rc for us.

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-09 23:22:09 -08:00
Anand Krishnamoorthi
5044d54d18 Scripting tweaks (#138)
- No need to build with coverage by default on linux platforms
- Will add coverage formally in CI later
- Rename rust.yml to pr.yml

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-08 16:24:40 -08:00
Anand Krishnamoorthi
bb1b25ff2f chore: release (#137)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-02-08 07:09:22 -08:00
Anand Krishnamoorthi
5fd826d79a Update docs (#136)
* Add `time` to opa.passing. Disable WASM from rust.yml

Bindings will be tested using a separate workflow.
Also remove scripts that are no longer useful

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

* Remove alpha tag from version

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

---------

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-08 06:56:14 -08:00
Anand Krishnamoorthi
d4dcbe7b9e Update README. Add link to playground. (#135)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-07 22:18:31 -08:00
Anand Krishnamoorthi
fc68bf9c8b Add devcontainer (#133)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-07 15:40:50 -08:00
Anand Krishnamoorthi
a95a9d21b3 Ability to add custom builtin functions (#132)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-07 15:15:55 -08:00
Burak
5717f9c249 Partially implement Go's time format (#130)
* Partially implement Go's time format

* Parse date only values

* Fix leap year handling in `time.diff`

* Disable failing test case
2024-02-07 15:12:34 -08:00
Anand Krishnamoorthi
dda525b989 C++ binding (#129)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-05 00:22:27 -08:00
Anand Krishnamoorthi
22260ac46f Bindings for C, C#, Golang (#124)
* FFI bindings

Generate C FFI as well as C# FFI

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

* Regorus C binding

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

* C# binding

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

* Golang binding

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

---------

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-04 19:50:44 -08:00
dependabot[bot]
25b1ffe6d7 Update env_logger requirement from 0.10.0 to 0.11.1 (#123)
Updates the requirements on [env_logger](https://github.com/rust-cli/env_logger) to permit the latest version.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/env_logger/compare/v0.10.0...v0.11.1)

---
updated-dependencies:
- dependency-name: env_logger
  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-02-02 16:17:16 -08:00
Anand Krishnamoorthi
bcde71b8f0 Create dependabot.yml 2024-02-02 14:54:58 -08:00
Anand Krishnamoorthi
1ab27b253b chore: release (#121)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-02-01 16:16:33 -08:00
Anand Krishnamoorthi
761d11ef48 Document bindings (#119)
* Instructions for WASM/JS binding

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

* Document Python, WASM/JS bindings

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

---------

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-02-01 13:47:27 -08:00
Anand Krishnamoorthi
beea2274d3 Conform to OPA 0.61.0. (#118)
Implement `import rego.v1`
https://www.openpolicyagent.org/docs/latest/policy-language/#the-regov1-import

- `if` required before rule body
- import rego.v1 automatically imports future.keywords
- handle import shadowing
- data, input cannot be shadowed
- deprecated functions as disallowed
- rules must have assignment or body
- `contains` required for parital set

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-01-31 21:39:02 -08:00
Anand Krishnamoorthi
5799a3e6c4 Update publish-python.yml 2024-01-28 23:55:15 -08:00
Anand Krishnamoorthi
ca91c0ea20 Publish python packages (#117)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-01-28 23:12:06 -08:00
Anand Krishnamoorthi
bf75813c43 Publish wasm (#116)
* Create jekyll-gh-pages.yml

* Fix publish-wasm

- --release instead of -r
- set working directory
- use v4

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

---------

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-01-28 16:45:52 -08:00
Anand Krishnamoorthi
35ec9c03ad Set working-directory for wasm-pack 2024-01-28 15:30:41 -08:00
Anand Krishnamoorthi
8ca863c661 Python bindings (#115)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-01-28 14:39:59 -08:00
Anand Krishnamoorthi
055bdd295f WASM binding (#114)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-01-27 22:14:29 -08:00
Anand Krishnamoorthi
0af97840f7 chore: release (#112)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-01-19 15:07:49 -08:00
Anand Krishnamoorthi
6eca85b497 Improve crate documentation (#111)
- Document QueryResults
- Delete snippets folder
- Document Value

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-01-19 14:51:14 -08:00
Anand Krishnamoorthi
f3884e87e5 Try out manual trigger for release-plz (#110) 2024-01-16 10:49:39 -08:00
Anand Krishnamoorthi
d39200a52c - Document Location, Expression, QueryResult (#109)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2024-01-15 17:21:49 -08:00
224 changed files with 66221 additions and 1330 deletions

View File

@@ -1,14 +0,0 @@
[build]
#target = "x86_64-unknown-linux-musl"
# Flags to enable code-coverage for all builds.
# These can be removed later.
#rustflags = ["-Cinstrument-coverage"]
incremental = true
[env]
# Name of coverage instrumentation log file.
LLVM_PROFILE_FILE="target/cargo-test-%p-%m.profraw"
[target.x86_64-unknown-linux-gnu]
rustflags = ["-Cinstrument-coverage"]

View File

@@ -0,0 +1,35 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/rust
{
"name": "Rust",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/rust:1-1-bullseye",
"features": {
"ghcr.io/devcontainers/features/dotnet:2": {},
"ghcr.io/devcontainers/features/python:1": {}
}
// Use 'mounts' to make the cargo cache persistent in a Docker Volume.
// "mounts": [
// {
// "source": "devcontainer-cargo-cache-${devcontainerId}",
// "target": "/usr/local/cargo",
// "type": "volume"
// }
// ]
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "rustc --version",
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

11
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "cargo"
directory: "/" # Location of package manifests
schedule:
interval: "weekly"

39
.github/workflows/pr.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: tests/release
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Format Check
run: cargo fmt --check
- name: Build (all features)
run: cargo build -r --all-features
- name: Build
run: cargo build -r
- name: Test no_std
run: cargo test -r --no-default-features
- name: Build only std
run: cargo build -r --example regorus --no-default-features --features "std"
- name: Doc Tests
run: cargo test -r --doc
- name: Run tests
run: cargo test -r
- name: Run tests (ACI)
run: cargo test -r --test aci
- name: Run tests (KATA)
run: cargo test -r --test kata
- name: Run tests (OPA Conformance)
run: >-
cargo test -r --test opa --features opa-testutil,serde_json/arbitrary_precision -- $(tr '\n' ' ' < tests/opa.passing)

89
.github/workflows/publish-java.yml vendored Normal file
View File

@@ -0,0 +1,89 @@
name: publish-java
on: workflow_dispatch
permissions:
contents: read
jobs:
build:
name: Build for ${{ matrix.target }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
extension: so
build_cmd: zigbuild
glibc: "2.17"
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
extension: so
build_cmd: zigbuild
glibc: "2.17"
- target: x86_64-apple-darwin
os: macos-latest
extension: dylib
- target: aarch64-apple-darwin
os: macos-latest
extension: dylib
- target: x86_64-pc-windows-msvc
os: windows-latest
extension: dll
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-java@v4
with:
java-version: 8
distribution: "corretto"
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- if: ${{ matrix.build_cmd == 'zigbuild' }}
uses: actions/setup-python@v5
with:
python-version: "3.11"
- if: ${{ matrix.build_cmd == 'zigbuild' }}
run: pip install cargo-zigbuild
- run: cargo ${{ matrix.build_cmd || 'build' }} --release --target ${{ matrix.target }}${{ matrix.glibc && format('.{0}', matrix.glibc) || '' }} --manifest-path ./bindings/java/Cargo.toml
- run: mkdir -p native/${{ matrix.target }}
- run: mv target/${{ matrix.target }}/release/*.${{ matrix.extension }} ./native/${{ matrix.target }}/
- uses: actions/upload-artifact@v4
with:
name: native-libraries-${{ matrix.target }}
path: native/
release:
name: Release
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-java@v4
with:
java-version: 8
distribution: "corretto"
server-id: ossrh
server-username: MAVEN_USERNAME
server-password: MAVEN_PASSWORD
- uses: actions/download-artifact@v4
with:
pattern: native-libraries-*
merge-multiple: true
path: ./bindings/java/native/
- run: mvn package
working-directory: ./bindings/java
- uses: actions/upload-artifact@v4
with:
name: built-jars
path: ./bindings/java/target/regorus-java-*.jar
- run: mvn deploy
working-directory: ./bindings/java
env:
MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }}

114
.github/workflows/publish-python.yml vendored Normal file
View File

@@ -0,0 +1,114 @@
# This file is autogenerated by maturin v1.4.0
# To update, run
#
# maturin generate-ci --manifest-path bindings/python/Cargo.toml github
#
name: publish-python
on:
workflow_dispatch:
permissions:
contents: read
jobs:
linux:
runs-on: ubuntu-latest
strategy:
matrix:
target: [x86_64, x86, aarch64, armv7, s390x, ppc64le]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Build wheels
uses: PyO3/maturin-action@63b75c597b83e247fbf4fb7719801cc4220ae9f3 # v1.43.0
with:
target: ${{ matrix.target }}
args: --release --out dist --find-interpreter --manifest-path bindings/python/Cargo.toml
sccache: 'true'
manylinux: auto
- name: Upload wheels
uses: actions/upload-artifact@v3
with:
name: wheels
path: dist
windows:
runs-on: windows-latest
strategy:
matrix:
target: [x64, x86]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.10'
architecture: ${{ matrix.target }}
- name: Build wheels
uses: PyO3/maturin-action@63b75c597b83e247fbf4fb7719801cc4220ae9f3 # v1.43.0
with:
target: ${{ matrix.target }}
args: --release --out dist --find-interpreter --manifest-path bindings/python/Cargo.toml
sccache: 'true'
- name: Upload wheels
uses: actions/upload-artifact@v3
with:
name: wheels
path: dist
macos:
runs-on: macos-latest
strategy:
matrix:
target: [x86_64, aarch64, universal2-apple-darwin]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Build wheels
uses: PyO3/maturin-action@63b75c597b83e247fbf4fb7719801cc4220ae9f3 # v1.43.0
with:
target: ${{ matrix.target }}
args: --release --out dist --find-interpreter --manifest-path bindings/python/Cargo.toml
sccache: 'true'
- name: Upload wheels
uses: actions/upload-artifact@v3
with:
name: wheels
path: dist
sdist:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build sdist
uses: PyO3/maturin-action@63b75c597b83e247fbf4fb7719801cc4220ae9f3 # v1.43.0
with:
command: sdist
args: --out dist --manifest-path bindings/python/Cargo.toml
- name: Upload sdist
uses: actions/upload-artifact@v3
with:
name: wheels
path: dist
release:
name: Release
runs-on: ubuntu-latest
# Commented out for initial release.
# if: "startsWith(github.ref, 'refs/tags/')"
needs: [linux, windows, macos, sdist]
steps:
- uses: actions/download-artifact@v3
with:
name: wheels
- name: Publish to PyPI
uses: PyO3/maturin-action@63b75c597b83e247fbf4fb7719801cc4220ae9f3 # v1.43.0
env:
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
with:
command: upload
args: --non-interactive --skip-existing *

32
.github/workflows/publish-wasm.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: publish-wasm
permissions:
pull-requests: write
contents: write
on: workflow_dispatch
jobs:
publish-wasm:
name: publish
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v4
with:
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Build
run: wasm-pack build --target nodejs --release
working-directory: ./bindings/wasm
- name: Publish
run: wasm-pack publish --target nodejs
working-directory: ./bindings/wasm
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}

View File

@@ -4,10 +4,7 @@ permissions:
pull-requests: write
contents: write
on:
push:
branches:
- main
on: workflow_dispatch
jobs:
release-plz:
@@ -21,7 +18,7 @@ jobs:
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Run release-plz
uses: MarcoIeni/release-plz-action@v0.5
uses: MarcoIeni/release-plz-action@98b2b45b090aadf18cb662caaf3de6222d98822a #v0.5.60
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

54
.github/workflows/rust-clippy.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# rust-clippy is a tool that runs a bunch of lints to catch common
# mistakes in your Rust code and help improve your Rust code.
# More details at https://github.com/rust-lang/rust-clippy
# and https://rust-lang.github.io/rust-clippy/
name: rust-clippy analyze
on:
push:
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
workflow_dispatch:
jobs:
rust-clippy-analyze:
name: Run rust-clippy analyzing
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install Rust toolchain
uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af #@v1
with:
profile: minimal
toolchain: stable
components: clippy
override: true
- name: Install required cargo
run: cargo install clippy-sarif sarif-fmt
- name: Run rust-clippy
run:
cargo clippy
--all-features
--message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt
continue-on-error: true
- name: Upload analysis results to GitHub
uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: rust-clippy-results.sarif
wait-for-processing: true

View File

@@ -1,37 +0,0 @@
name: Rust
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Add musl target
run: rustup target add x86_64-unknown-linux-musl
- name: Format Check
run: cargo fmt --check
- name: Build
run: cargo build --verbose
- name: Build Tests
run: cargo build --all-targets --verbose
- name: Clippy
run: cargo clippy --all-targets --no-deps -- -Dwarnings
- name: Run tests
run: cargo test --verbose
- name: Build (MUSL)
run: cargo build --verbose --all-targets --target x86_64-unknown-linux-musl
- name: Run tests (MUSL)
run: cargo test --verbose --target x86_64-unknown-linux-musl
- name: Run tests (OPA Conformance)
run: >-
cargo test --test opa -- $(tr '\n' ' ' < tests/opa.passing)

43
.github/workflows/test-c-cpp.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: bindings/c-cpp
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup gcc, g++, cmake, ninja
run: sudo apt update && sudo apt install -y gcc g++ cmake ninja-build
- name: Test c binding
run: |
mkdir bindings/c/build
cd bindings/c/build
cmake -G Ninja ..
ninja
./regorus_test
- name: Test c-nostd binding
run: |
mkdir bindings/c-nostd/build
cd bindings/c-nostd/build
cmake -G Ninja ..
ninja
./regorus_test
- name: Test cpp binding
run: |
mkdir bindings/cpp/build
cd bindings/cpp/build
cmake -G Ninja ..
ninja
./regorus_test

26
.github/workflows/test-csharp.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: bindings/csharp
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-dotnet@v4
- name: Build
run: dotnet build
working-directory: ./bindings/csharp
- name: Run
run: LD_LIBRARY_PATH=. dotnet run
working-directory: ./bindings/csharp

22
.github/workflows/test-ffi.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: bindings/c-cpp
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Test FFI
run: |
cargo build -r
cargo clippy --all-targets --no-deps -- -Dwarnings
working-directory: ./bindings/ffi

33
.github/workflows/test-go.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: bindings/go
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
- name: Build ffi
run: cargo build -r
working-directory: ./bindings/ffi
- name: Test go
run: |
go mod tidy
go build
LD_LIBRARY_PATH=../../target/release ./regorus_test
working-directory: ./bindings/go

37
.github/workflows/test-java.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: bindings/java
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-java@v4
with:
java-version: 8
distribution: "corretto"
- uses: dtolnay/rust-toolchain@stable
- name: Building binding
run: |
cargo clippy --all-targets --no-deps -- -Dwarnings
cargo build --release --manifest-path bindings/java/Cargo.toml
- name: Build jar
run: mvn package
working-directory: ./bindings/java
- name: Test jar
run: |
javac -cp target/regorus-java-0.1.5.jar Test.java
java -Djava.library.path=../../target/release -cp target/regorus-java-0.1.5.jar:. Test
working-directory: ./bindings/java

33
.github/workflows/test-musl.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: musl
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Add musl target
run: rustup target add x86_64-unknown-linux-musl
- name: Install musl-gcc
run: sudo apt update && sudo apt install -y musl-tools
- name: Build (MUSL)
run: cargo build --verbose --all-targets --target x86_64-unknown-linux-musl
- name: Run tests (MUSL)
run: cargo test -r --verbose --target x86_64-unknown-linux-musl
- name: Run tests (MUSL ACI)
run: cargo test -r --test aci --target x86_64-unknown-linux-musl
- name: Run tests (KATA ACI)
run: cargo test -r --test kata --target x86_64-unknown-linux-musl
- name: Run tests (MUSL OPA Conformance)
run: >-
cargo test -r --test opa --features opa-testutil,serde_json/arbitrary_precision --target x86_64-unknown-linux-musl -- $(tr '\n' ' ' < tests/opa.passing)

24
.github/workflows/test-no-std.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: bindings/no-std
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Add no_std target
run: rustup target add thumbv7m-none-eabi
- name: Build
run: cargo build -r --target thumbv7m-none-eabi
working-directory: ./tests/ensure_no_std

38
.github/workflows/test-python.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
name: bindings/python
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
PYTHON_VERSION: "3.10"
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
- name: Build wheels
uses: PyO3/maturin-action@63b75c597b83e247fbf4fb7719801cc4220ae9f3 # v1.43.0
with:
target: x86_64
args: --release --out dist --manifest-path bindings/python/Cargo.toml
sccache: 'true'
- name: Test wheel
run: |
pip3 install dist/regorus-*.whl
cd bindings/python
cargo clippy --all-targets --no-deps -- -Dwarnings
python3 test.py

31
.github/workflows/test-ruby.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: bindings/ruby
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Ruby and Rust
uses: oxidize-rb/actions/setup-ruby-and-rust@7ca44a16e287e5ff7dd72ab53f4bd41cbf34a571 #v1.26
with:
ruby-version: "3.3.1"
rustup-toolchain: "stable"
bundler-cache: true
cargo-cache: true
working-directory: "bindings/ruby"
- name: Run ruby tests
run: |
cd bindings/ruby
cargo clippy --all-targets --no-deps -- -Dwarnings
bundle exec rake

32
.github/workflows/test-wasm.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: bindings/wasm
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 18
- name: Install wasmlpack
run: cargo install wasm-pack
- name: Test wasm binding
run: |
cd bindings/wasm
cargo clippy --all-targets --no-deps -- -Dwarnings
wasm-pack build --target nodejs --release
wasm-pack test --release --node
node test.js

37
.github/workflows/tests-debug.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: tests/debug
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build (all features)
run: cargo build --all-features
- name: Build
run: cargo build
- name: Test no_std
run: cargo test --no-default-features
- name: Build only std
run: cargo build --example regorus --no-default-features --features "std"
- name: Doc Tests
run: cargo test --doc
- name: Run tests
run: cargo test
- name: Run tests (ACI)
run: cargo test --test aci
- name: Run tests (KATA)
run: cargo test --test kata
- name: Run tests (OPA Conformance)
run: >-
cargo test --test opa --features opa-testutil,serde_json/arbitrary_precision -- $(tr '\n' ' ' < tests/opa.passing)

12
.gitignore vendored
View File

@@ -13,4 +13,14 @@ Cargo.lock
.vscode/
# worktrees
worktrees/
worktrees/
# build folders
**/build
# Generated C# bindings
**/*.g.cs
# Generated C, C++ headers
bindings/ffi/regorus.h
bindings/ffi/regorus.ffi.hpp

View File

@@ -6,6 +6,231 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.2.0](https://github.com/microsoft/regorus/compare/regorus-v0.1.5...regorus-v0.2.0) - 2024-05-30
### Other
- Add release-plz config to publish only regorus package ([#259](https://github.com/microsoft/regorus/pull/259))
- Revert "chore: release v0.2.0 ([#257](https://github.com/microsoft/regorus/pull/257))" ([#258](https://github.com/microsoft/regorus/pull/258))
- release v0.2.0 ([#257](https://github.com/microsoft/regorus/pull/257))
- Fix release-plz hash ([#256](https://github.com/microsoft/regorus/pull/256))
- non collections should evaluate to false ([#253](https://github.com/microsoft/regorus/pull/253))
- Fix merge issue ([#252](https://github.com/microsoft/regorus/pull/252))
- Update bindings to include newer APIs ([#250](https://github.com/microsoft/regorus/pull/250))
- update ruby bindings version to 0.1.5, bump deps ([#251](https://github.com/microsoft/regorus/pull/251))
- Use correct docsrs feature annotation ([#248](https://github.com/microsoft/regorus/pull/248))
- Lockdown kata test prints as well as prints of various values ([#249](https://github.com/microsoft/regorus/pull/249))
- Fix bindings and add CI tests ([#247](https://github.com/microsoft/regorus/pull/247))
- Add test-ruby CI for github actions ([#244](https://github.com/microsoft/regorus/pull/244))
- Update `README.md` for Java bindings to mention we don't publish to ([#246](https://github.com/microsoft/regorus/pull/246))
- Update itertools requirement from 0.12.1 to 0.13.0 ([#245](https://github.com/microsoft/regorus/pull/245))
- Update ruby bindings for add_policy and add_policy_from_file to return package name ([#240](https://github.com/microsoft/regorus/pull/240))
- Provide a way to obtain package names of loaded policies ([#239](https://github.com/microsoft/regorus/pull/239))
- `c_no_std` binding to show use in C freestanding environments. ([#238](https://github.com/microsoft/regorus/pull/238))
- Bump rexml in /bindings/ruby in the bundler group across 1 directory ([#236](https://github.com/microsoft/regorus/pull/236))
- Update prettydiff requirement from 0.6.4 to 0.7.0 ([#234](https://github.com/microsoft/regorus/pull/234))
- Update jsonschema requirement from 0.17.1 to 0.18.0 ([#235](https://github.com/microsoft/regorus/pull/235))
- no_std support ([#232](https://github.com/microsoft/regorus/pull/232))
- add `std` feature ([#231](https://github.com/microsoft/regorus/pull/231))
- Tests from MSFT fork of kata-containers ([#230](https://github.com/microsoft/regorus/pull/230))
- Use alloc, core instead of std ([#225](https://github.com/microsoft/regorus/pull/225))
## [0.1.5](https://github.com/microsoft/regorus/compare/regorus-v0.1.4...regorus-v0.1.5) - 2024-05-07
### Added
- Support policy files greater than 64KB in size ([#217](https://github.com/microsoft/regorus/pull/217))
- Add tests for kata containers policies ([#221](https://github.com/microsoft/regorus/pull/221))
- Support for OPA v0.64.0 ([#219](https://github.com/microsoft/regorus/pull/219))
- New builtin `json.marshal_with_options`
### Changed
- Improve example in readme ([#224](https://github.com/microsoft/regorus/pull/224))
### Fixed
- OPA Conformance: Do not interpret # within regular string ([#216](https://github.com/microsoft/regorus/pull/216))
## [0.1.4](https://github.com/microsoft/regorus/compare/regorus-v0.1.3...regorus-v0.1.4) - 2024-04-22
### Other
- early return ([#189](https://github.com/microsoft/regorus/pull/189))
- Fix anyhow dependency issues ([#208](https://github.com/microsoft/regorus/pull/208))
- remove unused compact-rc dependency ([#207](https://github.com/microsoft/regorus/pull/207))
## [0.1.3](https://github.com/microsoft/regorus/compare/regorus-v0.1.2...regorus-v0.1.3) - 2024-04-11
### Other
- Add a note in example to prefer eval_rule over eval_query ([#204](https://github.com/microsoft/regorus/pull/204))
- Do not enable serde_json/arbitrary_precision by default ([#203](https://github.com/microsoft/regorus/pull/203))
- Rewrite so that code compiles with chrono_tz 0.8.5 and 0.9.0 ([#201](https://github.com/microsoft/regorus/pull/201))
- update ruby bindings ([#200](https://github.com/microsoft/regorus/pull/200))
- Store Value instances in AST for strings, numbers and idents ([#197](https://github.com/microsoft/regorus/pull/197))
- :Value> and From<serde_yaml::Value> ([#196](https://github.com/microsoft/regorus/pull/196))
- Build dependency on git only if opa.runtime feature is enabled. ([#194](https://github.com/microsoft/regorus/pull/194))
- Update to opa v0.63.0 ([#192](https://github.com/microsoft/regorus/pull/192))
- Update pyo3 requirement from 0.20.2 to 0.21.0 ([#190](https://github.com/microsoft/regorus/pull/190))
- Ruby bindings for existing FFI methods, plus eval_rule() ([#188](https://github.com/microsoft/regorus/pull/188))
- Evaluate rules directly instead of queries ([#186](https://github.com/microsoft/regorus/pull/186))
- Remove cruft. ([#184](https://github.com/microsoft/regorus/pull/184))
## [0.1.2](https://github.com/microsoft/regorus/compare/regorus-v0.1.1...regorus-v0.1.2) - 2024-03-22
### Other
- Handle non simple refs in chained expressions ([#182](https://github.com/microsoft/regorus/pull/182))
- Ability to gather print statements ([#179](https://github.com/microsoft/regorus/pull/179))
- Top-down evaluation ([#177](https://github.com/microsoft/regorus/pull/177))
- Make unary `-` operator OPA compatible. ([#175](https://github.com/microsoft/regorus/pull/175))
- Don't use deprecated chrono `Duration` methods ([#173](https://github.com/microsoft/regorus/pull/173))
- Propagate Undefined in object expressions ([#171](https://github.com/microsoft/regorus/pull/171))
- Bump to OPA v0.62.0 ([#169](https://github.com/microsoft/regorus/pull/169))
- Fix regression ([#164](https://github.com/microsoft/regorus/pull/164))
- Separately keep track of whether rules have been evaluated or not ([#163](https://github.com/microsoft/regorus/pull/163))
- Link Linux libraries against glibc 2.17 using `cargo-zigbuild` ([#158](https://github.com/microsoft/regorus/pull/158))
## [0.1.1](https://github.com/microsoft/regorus/compare/regorus-v0.1.0...regorus-v0.1.1) - 2024-02-23
### Other
- Handle else block without body ([#155](https://github.com/microsoft/regorus/pull/155))
- Ignore errors from builtin functions in non strict mode ([#154](https://github.com/microsoft/regorus/pull/154))
- Java publishing ([#151](https://github.com/microsoft/regorus/pull/151))
- Document coverage feature; Convenience query functions ([#152](https://github.com/microsoft/regorus/pull/152))
- Policy Coverage ([#149](https://github.com/microsoft/regorus/pull/149))
- Initial implementation of policy coverage ([#146](https://github.com/microsoft/regorus/pull/146))
- Java bindings ([#147](https://github.com/microsoft/regorus/pull/147))
- Preserve false in single-expression queries ([#145](https://github.com/microsoft/regorus/pull/145))
- Create rust-clippy.yml ([#143](https://github.com/microsoft/regorus/pull/143))
- `arc` feature to enable using Engine and other data structures from multiple threads ([#142](https://github.com/microsoft/regorus/pull/142))
- genpolicy tweaks ([#141](https://github.com/microsoft/regorus/pull/141))
- io.jwt.decode ([#140](https://github.com/microsoft/regorus/pull/140))
- Use compact_rc ([#139](https://github.com/microsoft/regorus/pull/139))
- Scripting tweaks ([#138](https://github.com/microsoft/regorus/pull/138))
## [0.1.0-alpha.3](https://github.com/microsoft/regorus/compare/regorus-v0.1.0-alpha.2...regorus-v0.1.0-alpha.3) - 2024-02-01
### Fixed
- fix bitwise.and and add tests ([#19](https://github.com/microsoft/regorus/pull/19))
### Other
- Document bindings ([#119](https://github.com/microsoft/regorus/pull/119))
- Conform to OPA 0.61.0. ([#118](https://github.com/microsoft/regorus/pull/118))
- Update publish-python.yml
- Publish python packages ([#117](https://github.com/microsoft/regorus/pull/117))
- Publish wasm ([#116](https://github.com/microsoft/regorus/pull/116))
- Set working-directory for wasm-pack
- Python bindings ([#115](https://github.com/microsoft/regorus/pull/115))
- WASM binding ([#114](https://github.com/microsoft/regorus/pull/114))
- release ([#112](https://github.com/microsoft/regorus/pull/112))
- Improve crate documentation ([#111](https://github.com/microsoft/regorus/pull/111))
- Try out manual trigger for release-plz ([#110](https://github.com/microsoft/regorus/pull/110))
- - Document Location, Expression, QueryResult ([#109](https://github.com/microsoft/regorus/pull/109))
- Update Cargo.toml ([#108](https://github.com/microsoft/regorus/pull/108))
- Change version to `0.1.0-alpha.1` ([#107](https://github.com/microsoft/regorus/pull/107))
- Add crate documentation ([#106](https://github.com/microsoft/regorus/pull/106))
- Release preparation ([#105](https://github.com/microsoft/regorus/pull/105))
- Update READEME.md with current status, grammar etc. ([#102](https://github.com/microsoft/regorus/pull/102))
- Implement builtin `time.parse_duration_ns` method ([#100](https://github.com/microsoft/regorus/pull/100))
- Implement import keyword ([#101](https://github.com/microsoft/regorus/pull/101))
- OPA conformance: Pass refheads test suite ([#90](https://github.com/microsoft/regorus/pull/90))
- OPA conformance: Ensure that `withkeyword` OPA tests pass ([#88](https://github.com/microsoft/regorus/pull/88))
- Handle walk builtin as a loop expression ([#86](https://github.com/microsoft/regorus/pull/86))
- Implement most of the builtin `time` module ([#82](https://github.com/microsoft/regorus/pull/82))
- OPA Conformance
- OPA conformance ([#81](https://github.com/microsoft/regorus/pull/81))
- More OPA conformance ([#77](https://github.com/microsoft/regorus/pull/77))
- OPA conformance ([#71](https://github.com/microsoft/regorus/pull/71))
- Builtin UUID module ([#68](https://github.com/microsoft/regorus/pull/68))
- Add tests for builtin `string::format_int` method ([#65](https://github.com/microsoft/regorus/pull/65))
- More builtins and semantic improvements ([#66](https://github.com/microsoft/regorus/pull/66))
- More OPA conformance; in-progress: ability to trace interpreter ([#63](https://github.com/microsoft/regorus/pull/63))
- More OPA conformant semantics ([#62](https://github.com/microsoft/regorus/pull/62))
- Updated readme. Added bundle support. ([#61](https://github.com/microsoft/regorus/pull/61))
- crypto builtins ([#57](https://github.com/microsoft/regorus/pull/57))
- Regex and Glob builtins ([#56](https://github.com/microsoft/regorus/pull/56))
- Formalize concept of a Number ([#55](https://github.com/microsoft/regorus/pull/55))
- Lock down ACI tests and more OPA test folders ([#54](https://github.com/microsoft/regorus/pull/54))
- Fix scheduling regression ([#53](https://github.com/microsoft/regorus/pull/53))
- add full api to engine ([#50](https://github.com/microsoft/regorus/pull/50))
- Use Rc<str> instead of string. ([#52](https://github.com/microsoft/regorus/pull/52))
- More library functions ([#51](https://github.com/microsoft/regorus/pull/51))
- Added semver.is_valid and semver.compare ([#49](https://github.com/microsoft/regorus/pull/49))
- OPA conformance tests ([#45](https://github.com/microsoft/regorus/pull/45))
- Avoid dependency on `source lifetime. ([#43](https://github.com/microsoft/regorus/pull/43))
- Allow with modifier for builtin and user functions ([#42](https://github.com/microsoft/regorus/pull/42))
- Special cases of refs to data ([#41](https://github.com/microsoft/regorus/pull/41))
- Fix scheduling statements that don't create bindings ([#40](https://github.com/microsoft/regorus/pull/40))
- Ability to run the OPA testsuite ([#39](https://github.com/microsoft/regorus/pull/39))
- Engine ([#38](https://github.com/microsoft/regorus/pull/38))
- Use Ref for storing ast nodes in collections. ([#37](https://github.com/microsoft/regorus/pull/37))
- all, any deprecated functions ([#35](https://github.com/microsoft/regorus/pull/35))
- all, any deprecated functions ([#34](https://github.com/microsoft/regorus/pull/34))
- Improvements ([#33](https://github.com/microsoft/regorus/pull/33))
- Order query expression results ([#32](https://github.com/microsoft/regorus/pull/32))
- Scheduling of statements in user queries ([#31](https://github.com/microsoft/regorus/pull/31))
- eval, lex, parse commands ([#30](https://github.com/microsoft/regorus/pull/30))
- eval_user_query for OPA style results ([#29](https://github.com/microsoft/regorus/pull/29))
- Arity for builtins ([#28](https://github.com/microsoft/regorus/pull/28))
- Handle chained _ ([#27](https://github.com/microsoft/regorus/pull/27))
- Minimize PR 22 ([#26](https://github.com/microsoft/regorus/pull/26))
- improve errors location ([#23](https://github.com/microsoft/regorus/pull/23))
- Fix clippy warning ([#25](https://github.com/microsoft/regorus/pull/25))
- negation of an undefined value should return true ([#21](https://github.com/microsoft/regorus/pull/21))
- Ensure that scopes are cleaned up correctly upon error. ([#20](https://github.com/microsoft/regorus/pull/20))
- support of or-functions ([#18](https://github.com/microsoft/regorus/pull/18))
- Statement Scheduler Implementation
- Remove unnecessary lifetime
- json.filter, object.filter, object.get, object.keys, object.remove
- :to_number builtin
- :trace builtin
- bitwise builtins
- :print builtin
- Partial sprintf implementation.
- All string functions except sprintf. TODO: Add tests
- More string functions without tests
- More string functions
- concat and contains
- string concat (WIP)
- Support build on non Linux platforms
- Prepare for upstreaming
- Test for multi-assign
- Support dependencies between vars defined in same statement
- Statement scheduler (WIP)
- Print small-form table of files without 100% coverage.
- Code tweaks to improve coverage
- Tests for aggregates builtins
- Tests for numbers builtins
- Tests for arrays builtins
- Tests for types functions
- Destructuring of arrays and objects in some-in expressions
- `some .. in` implementation
- Fix key, value in membership and some-in
- refactor
- Arrays and Aggregates
- Implement `every` statement ([#4](https://github.com/microsoft/regorus/pull/4))
- Set loop index variable if not "_" ([#3](https://github.com/microsoft/regorus/pull/3))
- Allow comprehensions in default value. ([#2](https://github.com/microsoft/regorus/pull/2))
- Lock down numbers
- mod function
- Builtin functions for numbers (WIP)
- Implement comparison operators. Formalize semantics.
- Rework assign operations ([#6](https://github.com/microsoft/regorus/pull/6))
- Locked down supported values in default rule.
- Improvements to github workflow ([#4](https://github.com/microsoft/regorus/pull/4))
- Update name to regorus
- Update rust.yml
- Add simple git action
- Add missing config.toml
- Update license to MIT
- Code from github.com/anakrish/rego-rs
- SUPPORT.md committed
- SECURITY.md committed
- README.md committed
- LICENSE committed
- CODE_OF_CONDUCT.md committed
- Initial commit
## [0.1.0-alpha.2](https://github.com/microsoft/regorus/compare/v0.1.0-alpha.1...v0.1.0-alpha.2) - 2024-01-19
### Other
- Improve crate documentation ([#111](https://github.com/microsoft/regorus/pull/111))
- Try out manual trigger for release-plz ([#110](https://github.com/microsoft/regorus/pull/110))
- - Document Location, Expression, QueryResult ([#109](https://github.com/microsoft/regorus/pull/109))
## [0.1.0-alpha.1](https://github.com/microsoft/regorus/releases/tag/v0.1.0-alpha.1) - 2024-01-15
### Fixed

View File

@@ -1,37 +1,56 @@
[workspace]
members = [
"bindings/ffi",
"bindings/python",
"bindings/wasm",
"bindings/java",
"bindings/ruby/ext/regorusrb",
"tests/ensure_no_std",
]
[package]
name = "regorus"
description = "A fast, lightweight Rego (OPA policy language) interpreter"
version = "0.1.0-alpha.1"
version = "0.2.0"
edition = "2021"
license-file = "LICENSE"
repository = "https://github.com/microsoft/regorus"
keywords = ["interpreter", "opa", "policy-as-code", "rego"]
keywords = ["interpreter", "no_std", "opa", "policy-as-code", "rego"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["full-opa"]
[lib]
doctest = false
[features]
default = ["full-opa", "arc"]
arc = ["scientific/arc"]
base64 = ["dep:data-encoding"]
base64url = ["dep:data-encoding"]
coverage = []
crypto = ["dep:constant_time_eq", "dep:hmac", "dep:hex", "dep:md-5", "dep:sha1", "dep:sha2"]
deprecated = []
hex = ["dep:data-encoding"]
http = []
jwt = []
glob = ["dep:wax"]
graph = []
jsonschema = ["dep:jsonschema"]
jwt = ["dep:jsonwebtoken", "dep:data-encoding", "dep:itertools"]
no_std = ["lazy_static/spin_no_std"]
opa-runtime = []
regex = ["dep:regex"]
semver = ["dep:semver"]
std = ["rand/std", "rand/std_rng", "serde_json/std"]
time = ["dep:chrono", "dep:chrono-tz"]
uuid = ["dep:uuid"]
urlquery = ["dep:url"]
time = ["dep:chrono", "dep:chrono-tz"]
yaml = ["serde_yaml"]
full-opa = [
"base64",
"base64url",
"coverage",
"crypto",
"deprecated",
"glob",
@@ -43,56 +62,78 @@ full-opa = [
"opa-runtime",
"regex",
"semver",
"std",
"time",
"uuid",
"urlquery",
"yaml"
]
# Features that can be used in no_std environments.
# Note that: the spin_no_std feature in lazy_static must be specified.
opa-no-std = [
"arc",
"base64",
"base64url",
"coverage",
"crypto",
"deprecated",
"graph",
"hex",
"no_std",
"opa-runtime",
"regex",
"semver",
# Configure lazy_static to use spinlocks.
"lazy_static/spin_no_std"
]
# This feature enables some testing utils for OPA tests.
opa-testutil = []
rand = ["dep:rand"]
[dependencies]
anyhow = {version = "1.0.66", features = ["backtrace"] }
serde = {version = "1.0.150", features = ["derive", "rc"] }
serde_json = {version = "1.0.89", features = ["arbitrary_precision"] }
serde_yaml = {version = "0.9.16", optional = true }
log = "0.4.17"
env_logger="0.10.0"
lazy_static = "1.4.0"
rand = "0.8.5"
num = "0.4.1"
anyhow = { version = "1.0.45", default-features = false }
serde = {version = "1.0.150", default-features = false, features = ["derive", "rc"] }
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}
hmac = {version = "0.12.1", optional = true}
sha2 = {version= "0.10.8", optional = true}
hex = {version = "0.4.3", optional = true}
sha1 = {version = "0.10.6", optional = true}
md-5 = {version = "0.10.6", optional = true}
constant_time_eq = {version = "0.3.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 }
data-encoding = { version = "2.4.0", optional = true, default-features=false, features = ["alloc"] }
scientific = { version = "0.5.2" }
regex = {version = "1.10.2", optional = true}
semver = {version = "1.0.20", optional = true}
regex = {version = "1.10.2", optional = true, default-features = false }
semver = {version = "1.0.20", 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", features = ["v4", "fast-rng"], optional = true }
jsonschema = { version = "0.17.1", default-features = false, optional = true }
uuid = { version = "1.6.1", default-features = false, features = ["v4", "fast-rng"], optional = true }
jsonschema = { version = "0.18.0", default-features = false, optional = true }
chrono = { version = "0.4.31", optional = true }
chrono-tz = { version = "0.8.5", optional = true }
jsonwebtoken = { version = "9.2.0", optional = true }
itertools = { version = "0.13.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 }
[dev-dependencies]
anyhow = "1.0.45"
cfg-if = "1.0.0"
clap = { version = "4.4.7", features = ["derive"] }
colored-diff = "0.2.3"
prettydiff = { version = "0.7.0", default-features = false }
serde_yaml = "0.9.16"
test-generator = "0.3.1"
walkdir = "2.3.2"
[build-dependencies]
anyhow = "1.0.66"
anyhow = "1.0"
[profile.release]
debug = true
@@ -107,3 +148,20 @@ required-features = ["full-opa"]
name="aci"
harness=false
test=false
[[test]]
name="kata"
harness=false
test=false
[[example]]
name="regorus"
harness=false
test=false
doctest=false
[package.metadata.docs.rs]
# To build locally:
# RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features --no-deps
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

174
README.md
View File

@@ -2,46 +2,136 @@
**Regorus** is
- *Rego*-*Rus(t)* - A fast, light-weight [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/) interpreter written in Rust.
- *Rego*-*Rus(t)* - A fast, light-weight [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/)
interpreter written in Rust.
- *Rigorous* - A rigorous enforcer of well-defined Rego semantics.
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.64.0](https://github.com/open-policy-agent/opa/releases/tag/v0.64.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.
- *polyglot* - In addition to Rust, Regorus can be used from *C*, *C++*, *C#*, *Golang*, *Java*, *Javascript*, *Python*, and *Ruby*.
This is made possible by the excellent FFI tools available in the Rust ecosystem. See [bindings](#bindings) for information on how to use Regorus from different languages.
To try out a *Javascript(WASM)* compiled version of Regorus from your browser, visit [Regorus Playground](https://anakrish.github.io/regorus-playground/).
Regorus is available as a library that can be easily integrated into your Rust projects.
Here is an example of evaluating a simple Rego policy:
```rust
use anyhow::Result;
use regorus::*;
use serde_json;
fn main() -> anyhow::Result<()> {
// Create an engine for evaluating Rego policies.
let mut engine = regorus::Engine::new();
fn main() -> Result<()> {
// Create an engine for evaluating Rego policies.
let mut engine = Engine::new();
let policy = String::from(
r#"
package example
import rego.v1
// Add policy to the engine.
engine.add_policy(
// Filename to be associated with the policy.
"hello.rego".to_string(),
allow if {
## All actions are allowed for admins.
input.principal == "admin"
} else if {
## Check if action is allowed for given user.
input.action in data.allowed_actions[input.principal]
}
"#,
);
// Rego policy that just sets a message.
r#"
package test
message = "Hello, World!"
"#.to_string()
)?;
// Add policy to the engine.
engine.add_policy(String::from("policy.rego"), policy)?;
// Evaluate the policy, fetch the message and print it.
let results = engine.eval_query("data.test.message".to_string(), false)?;
println!("{}", serde_json::to_string_pretty(&results)?);
// Add data to engine.
engine.add_data(regorus::Value::from_json_str(
r#"{
"allowed_actions": {
"user1" : ["read", "write"],
"user2" : ["read"]
}}"#,
)?)?;
Ok(())
// Set input and evaluate whether user1 can write.
engine.set_input(regorus::Value::from_json_str(
r#"{
"principal": "user1",
"action": "write"
}"#,
)?);
let r = engine.eval_rule(String::from("data.example.allow"))?;
assert_eq!(r, regorus::Value::from(true));
// Set input and evaluate whether user2 can write.
engine.set_input(regorus::Value::from_json_str(
r#"{
"principal": "user2",
"action": "write"
}"#,
)?);
let r = engine.eval_rule(String::from("data.example.allow"))?;
assert_eq!(r, regorus::Value::Undefined);
Ok(())
}
```
Regorus passes the [OPA v0.60.0 test-suite](https://www.openpolicyagent.org/docs/latest/ir/#test-suite) barring a few builtins.
See [OPA Conformance](#opa-conformance) below.
Regorus is designed with [Confidential Computing](https://confidentialcomputing.io/about/) in mind. In Confidential Computing environments,
it is important to be able to control exactly what is being run. Regorus allows enabling and disabling various components using cargo
features. By default all features are enabled.
The default build of regorus example program is 6.3M:
```bash
$ cargo build -r --example regorus; strip target/release/examples/regorus; ls -lh target/release/examples/regorus
-rwxr-xr-x 1 anand staff 6.3M May 11 22:03 target/release/examples/regorus*
```
When all default features are disabled, the binary size drops down to 1.9M.
```bash
$ cargo build -r --example regorus --no-default-features; strip target/release/examples/regorus; ls -lh target/release/examples/regorus
-rwxr-xr-x 1 anand staff 1.9M May 11 22:04 target/release/examples/regorus*
```
Regorus passes the [OPA v0.64.0 test-suite](https://www.openpolicyagent.org/docs/latest/ir/#test-suite) barring a few
builtins. See [OPA Conformance](#opa-conformance) below.
## Bindings
Regorus can be used from a variety of languages:
- *C*: C binding is generated using [cbindgen](https://github.com/mozilla/cbindgen).
[corrosion-rs](https://github.com/corrosion-rs/corrosion) can be used to seamlessly use Regorous
in your CMake based projects. See [bindings/c](https://github.com/microsoft/regorus/tree/main/bindings/c).
- *C freestanding*: [bindings/c_no_std](https://github.com/microsoft/regorus/tree/main/bindings/c_no_std) shows how to use Regorus from C environments without a libc.
- *C++*: C++ binding is generated using [cbindgen](https://github.com/mozilla/cbindgen).
[corrosion-rs](https://github.com/corrosion-rs/corrosion) can be used to seamlessly use Regorous
in your CMake based projects. See [bindings/cpp](https://github.com/microsoft/regorus/tree/main/bindings/cpp).
- *C#*: C# binding is generated using [csbindgen](https://github.com/Cysharp/csbindgen). See [bindings/csharp](https://github.com/microsoft/regorus/tree/main/bindings/csharp) for an example of how to build and use Regorus in your C# projects.
- *Golang*: The C bindings are exposed to Golang via [CGo](https://pkg.go.dev/cmd/cgo). See [bindings/go](https://github.com/microsoft/regorus/tree/main/bindings/go) for an example of how to build and use Regorus in your Go projects.
- *Python*: Python bindings are generated using [pyo3](https://github.com/PyO3/pyo3). Wheels are created using [maturin](https://github.com/PyO3/maturin). See [bindings/python](https://github.com/microsoft/regorus/tree/main/bindings/python).
- *Java*: Java bindings are developed using [jni-rs](https://github.com/jni-rs/jni-rs).
See [bindings/java](https://github.com/microsoft/regorus/tree/main/bindings/java).
- *Javascript*: Regorus is compiled to WASM using [wasmpack](https://github.com/rustwasm/wasm-pack).
See [bindings/wasm](https://github.com/microsoft/regorus/tree/main/bindings/wasm) for an example of using Regorus from nodejs.
To try out a *Javascript(WASM)* compiled version of Regorus from your browser, visit [Regorus Playground](https://anakrish.github.io/regorus-playground/).
- *Ruby*: Ruby bindings are developed using [magnus](https://github.com/matsadler/magnus).
See [bindings/ruby](https://github.com/microsoft/regorus/tree/main/bindings/ruby).
To avoid operational overhead, we currently don't publish these bindings to various repositories.
It is straight-forward to build these bindings yourself.
## Getting Started
[examples/regorus](https://github.com/microsoft/regorus/blob/main/examples/regorus.rs) is an example program that shows how to integrate Regorus into your project and evaluate Rego policies.
[examples/regorus](https://github.com/microsoft/regorus/blob/main/examples/regorus.rs) is an example program that
shows how to integrate Regorus into your project and evaluate Rego policies.
To build and install it, do
@@ -94,7 +184,8 @@ This produces the following output
}
```
Next, evaluate a sample [policy](examples/example.rego) and [input](examples/input.json) (borrowed from [Rego tutorial](https://www.openpolicyagent.org/docs/latest/#2-try-opa-eval)):
Next, evaluate a sample [policy](https://github.com/microsoft/regorus/blob/main/examples/example.rego) and [input](https://github.com/microsoft/regorus/blob/main/examples/input.json)
(borrowed from [Rego tutorial](https://www.openpolicyagent.org/docs/latest/#2-try-opa-eval)):
```bash
$ regorus eval -d examples/example.rego -i examples/input.json data.example
@@ -106,6 +197,23 @@ Finally, evaluate real-world [policies](tests/aci/) used in Azure Container Inst
$ regorus eval -b tests/aci -d tests/aci/data.json -i tests/aci/input.json data.policy.mount_overlay=x
```
## Policy coverage
Regorus allows determining which lines of a policy have been executed using the `coverage` feature (enabled by default).
We can try it out using the `regorus` example program by passing in the `--coverage` flag.
```shell
$ regorus eval -d examples/example.rego -i examples/input.json data.example --coverage
```
It produces the following coverage report which shows that all lines are executed except the line that sets `allow` to true.
![coverage.png](https://github.com/microsoft/regorus/blob/main/docs/coverage.png?raw=true)
See [Engine::get_coverage_report](https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_coverage_report) for details.
Policy coverage information is useful for debugging your policy as well as to write tests for your policy so that all
lines of the policy are exercised by the tests.
## ACI Policies
@@ -168,17 +276,17 @@ 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.60.0](https://github.com/open-policy-agent/opa/releases/tag/v0.60.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.
Regorus has been verified to be compliant with [OPA v0.64.0](https://github.com/open-policy-agent/opa/releases/tag/v0.64.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:
```bash
$ cargo test -r --test opa
$ cargo test -r --test opa --features opa-testutil,serde_json/arbitrary_precision
```
Currently, Regorus passes all the non-builtin specific tests. See [passing tests suites](https://github.com/microsoft/regorus/blob/main/tests/opa.passing).
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:
- `cryptoparsersaprivatekeys`
@@ -191,7 +299,6 @@ The following test suites don't pass fully due to mising builtins:
- `graphql`
- `invalidkeyerror`
- `jsonpatch`
- `jwtbuiltins`
- `jwtdecodeverify`
- `jwtencodesign`
- `jwtencodesignraw`
@@ -212,14 +319,15 @@ The following test suites don't pass fully due to mising builtins:
- `regometadatarule`
- `regoparsemodule`
- `rendertemplate`
- `time`
They are captured in the following [github issues](https://github.com/microsoft/regorus/issues?q=is%3Aopen+is%3Aissue+label%3Alib).
### Grammar
The grammar used by Regorus to parse Rego policies is described in [grammar.md](https://github.com/microsoft/regorus/blob/main/docs/grammar.md) in both [W3C EBNF](https://www.w3.org/Notation.html) and [RailRoad Diagram](https://en.wikipedia.org/wiki/Syntax_diagram) formats.
The grammar used by Regorus to parse Rego policies is described in [grammar.md](https://github.com/microsoft/regorus/blob/main/docs/grammar.md)
in both [W3C EBNF](https://www.w3.org/Notation.html) and [RailRoad Diagram](https://en.wikipedia.org/wiki/Syntax_diagram) formats.
## Contributing

View File

@@ -0,0 +1,41 @@
# Copyright (c) Microsoft
# Licensed under the MIT License.
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
include(FetchContent)
FetchContent_Declare(
Corrosion
GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
GIT_TAG v0.4 # Optionally specify a commit hash, version tag or branch here
)
FetchContent_MakeAvailable(Corrosion)
project("regorus-test")
corrosion_import_crate(
# Path to <regorus-source-folder>/bindings/ffi/Cargo.toml
MANIFEST_PATH "../ffi/Cargo.toml"
# Always build regorus in Release mode.
PROFILE "release"
# Only build the "regorus-ffi" crate.
CRATES "regorus-ffi"
# Turn off std support in regorus-ffi.
NO_DEFAULT_FEATURES
NO_STD
# custom_allocator allows using a custom memory allocator.
# To use malloc/free remove custom_allocator below.
# Additionally, select specific features in regorus.
# See regorus/opa_no_std
FEATURES "custom_allocator,regorus/semver"
# Link statically
CRATE_TYPES staticlib FLAGS --crate-type=staticlib
)
add_executable(regorus_test main.c)
# Add path to <regorus-source-folder>/bindings/ffi
target_include_directories(regorus_test PRIVATE "../ffi")
target_link_libraries(regorus_test regorus-ffi)

98
bindings/c-nostd/main.c Normal file
View File

@@ -0,0 +1,98 @@
#include <stdio.h>
#include "regorus.h"
// Regorus has been built for no_std and cannot access files.
char* file_to_string(const char* file) {
char * buffer = 0;
long length;
FILE * f = fopen (file, "rb");
if (f)
{
fseek (f, 0, SEEK_END);
length = ftell (f);
fseek (f, 0, SEEK_SET);
buffer = malloc (length + 1);
buffer[length] = '\0';
if (buffer)
{
fread (buffer, 1, length, f);
}
fclose (f);
}
return buffer;
}
// 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);
}
void regorus_free(uint8_t* ptr) {
free(ptr);
}
int main() {
// Create engine.
RegorusEngine* engine = regorus_engine_new();
RegorusResult r;
char* buffer = NULL;
// Load policies.
r = regorus_engine_add_policy(engine, "framework.rego", (buffer = file_to_string("../../../tests/aci/framework.rego")));
free(buffer);
if (r.status != RegorusStatusOk)
goto error;
printf("Loaded package %s\n", r.output);
regorus_result_drop(r);
r = regorus_engine_add_policy(engine, "api.rego", (buffer = file_to_string("../../../tests/aci/api.rego")));
free(buffer);
if (r.status != RegorusStatusOk)
goto error;
printf("Loaded package %s\n", r.output);
regorus_result_drop(r);
r = regorus_engine_add_policy(engine, "policy.rego", (buffer = file_to_string("../../../tests/aci/policy.rego")));
free(buffer);
if (r.status != RegorusStatusOk)
goto error;
printf("Loaded package %s\n", r.output);
regorus_result_drop(r);
// Add data
r = regorus_engine_add_data_json(engine, (buffer = file_to_string("../../../tests/aci/data.json")));
free(buffer);
if (r.status != RegorusStatusOk)
goto error;
regorus_result_drop(r);
// Set input
r = regorus_engine_set_input_json(engine, (buffer = file_to_string("../../../tests/aci/input.json")));
free(buffer);
if (r.status != RegorusStatusOk)
goto error;
regorus_result_drop(r);
// Eval rule.
r = regorus_engine_eval_rule(engine, "data.framework.mount_overlay");
if (r.status != RegorusStatusOk)
goto error;
// Print output
printf("%s", r.output);
regorus_result_drop(r);
// Free the engine.
regorus_engine_drop(engine);
return 0;
error:
printf("%s", r.error_message);
return 1;
}

34
bindings/c/CMakeLists.txt Normal file
View File

@@ -0,0 +1,34 @@
# Copyright (c) Microsoft
# Licensed under the MIT License.
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
include(FetchContent)
FetchContent_Declare(
Corrosion
GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
GIT_TAG v0.4 # Optionally specify a commit hash, version tag or branch here
)
FetchContent_MakeAvailable(Corrosion)
project("regorus-test")
corrosion_import_crate(
# Path to <regorus-source-folder>/bindings/ffi/Cargo.toml
MANIFEST_PATH "../ffi/Cargo.toml"
# Always build regorus in Release mode.
PROFILE "release"
# Only build the "regorus-ffi" crate.
CRATES "regorus-ffi"
# Select specific features in regorus.
FEATURES "regorus/semver"
# Link statically
CRATE_TYPES "cdylib"
)
add_executable(regorus_test main.c)
# Add path to <regorus-source-folder>/bindings/ffi
target_include_directories(regorus_test PRIVATE "../ffi")
target_link_libraries(regorus_test regorus-ffi)

97
bindings/c/main.c Normal file
View File

@@ -0,0 +1,97 @@
#include <stdio.h>
#include "regorus.h"
int main() {
// Create engine.
RegorusEngine* engine = regorus_engine_new();
RegorusResult r;
// Load policies.
r = regorus_engine_add_policy_from_file(engine, "../../../tests/aci/framework.rego");
if (r.status != RegorusStatusOk)
goto error;
printf("Loaded package %s\n", r.output);
regorus_result_drop(r);
r = regorus_engine_add_policy_from_file(engine, "../../../tests/aci/api.rego");
if (r.status != RegorusStatusOk)
goto error;
printf("Loaded package %s\n", r.output);
regorus_result_drop(r);
r = regorus_engine_add_policy_from_file(engine, "../../../tests/aci/policy.rego");
if (r.status != RegorusStatusOk)
goto error;
printf("Loaded package %s\n", r.output);
regorus_result_drop(r);
// Add data
r = regorus_engine_add_data_from_json_file(engine, "../../../tests/aci/data.json");
if (r.status != RegorusStatusOk)
goto error;
regorus_result_drop(r);
// Set input
r = regorus_engine_set_input_from_json_file(engine, "../../../tests/aci/input.json");
if (r.status != RegorusStatusOk)
goto error;
regorus_result_drop(r);
// Eval rule.
r = regorus_engine_eval_query(engine, "data.framework.mount_overlay");
if (r.status != RegorusStatusOk)
goto error;
// Print output
printf("%s\n", r.output);
regorus_result_drop(r);
// Free the engine.
regorus_engine_drop(engine);
// Create another engine.
engine = regorus_engine_new();
r = regorus_engine_add_policy(
engine,
"test.rego",
"package test\n"
"x = 1\n"
"message = `Hello`"
);
// Evaluate rule.
if (r.status != RegorusStatusOk)
goto error;
r = regorus_engine_set_enable_coverage(engine, true);
regorus_result_drop(r);
r = regorus_engine_eval_query(engine, "data.test.message");
if (r.status != RegorusStatusOk)
goto error;
// Print output
printf("%s\n", r.output);
regorus_result_drop(r);
// Print pretty coverage report.
r = regorus_engine_get_coverage_report_pretty(engine);
if (r.status != RegorusStatusOk)
goto error;
printf("%s\n", r.output);
regorus_result_drop(r);
// Free the engine.
regorus_engine_drop(engine);
return 0;
error:
printf("%s", r.error_message);
regorus_result_drop(r);
regorus_engine_drop(engine);
return 1;
}

View File

@@ -0,0 +1,34 @@
# Copyright (c) Microsoft
# Licensed under the MIT License.
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
include(FetchContent)
FetchContent_Declare(
Corrosion
GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
GIT_TAG v0.4 # Optionally specify a commit hash, version tag or branch here
)
FetchContent_MakeAvailable(Corrosion)
project("regorus-test")
set(CMAKE_CXX_STANDARD 17)
corrosion_import_crate(
# Path to <regorus-source-folder>/bindings/ffi/Cargo.toml
MANIFEST_PATH "../ffi/Cargo.toml"
# Always build regorus in Release mode.
PROFILE "release"
# Only build the "regorus-ffi" crate.
CRATES "regorus-ffi"
# Select specific features in regorus.
FEATURES "regorus/semver"
# Link statically
CRATE_TYPES "cdylib")
add_executable(regorus_test main.cpp)
# Add path to <regorus-source-folder>/bindings/ffi
target_include_directories(regorus_test PRIVATE "../ffi")
target_link_libraries(regorus_test regorus-ffi)

129
bindings/cpp/main.cpp Normal file
View File

@@ -0,0 +1,129 @@
#include <iostream>
#include "regorus.hpp"
void example()
{
// Create engine
regorus::Engine engine;
engine.set_enable_coverage(true);
// Add policies.
engine.add_policy("objects.rego",R"(package objects
rect := {`width`: 2, "height": 4}
cube := {"width": 3, `height`: 4, "depth": 5}
a := 42
b := false
c := null
d := {"a": a, "x": [b, c]}
index := 1
shapes := [rect, cube]
names := ["prod", `smoke1`, "dev"]
sites := [{"name": "prod"}, {"name": names[index]}, {"name": "dev"}]
e := {
a: "foo",
"three": c,
names[2]: b,
"four": d,
}
f := e["dev"])");
// Add data.
engine.add_data_json(R"({
"one": {
"bar": "Foo",
"baz": 5,
"be": true,
"bop": 23.4
},
"two": {
"bar": "Bar",
"baz": 12.3,
"be": false,
"bop": 42
}
})");
engine.add_data_json(R"({
"three": {
"bar": "Baz",
"baz": 15,
"be": true,
"bop": 4.23
}
})");
// Set input.
engine.set_input_json(R"({
"a": 10,
"b": "20",
"c": 30.0,
"d": true
})");
// Eval query.
auto result = engine.eval_query("[data.one, input.b, data.objects.sites[1]] = x");
if (result) {
std::cout<<result.output()<<std::endl;
} else {
std::cerr<<result.error()<<std::endl;
}
// Print coverage report
auto result1 = engine.get_coverage_report_pretty();
if (result1) {
std::cout<<result1.output()<<std::endl;
} else {
std::cerr<<result1.error()<<std::endl;
}
}
int main() {
// Create engine.
regorus::Engine engine;
// Load policies.
const char* policies[] = {
"../../../tests/aci/framework.rego",
"../../../tests/aci/policy.rego",
"../../../tests/aci/api.rego",
};
// Add policies and data.
for (auto policy : policies) {
auto result = engine.add_policy_from_file(policy);
if (!result) {
std::cerr<<result.error()<<std::endl;
return -1;
}
std::cout<<"Loaded package "<<result.output()<< std::endl;
}
{
auto result = engine.add_data_from_json_file("../../../tests/aci/data.json");
if (!result) {
std::cerr<<result.error()<<std::endl;
return -1;
}
}
// Set input and eval rule.
{
auto result = engine.set_input_from_json_file("../../../tests/aci/input.json");
if (!result) {
std::cerr<<result.error()<<std::endl;
return -1;
}
}
auto result = engine.eval_rule("data.framework.mount_overlay");
if (!result) {
std::cerr<<result.error()<<std::endl;
return -1;
}
std::cout<<result.output()<<std::endl;
example();
}

121
bindings/cpp/regorus.hpp Normal file
View File

@@ -0,0 +1,121 @@
#ifndef REGORUS_WRAPPER_HPP
#define REGORUS_WRAPPER_HPP
#include <memory>
#include <variant>
#include "regorus.ffi.hpp"
namespace regorus {
class Result {
public:
operator bool() const { return result.status == RegorusStatus::RegorusStatusOk; }
bool operator !() const { return result.status != RegorusStatus::RegorusStatusOk; }
const char* output() const {
if (*this && result.output) {
return result.output;
} else {
return "";
}
}
const char* error() const {
if (!*this && result.error_message) {
return result.error_message;
} else {
return "";
}
}
~Result() {
regorus_result_drop(result);
}
private:
friend class Engine;
RegorusResult result;
Result(RegorusResult r) : result(r) {}
private:
Result(const Result&) = delete;
Result(Result&&) = delete;
Result& operator=(const Result&) = delete;
};
class Engine {
public:
Engine() : Engine(regorus_engine_new()) {}
std::unique_ptr<Engine> clone() const {
return std::unique_ptr<Engine>(new Engine(regorus_engine_clone(engine)));
}
Result add_policy(const char* path, const char* policy) {
return Result(regorus_engine_add_policy(engine, path, policy));
}
Result add_policy_from_file(const char* path) {
return Result(regorus_engine_add_policy_from_file(engine, path));
}
Result add_data_json(const char* data) {
return Result(regorus_engine_add_data_json(engine, data));
}
Result add_data_from_json_file(const char* path) {
return Result(regorus_engine_add_data_from_json_file(engine, path));
}
Result set_input_json(const char* input) {
return Result(regorus_engine_set_input_json(engine, input));
}
Result set_input_from_json_file(const char* path) {
return Result(regorus_engine_set_input_from_json_file(engine, path));
}
Result eval_query(const char* query) {
return Result(regorus_engine_eval_query(engine, query));
}
Result eval_rule(const char* rule) {
return Result(regorus_engine_eval_rule(engine, rule));
}
Result set_enable_coverage(bool enable) {
return Result(regorus_engine_set_enable_coverage(engine, enable));
}
Result clear_coverage_data() {
return Result(regorus_engine_clear_coverage_data(engine));
}
Result get_coverage_report() {
return Result(regorus_engine_get_coverage_report(engine));
}
Result get_coverage_report_pretty() {
return Result(regorus_engine_get_coverage_report_pretty(engine));
}
~Engine() {
regorus_engine_drop(engine);
}
private:
RegorusEngine* engine;
private:
Engine(RegorusEngine* e) : engine(e) {}
Engine(const Engine&) = delete;
Engine(Engine&&) = delete;
Engine& operator=(const Engine&) = delete;
};
}
#endif // REGORUS_WRAPPER_HPP

View File

@@ -0,0 +1,58 @@
using System.Diagnostics;
long nanosecPerTick = (1000L*1000L*1000L) / Stopwatch.Frequency;
var w = new Stopwatch();
// Force load of modules.
{
var _e = new Regorus.Engine();
var _j = System.Text.Json.JsonDocument.Parse("{}");
}
w.Restart();
var engine = new Regorus.Engine();
w.Stop();
var newEngineTicks = w.ElapsedTicks;
w.Restart();
// Load policies and data.
engine.AddPolicyFromFile("../../tests/aci/framework.rego");
engine.AddPolicyFromFile("../../tests/aci/api.rego");
engine.AddPolicyFromFile("../../tests/aci/policy.rego");
engine.AddDataFromJsonFile("../../tests/aci/data.json");
w.Stop();
var loadPoliciesTicks = w.ElapsedTicks;
w.Restart();
// Set input and eval rule.
engine.SetInputFromJsonFile("../../tests/aci/input.json");
var value = engine.EvalQuery("data.framework.mount_overlay");
var valueDoc = System.Text.Json.JsonDocument.Parse(value);
w.Stop();
var evalTicks = w.ElapsedTicks;
Console.WriteLine("{0}", valueDoc);
Console.WriteLine("Engine creation took {0} msecs", (newEngineTicks*nanosecPerTick)/(1000.0*1000.0));
Console.WriteLine("Load policies and data took {0} msecs", (loadPoliciesTicks*nanosecPerTick)/(1000.0*1000.0));
Console.WriteLine("EvalQuery took {0} msecs", (evalTicks*nanosecPerTick)/(1000.0*1000.0));
engine = new Regorus.Engine();
engine.AddPolicy(
"test.rego",
"package test\nx = 1\nmessage = `Hello`");
engine.SetEnableCoverage(true);
Console.WriteLine("{0}", engine.EvalRule("data.test.message"));
Console.WriteLine("{0}", engine.GetCoverageReportPretty());

220
bindings/csharp/Regorus.cs Normal file
View File

@@ -0,0 +1,220 @@
using System.Text;
namespace Regorus
{
public class Exception : System.Exception
{
public Exception(string? message) : base(message) {}
}
public class Engine : ICloneable
{
unsafe private RegorusFFI.RegorusEngine* E;
public Engine()
{
unsafe
{
E = RegorusFFI.API.regorus_engine_new();
}
}
public object Clone()
{
var clone = (Engine)this.MemberwiseClone();
unsafe
{
clone.E = RegorusFFI.API.regorus_engine_clone(E);
}
return clone;
}
public string AddPolicy(string path, string rego)
{
var pathBytes = Encoding.UTF8.GetBytes(path);
var regoBytes = Encoding.UTF8.GetBytes(rego);
unsafe
{
fixed (byte* pathPtr = pathBytes)
{
fixed(byte* regoPtr = regoBytes)
{
return CheckAndDropResult(RegorusFFI.API.regorus_engine_add_policy(E, pathPtr, regoPtr));
}
}
}
}
public string AddPolicyFromFile(string path)
{
var pathBytes = Encoding.UTF8.GetBytes(path);
unsafe
{
fixed (byte* pathPtr = pathBytes)
{
return CheckAndDropResult(RegorusFFI.API.regorus_engine_add_policy_from_file(E, pathPtr));
}
}
}
public void AddDataJson(string data)
{
var dataBytes = Encoding.UTF8.GetBytes(data);
unsafe
{
fixed (byte* dataPtr = dataBytes)
{
CheckAndDropResult(RegorusFFI.API.regorus_engine_add_data_json(E, dataPtr));
}
}
}
public void AddDataFromJsonFile(string path)
{
var pathBytes = Encoding.UTF8.GetBytes(path);
unsafe
{
fixed (byte* pathPtr = pathBytes)
{
CheckAndDropResult(RegorusFFI.API.regorus_engine_add_data_from_json_file(E, pathPtr));
}
}
}
public void SetInputJson(string input)
{
var inputBytes = Encoding.UTF8.GetBytes(input);
unsafe
{
fixed (byte* inputPtr = inputBytes)
{
CheckAndDropResult(RegorusFFI.API.regorus_engine_set_input_json(E, inputPtr));
}
}
}
public void SetInputFromJsonFile(string path)
{
var pathBytes = Encoding.UTF8.GetBytes(path);
unsafe
{
fixed (byte* pathPtr = pathBytes)
{
CheckAndDropResult(RegorusFFI.API.regorus_engine_set_input_from_json_file(E, pathPtr));
}
}
}
public string EvalQuery(string query)
{
var queryBytes = Encoding.UTF8.GetBytes(query);
unsafe
{
fixed (byte* queryPtr = queryBytes)
{
return CheckAndDropResult(RegorusFFI.API.regorus_engine_eval_query(E, queryPtr));
}
}
}
public string EvalRule(string rule)
{
var ruleBytes = Encoding.UTF8.GetBytes(rule);
unsafe
{
fixed (byte* rulePtr = ruleBytes)
{
return CheckAndDropResult(RegorusFFI.API.regorus_engine_eval_query(E, rulePtr));
}
}
}
public void SetEnableCoverage(bool enable)
{
unsafe
{
CheckAndDropResult(RegorusFFI.API.regorus_engine_set_enable_coverage(E, enable));
}
}
public void ClearCoverageData()
{
unsafe
{
CheckAndDropResult(RegorusFFI.API.regorus_engine_clear_coverage_data(E));
}
}
public string GetCoverageReport()
{
unsafe
{
return CheckAndDropResult(RegorusFFI.API.regorus_engine_get_coverage_report(E));
}
}
public string GetCoverageReportPretty()
{
unsafe
{
return CheckAndDropResult(RegorusFFI.API.regorus_engine_get_coverage_report_pretty(E));
}
}
public void SetGatherPrints(bool enable)
{
unsafe
{
CheckAndDropResult(RegorusFFI.API.regorus_engine_set_gather_prints(E, enable));
}
}
public string TakePrints()
{
unsafe
{
return CheckAndDropResult(RegorusFFI.API.regorus_engine_take_prints(E));
}
}
~Engine()
{
unsafe
{
RegorusFFI.API.regorus_engine_drop(E);
}
}
string CheckAndDropResult(RegorusFFI.RegorusResult result)
{
if (result.status != RegorusFFI.RegorusStatus.RegorusStatusOk) {
unsafe {
var message = System.Runtime.InteropServices.Marshal.PtrToStringUTF8((IntPtr)result.error_message);
var ex = new Exception(message);
RegorusFFI.API.regorus_result_drop(result);
throw ex;
}
}
var resultString = "";
unsafe
{
if (result.output is not null) {
resultString = System.Runtime.InteropServices.Marshal.PtrToStringUTF8((IntPtr)result.output);
}
RegorusFFI.API.regorus_result_drop(result);
}
return resultString;
}
}
}

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk" InitialTargets="BuildRegorusFFI">
<Target Name="BuildRegorusFFI">
<Exec Command="cargo build -r --manifest-path ../ffi/Cargo.toml" />
<Copy SourceFiles="../ffi/RegorusFFI.g.cs" DestinationFolder="." />
<ItemGroup>
<RegorusDylib Include="..\..\target\release\*regorus_ffi*" />
</ItemGroup>
<Copy SourceFiles="@(RegorusDylib)" DestinationFolder="." />
</Target>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>regorus_test</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>

13
bindings/ffi/CHANGELOG.md Normal file
View File

@@ -0,0 +1,13 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.1.0](https://github.com/microsoft/regorus/releases/tag/regorus-ffi-v0.1.0) - 2024-02-08
### Other
- C++ binding ([#129](https://github.com/microsoft/regorus/pull/129))
- Bindings for C, C#, Golang ([#124](https://github.com/microsoft/regorus/pull/124))

23
bindings/ffi/Cargo.toml Normal file
View File

@@ -0,0 +1,23 @@
[package]
name = "regorus-ffi"
version = "0.1.5"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib", "staticlib"]
[dependencies]
anyhow = "1.0"
regorus = { path = "../..", default-features = false }
serde_json = "1.0.113"
[features]
default = ["std", "coverage", "regorus/arc", "regorus/full-opa"]
std = ["regorus/std"]
coverage = ["regorus/coverage"]
custom_allocator = []
[build-dependencies]
cbindgen = "0.26.0"
csbindgen = "1.9.0"

32
bindings/ffi/build.rs Normal file
View File

@@ -0,0 +1,32 @@
extern crate cbindgen;
extern crate csbindgen;
use std::env;
fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
cbindgen::Builder::new()
.with_crate(&crate_dir)
.with_language(cbindgen::Language::C)
.with_include_guard("REGORUS_H")
.generate()
.expect("Unable to generate bindings")
.write_to_file("regorus.h");
cbindgen::Builder::new()
.with_crate(crate_dir)
.with_language(cbindgen::Language::Cxx)
.with_include_guard("REGORUS_FFI_HPP")
.generate()
.expect("Unable to generate bindings")
.write_to_file("regorus.ffi.hpp");
csbindgen::Builder::default()
.input_extern_file("src/lib.rs")
.csharp_dll_name("regorus_ffi")
.csharp_class_name("API")
.csharp_namespace("RegorusFFI")
.generate_csharp_file("./RegorusFFI.g.cs")
.unwrap();
}

155
bindings/ffi/cbindgen.toml Normal file
View File

@@ -0,0 +1,155 @@
# This is a template cbindgen.toml file with all of the default values.
# Some values are commented out because their absence is the real default.
#
# See https://github.com/mozilla/cbindgen/blob/master/docs.md#cbindgentoml
# for detailed documentation of every option here.
language = "C++"
############## Options for Wrapping the Contents of the Header #################
# header = "/* Text to put at the beginning of the generated file. Probably a license. */"
# trailer = "/* Text to put at the end of the generated file */"
# include_guard = "my_bindings_h"
# pragma_once = true
# autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
include_version = false
# namespace = "my_namespace"
namespaces = []
using_namespaces = []
sys_includes = []
includes = []
no_includes = false
after_includes = ""
############################ Code Style Options ################################
braces = "SameLine"
line_length = 100
tab_width = 2
documentation = true
documentation_style = "auto"
documentation_length = "full"
line_endings = "LF" # also "CR", "CRLF", "Native"
############################# Codegen Options ##################################
style = "both"
sort_by = "Name" # default for `fn.sort_by` and `const.sort_by`
usize_is_size_t = true
[defines]
# "target_os = freebsd" = "DEFINE_FREEBSD"
# "feature = serde" = "DEFINE_SERDE"
[export]
include = []
exclude = []
# prefix = "CAPI_"
item_types = []
renaming_overrides_prefixing = false
[export.rename]
[export.body]
[export.mangle]
[fn]
rename_args = "None"
# must_use = "MUST_USE_FUNC"
# deprecated = "DEPRECATED_FUNC"
# deprecated_with_note = "DEPRECATED_FUNC_WITH_NOTE"
# no_return = "NO_RETURN"
# prefix = "START_FUNC"
# postfix = "END_FUNC"
args = "auto"
sort_by = "Name"
[struct]
rename_fields = "None"
# must_use = "MUST_USE_STRUCT"
# deprecated = "DEPRECATED_STRUCT"
# deprecated_with_note = "DEPRECATED_STRUCT_WITH_NOTE"
derive_constructor = false
derive_eq = false
derive_neq = false
derive_lt = false
derive_lte = false
derive_gt = false
derive_gte = false
[enum]
rename_variants = "None"
# must_use = "MUST_USE_ENUM"
# deprecated = "DEPRECATED_ENUM"
# deprecated_with_note = "DEPRECATED_ENUM_WITH_NOTE"
add_sentinel = false
prefix_with_name = false
derive_helper_methods = false
derive_const_casts = false
derive_mut_casts = false
# cast_assert_name = "ASSERT"
derive_tagged_enum_destructor = false
derive_tagged_enum_copy_constructor = false
enum_class = true
private_default_tagged_enum_constructor = false
[const]
allow_static_const = true
allow_constexpr = false
sort_by = "Name"
[macro_expansion]
bitflags = false
############## Options for How Your Rust library Should Be Parsed ##############
[parse]
parse_deps = false
# include = []
exclude = []
clean = false
extra_bindings = []
[parse.expand]
crates = []
all_features = false
default_features = false
features = []

441
bindings/ffi/src/lib.rs Normal file
View File

@@ -0,0 +1,441 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use anyhow::{anyhow, bail, Result};
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
/// Status of a call on `RegorusEngine`.
#[repr(C)]
pub enum RegorusStatus {
/// The operation was successful.
RegorusStatusOk,
/// The operation was unsuccessful.
RegorusStatusError,
}
/// Result of a call on `RegorusEngine`.
///
/// Must be freed using `regorus_result_drop`.
#[repr(C)]
pub struct RegorusResult {
/// Status
status: RegorusStatus,
/// Output produced by the call.
/// Owned by Rust.
output: *mut c_char,
/// Errors produced by the call.
/// Owned by Rust.
error_message: *mut c_char,
}
fn to_c_str(s: String) -> *mut c_char {
match CString::new(s) {
Ok(cs) => cs.into_raw(),
_ => to_c_str("binding error: failed to create c-style string".to_string()),
}
}
fn from_c_str(name: &str, s: *const c_char) -> Result<String> {
if s.is_null() {
bail!("null pointer");
}
unsafe {
CStr::from_ptr(s)
.to_str()
.map_err(|e| anyhow!("`{name}`: invalid utf8.\n{e}"))
.map(|s| s.to_string())
}
}
fn to_ref<T>(t: &*mut T) -> Result<&mut T> {
unsafe { t.as_mut().ok_or_else(|| anyhow!("null pointer")) }
}
fn to_regorus_result(r: Result<()>) -> RegorusResult {
match r {
Ok(()) => RegorusResult {
status: RegorusStatus::RegorusStatusOk,
output: std::ptr::null_mut(),
error_message: std::ptr::null_mut(),
},
Err(e) => RegorusResult {
status: RegorusStatus::RegorusStatusError,
output: std::ptr::null_mut(),
error_message: to_c_str(format!("{e}")),
},
}
}
fn to_regorus_string_result(r: Result<String>) -> RegorusResult {
match r {
Ok(s) => RegorusResult {
status: RegorusStatus::RegorusStatusOk,
output: to_c_str(s),
error_message: std::ptr::null_mut(),
},
Err(e) => RegorusResult {
status: RegorusStatus::RegorusStatusError,
output: std::ptr::null_mut(),
error_message: to_c_str(format!("{e}")),
},
}
}
/// Wrapper for `regorus::Engine`.
#[derive(Clone)]
pub struct RegorusEngine {
engine: ::regorus::Engine,
}
/// Drop a `RegorusResult`.
///
/// `output` and `error_message` strings are not valid after drop.
#[no_mangle]
pub extern "C" fn regorus_result_drop(r: RegorusResult) {
unsafe {
if !r.error_message.is_null() {
let _ = CString::from_raw(r.error_message);
}
if !r.output.is_null() {
let _ = CString::from_raw(r.output);
}
}
}
#[no_mangle]
/// Construct a new Engine
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html
pub extern "C" fn regorus_engine_new() -> *mut RegorusEngine {
let engine = ::regorus::Engine::new();
Box::into_raw(Box::new(RegorusEngine { engine }))
}
/// Clone a [`RegorusEngine`]
///
/// To avoid having to parse same policy again, the engine can be cloned
/// after policies and data have been added.
///
#[no_mangle]
pub extern "C" fn regorus_engine_clone(engine: *mut RegorusEngine) -> *mut RegorusEngine {
match to_ref(&engine) {
Ok(e) => Box::into_raw(Box::new(e.clone())),
_ => std::ptr::null_mut(),
}
}
#[no_mangle]
pub extern "C" fn regorus_engine_drop(engine: *mut RegorusEngine) {
if let Ok(e) = to_ref(&engine) {
unsafe {
let _ = Box::from_raw(std::ptr::from_mut(e));
}
}
}
/// Add a policy
///
/// The policy is parsed into AST.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.add_policy
///
/// * `path`: A filename to be associated with the policy.
/// * `rego`: Rego policy.
#[no_mangle]
pub extern "C" fn regorus_engine_add_policy(
engine: *mut RegorusEngine,
path: *const c_char,
rego: *const c_char,
) -> RegorusResult {
to_regorus_string_result(|| -> Result<String> {
to_ref(&engine)?
.engine
.add_policy(from_c_str("path", path)?, from_c_str("rego", rego)?)
}())
}
#[cfg(feature = "std")]
#[no_mangle]
pub extern "C" fn regorus_engine_add_policy_from_file(
engine: *mut RegorusEngine,
path: *const c_char,
) -> RegorusResult {
to_regorus_string_result(|| -> Result<String> {
to_ref(&engine)?
.engine
.add_policy_from_file(from_c_str("path", path)?)
}())
}
/// Add policy data.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.add_data
/// * `data`: JSON encoded value to be used as policy data.
#[no_mangle]
pub extern "C" fn regorus_engine_add_data_json(
engine: *mut RegorusEngine,
data: *const c_char,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(&engine)?
.engine
.add_data(regorus::Value::from_json_str(&from_c_str("data", data)?)?)
}())
}
/// Get list of loaded Rego packages as JSON.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_packages
/// * `data`: JSON encoded value to be used as policy data.
#[no_mangle]
pub extern "C" fn regorus_engine_get_packages(engine: *mut RegorusEngine) -> RegorusResult {
to_regorus_string_result(|| -> Result<String> {
serde_json::to_string_pretty(&to_ref(&engine)?.engine.get_packages()?)
.map_err(anyhow::Error::msg)
}())
}
#[cfg(feature = "std")]
#[no_mangle]
pub extern "C" fn regorus_engine_add_data_from_json_file(
engine: *mut RegorusEngine,
path: *const c_char,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(&engine)?
.engine
.add_data(regorus::Value::from_json_file(from_c_str("path", path)?)?)
}())
}
/// Clear policy data.
///
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.clear_data
#[no_mangle]
pub extern "C" fn regorus_engine_clear_data(engine: *mut RegorusEngine) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(&engine)?.engine.clear_data();
Ok(())
}())
}
/// Set input.
///
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.set_input
/// * `input`: JSON encoded value to be used as input to query.
#[no_mangle]
pub extern "C" fn regorus_engine_set_input_json(
engine: *mut RegorusEngine,
input: *const c_char,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(&engine)?
.engine
.set_input(regorus::Value::from_json_str(&from_c_str("input", input)?)?);
Ok(())
}())
}
#[cfg(feature = "std")]
#[no_mangle]
pub extern "C" fn regorus_engine_set_input_from_json_file(
engine: *mut RegorusEngine,
path: *const c_char,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(&engine)?
.engine
.set_input(regorus::Value::from_json_file(from_c_str("path", path)?)?);
Ok(())
}())
}
/// Evaluate query.
///
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.eval_query
/// * `query`: Rego expression to be evaluate.
#[no_mangle]
pub extern "C" fn regorus_engine_eval_query(
engine: *mut RegorusEngine,
query: *const c_char,
) -> RegorusResult {
let output = || -> Result<String> {
let results = to_ref(&engine)?
.engine
.eval_query(from_c_str("query", query)?, false)?;
Ok(serde_json::to_string_pretty(&results)?)
}();
match output {
Ok(out) => RegorusResult {
status: RegorusStatus::RegorusStatusOk,
output: to_c_str(out),
error_message: std::ptr::null_mut(),
},
Err(e) => to_regorus_result(Err(e)),
}
}
/// Evaluate specified rule.
///
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.eval_rule
/// * `rule`: Path to the rule.
#[no_mangle]
pub extern "C" fn regorus_engine_eval_rule(
engine: *mut RegorusEngine,
rule: *const c_char,
) -> RegorusResult {
let output = || -> Result<String> {
to_ref(&engine)?
.engine
.eval_rule(from_c_str("rule", rule)?)?
.to_json_str()
}();
match output {
Ok(out) => RegorusResult {
status: RegorusStatus::RegorusStatusOk,
output: to_c_str(out),
error_message: std::ptr::null_mut(),
},
Err(e) => to_regorus_result(Err(e)),
}
}
/// Enable/disable coverage.
///
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.set_enable_coverage
/// * `enable`: Whether to enable or disable coverage.
#[no_mangle]
#[cfg(feature = "coverage")]
pub extern "C" fn regorus_engine_set_enable_coverage(
engine: *mut RegorusEngine,
enable: bool,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(&engine)?.engine.set_enable_coverage(enable);
Ok(())
}())
}
/// Get coverage report.
///
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.get_coverage_report
#[no_mangle]
#[cfg(feature = "coverage")]
pub extern "C" fn regorus_engine_get_coverage_report(engine: *mut RegorusEngine) -> RegorusResult {
let output = || -> Result<String> {
Ok(serde_json::to_string_pretty(
&to_ref(&engine)?.engine.get_coverage_report()?,
)?)
}();
match output {
Ok(out) => RegorusResult {
status: RegorusStatus::RegorusStatusOk,
output: to_c_str(out),
error_message: std::ptr::null_mut(),
},
Err(e) => to_regorus_result(Err(e)),
}
}
/// Get pretty printed coverage report.
///
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Report.html#method.to_string_pretty
#[no_mangle]
#[cfg(feature = "coverage")]
pub extern "C" fn regorus_engine_get_coverage_report_pretty(
engine: *mut RegorusEngine,
) -> RegorusResult {
let output = || -> Result<String> {
to_ref(&engine)?
.engine
.get_coverage_report()?
.to_string_pretty()
}();
match output {
Ok(out) => RegorusResult {
status: RegorusStatus::RegorusStatusOk,
output: to_c_str(out),
error_message: std::ptr::null_mut(),
},
Err(e) => to_regorus_result(Err(e)),
}
}
/// Clear coverage data.
///
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.clear_coverage_data
#[no_mangle]
#[cfg(feature = "coverage")]
pub extern "C" fn regorus_engine_clear_coverage_data(engine: *mut RegorusEngine) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(&engine)?.engine.clear_coverage_data();
Ok(())
}())
}
/// Whether to gather output of print statements.
///
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.set_gather_prints
/// * `enable`: Whether to enable or disable gathering print statements.
#[no_mangle]
pub extern "C" fn regorus_engine_set_gather_prints(
engine: *mut RegorusEngine,
enable: bool,
) -> RegorusResult {
to_regorus_result(|| -> Result<()> {
to_ref(&engine)?.engine.set_gather_prints(enable);
Ok(())
}())
}
/// Take all the gathered print statements.
///
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.take_prints
#[no_mangle]
pub extern "C" fn regorus_engine_take_prints(engine: *mut RegorusEngine) -> RegorusResult {
let output = || -> Result<String> {
Ok(serde_json::to_string_pretty(
&to_ref(&engine)?.engine.take_prints()?,
)?)
}();
match output {
Ok(out) => RegorusResult {
status: RegorusStatus::RegorusStatusOk,
output: to_c_str(out),
error_message: std::ptr::null_mut(),
},
Err(e) => to_regorus_result(Err(e)),
}
}
#[cfg(feature = "custom_allocator")]
extern "C" {
fn regorus_aligned_alloc(alignment: usize, size: usize) -> *mut u8;
fn regorus_free(ptr: *mut u8);
}
#[cfg(feature = "custom_allocator")]
mod allocator {
use std::alloc::{GlobalAlloc, Layout};
struct RegorusAllocator {}
unsafe impl GlobalAlloc for RegorusAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let size = layout.size();
let align = layout.align();
crate::regorus_aligned_alloc(align, size)
}
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
crate::regorus_free(ptr)
}
}
#[global_allocator]
static ALLOCATOR: RegorusAllocator = RegorusAllocator {};
}

3
bindings/go/go.mod Normal file
View File

@@ -0,0 +1,3 @@
module regorus_test
go 1.21.5

87
bindings/go/main.go Normal file
View File

@@ -0,0 +1,87 @@
package main
import (
"fmt"
"os"
"regorus_test/pkg/regorus"
"time"
)
func main() {
var output string
var err error
t := time.Now();
// Create new engine
engine := regorus.NewEngine()
defer engine.Close()
elapsed1 := time.Since(t)
t = time.Now()
// Add policies and data.
policies := []string{
"../../tests/aci/framework.rego",
"../../tests/aci/api.rego",
"../../tests/aci/policy.rego",
}
for _, policy := range policies {
var pkg string
if pkg, err = engine.AddPolicyFromFile(policy); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
fmt.Printf("Loaded package %s\n", pkg);
}
if err = engine.AddDataFromJsonFile("../../tests/aci/data.json"); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
elapsed2 := time.Since(t)
t = time.Now()
// Set input.
if err = engine.SetInputFromJsonFile("../../tests/aci/input.json"); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
// Eval Rule
if output, err = engine.EvalRule("data.framework.mount_overlay"); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
elapsed3 := time.Since(t)
fmt.Printf("%s\n", output)
fmt.Printf("NewEngine took %v\n", elapsed1)
fmt.Printf("Add policies and data took %v\n", elapsed2)
fmt.Printf("Set input and eval query took %v\n", elapsed3)
// Create new engine.
engine1 := regorus.NewEngine()
defer engine1.Close()
// Enable coverage
engine1.SetEnableCoverage(true)
var pkg string
pkg, err = engine1.AddPolicy("test.rego", "package test\nx = 1\nmessage = `Hello`")
fmt.Printf("Loaded package %s\n", pkg)
// Eval Rule
if output, err = engine1.EvalRule("data.test.message"); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
fmt.Printf("%s\n", output)
// Print pretty coverage report.
if output, err = engine1.GetCoverageReportPretty(); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
fmt.Printf("%s\n", output)
}

View File

@@ -0,0 +1,187 @@
package regorus
// #cgo LDFLAGS: -L ../../../../target/release -lregorus_ffi
// #include "../../../ffi/regorus.h"
import "C"
import (
"fmt"
"unsafe"
)
type Engine struct {
e *C.RegorusEngine
}
func NewEngine() *Engine {
e := new(Engine)
e.e = C.regorus_engine_new()
return e
}
func (e *Engine) Close() {
C.regorus_engine_drop(e.e)
}
func (e *Engine) Clone() *Engine {
c := new(Engine)
c.e = C.regorus_engine_clone(e.e)
return c
}
func (e *Engine) AddPolicy(path string, rego string) (string, error) {
path_c := C.CString(path)
defer C.free(unsafe.Pointer(path_c))
rego_c := C.CString(rego)
defer C.free(unsafe.Pointer(rego_c))
result := C.regorus_engine_add_policy(e.e, path_c, rego_c)
defer C.regorus_result_drop(result)
if result.status != C.RegorusStatusOk {
return "", fmt.Errorf("%s", C.GoString(result.error_message))
}
return C.GoString(result.output), nil
}
func (e *Engine) AddPolicyFromFile(path string) (string, error) {
path_c := C.CString(path)
defer C.free(unsafe.Pointer(path_c))
result := C.regorus_engine_add_policy_from_file(e.e, path_c)
defer C.regorus_result_drop(result)
if result.status != C.RegorusStatusOk {
return "", fmt.Errorf("%s", C.GoString(result.error_message))
}
return C.GoString(result.output), nil
}
func (e *Engine) AddDataJson(data string) error {
data_c := C.CString(data)
defer C.free(unsafe.Pointer(data_c))
result := C.regorus_engine_add_data_json(e.e, data_c)
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) AddDataFromJsonFile(path string) error {
path_c := C.CString(path)
defer C.free(unsafe.Pointer(path_c))
result := C.regorus_engine_add_data_from_json_file(e.e, path_c)
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) SetInputJson(input string) error {
input_c := C.CString(input)
defer C.free(unsafe.Pointer(input_c))
result := C.regorus_engine_set_input_json(e.e, input_c)
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) SetInputFromJsonFile(path string) error {
path_c := C.CString(path)
defer C.free(unsafe.Pointer(path_c))
result := C.regorus_engine_set_input_from_json_file(e.e, path_c)
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) EvalQuery(query string) (string, error) {
query_c := C.CString(query)
defer C.free(unsafe.Pointer(query_c))
result := C.regorus_engine_eval_query(e.e, query_c)
defer C.regorus_result_drop(result)
if result.status != C.RegorusStatusOk {
return "", fmt.Errorf("%s", C.GoString(result.error_message))
}
return C.GoString(result.output), nil
}
func (e *Engine) EvalRule(rule string) (string, error) {
rule_c := C.CString(rule)
defer C.free(unsafe.Pointer(rule_c))
result := C.regorus_engine_eval_rule(e.e, rule_c)
defer C.regorus_result_drop(result)
if result.status != C.RegorusStatusOk {
return "", fmt.Errorf("%s", C.GoString(result.error_message))
}
return C.GoString(result.output), nil
}
func (e *Engine) SetEnableCoverage(enable bool) error {
result := C.regorus_engine_set_enable_coverage(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) ClearCoverageData() error {
result := C.regorus_engine_clear_coverage_data(e.e)
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) GetCoverageReport() (string, error) {
result := C.regorus_engine_get_coverage_report(e.e)
defer C.regorus_result_drop(result)
if result.status != C.RegorusStatusOk {
return "", fmt.Errorf("%s", C.GoString(result.error_message))
}
return C.GoString(result.output), nil
}
func (e *Engine) GetCoverageReportPretty() (string, error) {
result := C.regorus_engine_get_coverage_report_pretty(e.e)
defer C.regorus_result_drop(result)
if result.status != C.RegorusStatusOk {
return "", fmt.Errorf("%s", C.GoString(result.error_message))
}
return C.GoString(result.output), nil
}
func (e *Engine) SetGatherPrints(b bool) error {
result := C.regorus_engine_set_gather_prints(e.e, C.bool(b))
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) TakePrints() (string, error) {
result := C.regorus_engine_take_prints(e.e)
defer C.regorus_result_drop(result)
if result.status != C.RegorusStatusOk {
return "", fmt.Errorf("%s", C.GoString(result.error_message))
}
return C.GoString(result.output), nil
}

1
bindings/java/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
target/

View File

@@ -0,0 +1,13 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.1.0](https://github.com/microsoft/regorus/releases/tag/regorus-java-v0.1.0) - 2024-02-23
### Other
- Java publishing ([#151](https://github.com/microsoft/regorus/pull/151))
- Java bindings ([#147](https://github.com/microsoft/regorus/pull/147))

20
bindings/java/Cargo.toml Normal file
View File

@@ -0,0 +1,20 @@
[package]
name = "regorus-java"
version = "0.1.5"
edition = "2021"
repository = "https://github.com/microsoft/regorus/bindings/java"
description = "Java bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
keywords = ["interpreter", "opa", "policy-as-code", "rego"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib"]
[features]
default = ["regorus/std", "regorus/full-opa"]
[dependencies]
anyhow = "1.0"
serde_json = "1.0.112"
jni = "0.21.1"
regorus = { path = "../..", default-features = false, features = ["arc"] }

69
bindings/java/README.md Normal file
View File

@@ -0,0 +1,69 @@
# Regorus Java
**Regorus** is
- *Rego*-*Rus(t)* - A fast, light-weight [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/)
interpreter written in Rust.
- *Rigorous* - A rigorous enforcer of well-defined Rego semantics.
See main [Regorus page](https://github.com/microsoft/regorus) for more details about the project.
## Building
Due to operational overhead we don't publish Java bindings to Maven Central
currently (see https://github.com/microsoft/regorus/issues/237) and you need to build from source to use it.
In order to build Regorus Java for a target platform, you need to install Rust target for that platform first:
```bash
$ rustup target add aarch64-apple-darwin
```
Afterwards, you can build native library for that target using:
```bash
$ cargo build --release --target aarch64-apple-darwin
```
You will then have a native library at `../../target/aarch64-apple-darwin/release/libregorus_java.dylib` depending on your target.
You then need to build Java bindings using:
```bash
$ mvn package
```
And you will have a JAR at `./target/regorus-java-0.1.5.jar`.
## Usage
You can use Regorus Java bindings as:
```java
import com.microsoft.regorus.Engine;
public class Test {
public static void main(String[] args) {
try (Engine engine = new Engine()) {
engine.addPolicy(
"hello.rego",
"package test\nmessage = concat(\", \", [input.message, data.message])"
);
engine.addDataJson("{\"message\":\"World!\"}");
engine.setInputJson("{\"message\":\"Hello\"}");
String resJson = engine.evalQuery("data.test.message");
System.out.println(resJson);
}
}
}
```
You need to ensure artifacts built in [previous section](#building) are in Java's classpath.
For example with `java` CLI:
```bash
$ java -Djava.library.path=../../target/aarch64-apple-darwin/release/ -cp target/regorus-java-0.1.5.jar Test.java
```
should gave you the output:
```
{"result":[{"expressions":[{"value":"Hello, World!","text":"data.test.message","location":{"row":1,"col":1}}]}]}
```

36
bindings/java/Test.java Normal file
View File

@@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
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])"
);
System.out.println("Loaded package " + pkg);
engine.addDataJson("{\"message\":\"World!\"}");
engine.setInputJson("{\"message\":\"Hello\"}");
// Evaluate query.
String resJson = engine.evalQuery("data.test.message");
System.out.println(resJson);
// Enable coverage.
engine.setEnableCoverage(true);
// Evaluate rule.
String valueJson = engine.evalRule("data.test.message");
System.out.println(valueJson);
String coverageJson = engine.getCoverageReport();
System.out.println(coverageJson);
System.out.println(engine.getCoverageReportPretty());
}
}
}

View File

@@ -0,0 +1,157 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_microsoft_regorus_Engine */
#ifndef _Included_com_microsoft_regorus_Engine
#define _Included_com_microsoft_regorus_Engine
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_microsoft_regorus_Engine
* Method: nativeNewEngine
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_com_microsoft_regorus_Engine_nativeNewEngine
(JNIEnv *, jclass);
/*
* Class: com_microsoft_regorus_Engine
* Method: nativeClone
* Signature: (J)J
*/
JNIEXPORT jlong JNICALL Java_com_microsoft_regorus_Engine_nativeClone
(JNIEnv *, jclass, jlong);
/*
* Class: com_microsoft_regorus_Engine
* Method: nativeAddPolicy
* Signature: (JLjava/lang/String;Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_microsoft_regorus_Engine_nativeAddPolicy
(JNIEnv *, jclass, jlong, jstring, jstring);
/*
* Class: com_microsoft_regorus_Engine
* Method: nativeAddPolicyFromFile
* Signature: (JLjava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_microsoft_regorus_Engine_nativeAddPolicyFromFile
(JNIEnv *, jclass, jlong, jstring);
/*
* Class: com_microsoft_regorus_Engine
* Method: nativeClearData
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeClearData
(JNIEnv *, jclass, jlong);
/*
* Class: com_microsoft_regorus_Engine
* Method: nativeAddDataJson
* Signature: (JLjava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeAddDataJson
(JNIEnv *, jclass, jlong, jstring);
/*
* Class: com_microsoft_regorus_Engine
* Method: nativeAddDataJsonFromFile
* Signature: (JLjava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeAddDataJsonFromFile
(JNIEnv *, jclass, jlong, jstring);
/*
* Class: com_microsoft_regorus_Engine
* Method: nativeSetInputJson
* Signature: (JLjava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeSetInputJson
(JNIEnv *, jclass, jlong, jstring);
/*
* Class: com_microsoft_regorus_Engine
* Method: nativeSetInputJsonFromFile
* Signature: (JLjava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeSetInputJsonFromFile
(JNIEnv *, jclass, jlong, jstring);
/*
* Class: com_microsoft_regorus_Engine
* Method: nativeEvalQuery
* Signature: (JLjava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_microsoft_regorus_Engine_nativeEvalQuery
(JNIEnv *, jclass, jlong, jstring);
/*
* Class: com_microsoft_regorus_Engine
* Method: nativeEvalRule
* Signature: (JLjava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_microsoft_regorus_Engine_nativeEvalRule
(JNIEnv *, jclass, jlong, jstring);
/*
* Class: com_microsoft_regorus_Engine
* Method: nativeSetEnableCoverage
* Signature: (JZ)V
*/
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeSetEnableCoverage
(JNIEnv *, jclass, jlong, jboolean);
/*
* Class: com_microsoft_regorus_Engine
* Method: nativeGetCoverageReport
* Signature: (J)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_microsoft_regorus_Engine_nativeGetCoverageReport
(JNIEnv *, jclass, jlong);
/*
* Class: com_microsoft_regorus_Engine
* Method: nativeGetCoverageReportAsColoredString
* Signature: (J)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_microsoft_regorus_Engine_nativeGetCoverageReportAsColoredString
(JNIEnv *, jclass, jlong);
/*
* Class: com_microsoft_regorus_Engine
* Method: nativeClearCoverageData
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeClearCoverageData
(JNIEnv *, jclass, jlong);
/*
* Class: com_microsoft_regorus_Engine
* Method: nativeSetGatherPrints
* Signature: (JZ)V
*/
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeSetGatherPrints
(JNIEnv *, jclass, jlong, jboolean);
/*
* Class: com_microsoft_regorus_Engine
* Method: nativeTakePrints
* Signature: (J)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_microsoft_regorus_Engine_nativeTakePrints
(JNIEnv *, jclass, jlong);
/*
* Class: com_microsoft_regorus_Engine
* Method: nativeDestroyEngine
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeDestroyEngine
(JNIEnv *, jclass, jlong);
#ifdef __cplusplus
}
#endif
#endif

138
bindings/java/pom.xml Normal file
View File

@@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) Microsoft Corporation.
Licensed under the MIT License.
-->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.microsoft.regorus</groupId>
<artifactId>regorus-java</artifactId>
<version>0.1.5</version>
<name>Regorus Java</name>
<description>Java bindings for Regorus - a fast, lightweight Rego interpreter written in Rust</description>
<url>https://github.com/microsoft/regorus/bindings/java</url>
<licenses>
<license>
<name>MIT License</name>
<url>https://opensource.org/blog/license/mit</url>
</license>
</licenses>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<distributionManagement>
<repository>
<id>ossrh</id>
<name>Central Repository OSSRH</name>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<!--
Include native/ folder in built JAR.
During CI build we build native libraries for various platforms
and put them into native/ folder.
See `.github/publish-java.yml`.
-->
<directory>${project.basedir}/native</directory>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>exec-maven-plugin</artifactId>
<groupId>org.codehaus.mojo</groupId>
<version>3.1.0</version>
<executions>
<execution>
<!-- Build a debug release for tests -->
<id>build-native-lib-for-test</id>
<phase>test-compile</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>python3</executable>
<arguments>
<argument>${project.basedir}/tools/testbuild.py</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<!-- Add debug build to Java path, so it's discoverable by JVM. This is only for tests. -->
<argLine>-Djava.library.path=${project.basedir}/target/debug:${java.library.path}</argLine>
</configuration>
</plugin>
<!-- Build javadoc JAR, this is required by Maven Central. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.6.3</version>
<executions>
<execution>
<id>attach-javadoc</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Build sources JAR, this is required by Maven Central. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
</plugin>
</plugins>
</reporting>
</project>

324
bindings/java/src/lib.rs Normal file
View File

@@ -0,0 +1,324 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use anyhow::Result;
use jni::objects::{JClass, JObject, JString};
use jni::sys::{jlong, jstring};
use jni::JNIEnv;
use regorus::{Engine, Value};
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeNewEngine(
_env: JNIEnv,
_class: JClass,
) -> jlong {
let engine = Engine::new();
Box::into_raw(Box::new(engine)) as jlong
}
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeClone(
_env: JNIEnv,
_class: JClass,
engine_ptr: jlong,
) -> jlong {
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
let c = engine.clone();
Box::into_raw(Box::new(c)) as jlong
}
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddPolicy(
env: JNIEnv,
_class: JClass,
engine_ptr: jlong,
path: JString,
rego: JString,
) -> jstring {
let res = throw_err(env, |env| {
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
let path: String = env.get_string(&path)?.into();
let rego: String = env.get_string(&rego)?.into();
let pkg = env.new_string(engine.add_policy(path, rego)?)?;
Ok(pkg.into_raw())
});
match res {
Ok(val) => val,
Err(_) => JObject::null().into_raw(),
}
}
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddPolicyFromFile(
env: JNIEnv,
_class: JClass,
engine_ptr: jlong,
path: JString,
) -> jstring {
let res = throw_err(env, |env| {
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
let path: String = env.get_string(&path)?.into();
let pkg = env.new_string(engine.add_policy_from_file(path)?)?;
Ok(pkg.into_raw())
});
match res {
Ok(val) => val,
Err(_) => JObject::null().into_raw(),
}
}
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeGetPackages(
env: JNIEnv,
_class: JClass,
engine_ptr: jlong,
) -> jstring {
let res = throw_err(env, |env| {
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
let packages = engine.get_packages()?;
let packages_json = env.new_string(serde_json::to_string_pretty(&packages)?)?;
Ok(packages_json.into_raw())
});
match res {
Ok(val) => val,
Err(_) => JObject::null().into_raw(),
}
}
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeClearData(
env: JNIEnv,
_class: JClass,
engine_ptr: jlong,
) {
let _ = throw_err(env, |_env| {
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
engine.clear_data();
Ok(())
});
}
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddDataJson(
env: JNIEnv,
_class: JClass,
engine_ptr: jlong,
data: JString,
) {
let _ = throw_err(env, |env| {
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
let data: String = env.get_string(&data)?.into();
engine.add_data_json(&data)?;
Ok(())
});
}
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddDataJsonFromFile(
env: JNIEnv,
_class: JClass,
engine_ptr: jlong,
path: JString,
) {
let _ = throw_err(env, |env| {
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
let path: String = env.get_string(&path)?.into();
engine.add_data(Value::from_json_file(path)?)?;
Ok(())
});
}
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetInputJson(
env: JNIEnv,
_class: JClass,
engine_ptr: jlong,
input: JString,
) {
let _ = throw_err(env, |env| {
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
let input: String = env.get_string(&input)?.into();
engine.set_input_json(&input)?;
Ok(())
});
}
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetInputJsonFromFile(
env: JNIEnv,
_class: JClass,
engine_ptr: jlong,
path: JString,
) {
let _ = throw_err(env, |env| {
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
let path: String = env.get_string(&path)?.into();
engine.set_input(Value::from_json_file(path)?);
Ok(())
});
}
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeEvalQuery(
env: JNIEnv,
_class: JClass,
engine_ptr: jlong,
query: JString,
) -> jstring {
let res = throw_err(env, |env| {
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
let query: String = env.get_string(&query)?.into();
let results = engine.eval_query(query, false)?;
let output = env.new_string(serde_json::to_string(&results)?)?;
Ok(output.into_raw())
});
match res {
Ok(val) => val,
Err(_) => JObject::null().into_raw(),
}
}
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeEvalRule(
env: JNIEnv,
_class: JClass,
engine_ptr: jlong,
rule: JString,
) -> jstring {
let res = throw_err(env, |env| {
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
let rule: String = env.get_string(&rule)?.into();
let value = engine.eval_rule(rule)?;
let output = env.new_string(value.to_json_str()?)?;
Ok(output.into_raw())
});
match res {
Ok(val) => val,
Err(_) => JObject::null().into_raw(),
}
}
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetEnableCoverage(
env: JNIEnv,
_class: JClass,
engine_ptr: jlong,
enable: bool,
) {
let _ = throw_err(env, |_| {
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
engine.set_enable_coverage(enable);
Ok(())
});
}
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeGetCoverageReport(
env: JNIEnv,
_class: JClass,
engine_ptr: jlong,
) -> jstring {
let res = throw_err(env, |env| {
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
let report = engine.get_coverage_report()?;
let output = env.new_string(serde_json::to_string_pretty(&report)?)?;
Ok(output.into_raw())
});
match res {
Ok(val) => val,
Err(_) => JObject::null().into_raw(),
}
}
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeGetCoverageReportPretty(
env: JNIEnv,
_class: JClass,
engine_ptr: jlong,
) -> jstring {
let res = throw_err(env, |env| {
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
let report = engine.get_coverage_report()?.to_string_pretty()?;
let output = env.new_string(&report)?;
Ok(output.into_raw())
});
match res {
Ok(val) => val,
Err(_) => JObject::null().into_raw(),
}
}
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeClearCoverageData(
env: JNIEnv,
_class: JClass,
engine_ptr: jlong,
) {
let _ = throw_err(env, |_| {
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
engine.clear_coverage_data();
Ok(())
});
}
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetGatherPrints(
env: JNIEnv,
_class: JClass,
engine_ptr: jlong,
b: bool,
) {
let _ = throw_err(env, |_| {
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
engine.set_gather_prints(b);
Ok(())
});
}
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeTakePrints(
env: JNIEnv,
_class: JClass,
engine_ptr: jlong,
) -> jstring {
let res = throw_err(env, |env| {
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
let prints = engine.take_prints()?;
let output = env.new_string(serde_json::to_string_pretty(&prints)?)?;
Ok(output.into_raw())
});
match res {
Ok(val) => val,
Err(_) => JObject::null().into_raw(),
}
}
#[no_mangle]
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeDestroyEngine(
_env: JNIEnv,
_class: JClass,
engine_ptr: jlong,
) {
unsafe {
let _engine = Box::from_raw(engine_ptr as *mut Engine);
}
}
fn throw_err<T>(mut env: JNIEnv, mut f: impl FnMut(&mut JNIEnv) -> Result<T>) -> Result<T> {
match f(&mut env) {
Ok(val) => Ok(val),
Err(err) => {
env.throw(err.to_string())?;
Err(err)
}
}
}

View File

@@ -0,0 +1,297 @@
/**
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
**/
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.
*/
public class Engine implements AutoCloseable, Cloneable {
// Methods exposed from Rust side, you can run
// `javac -h . src/main/java/com/microsoft/regorus/Engine.java` to update
// expected native header at `bindings/java/com_microsoft_regorus_Engine.h`
// if you update the native API.
private static native long nativeNewEngine();
private static native long nativeClone(long enginePtr);
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);
private static native void nativeClearData(long enginePtr);
private static native void nativeAddDataJson(long enginePtr, String data);
private static native void nativeAddDataJsonFromFile(long enginePtr, String path);
private static native void nativeSetInputJson(long enginePtr, String input);
private static native void nativeSetInputJsonFromFile(long enginePtr, String path);
private static native String nativeEvalQuery(long enginePtr, String query);
private static native String nativeEvalRule(long enginePtr, String qrule);
private static native void nativeSetEnableCoverage(long enginePtr, boolean enable);
private static native String nativeGetCoverageReport(long enginePtr);
private static native String nativeGetCoverageReportPretty(long enginePtr);
private static native void nativeClearCoverageData(long enginePtr);
private static native void nativeSetGatherPrints(long enginePtr, boolean b);
private static native String nativeTakePrints(long enginePtr);
private static native void nativeDestroyEngine(long enginePtr);
// Pointer to Engine allocated on Rust's heap, all native methods works on
// engine expects this pointer. It is free'd in `close` method.
private final long enginePtr;
/**
* Creates a new Regorus Engine.
*/
public Engine() {
enginePtr = nativeNewEngine();
}
Engine(long ptr) {
enginePtr = ptr;
}
/**
* Efficiently clones an Engine.
*/
public Engine Clone() {
return new Engine(nativeClone(enginePtr));
}
/**
* Adds an inline Rego policy.
*
* @param filename Filename of this Rego policy.
* @param rego Rego policy.
*
* @return Rego package defined in the policy.
*/
public String addPolicy(String filename, String rego) {
return nativeAddPolicy(enginePtr, filename, rego);
}
/**
* Adds a Rego policy from given path.
*
* @param path Path of the Rego policy.
*
* @return Rego package defined in the policy.
*/
public String addPolicyFromFile(String path) {
return nativeAddPolicyFromFile(enginePtr, path);
}
/**
* Get list of loaded Rego packages.
*
* @return List of Rego packages as a JSON array of strings.
*/
public String getPackages() {
return nativeGetPackages(enginePtr);
}
/**
* Clears the data document.
*/
public void clearData() {
nativeClearData(enginePtr);
}
/**
* Adds inline data document from given JSON.
* The specified data document is merged into existing data document.
* It will throw an error if new data conflicts with the existing document.
*
* Example:
* addDataJson("[]") - Throws as it's not an object.
* addDataJson('{"a": 1}') - Fine
* addDataJson('{"b": 2}') - Fine, now {"a": 1, "b": 2}
* addDataJson('{"b": 3}') - Throws as `b` conflicts.
*
* @see clearData
*
* @throws RuntimeException If data conflicts with the existing document
* or data is not an object.
*
* @param data Inline data document.
*/
public void addDataJson(String data) throws RuntimeException {
nativeAddDataJson(enginePtr, data);
}
/**
* Adds data document from given JSON file.
* The specified data document is merged into existing data document.
* It will throw an error if new data conflicts with the existing document.
*
* @see addDataJson
* @see clearData
*
* @throws RuntimeException If data conflicts with the existing document
* or data is not an object.
*
* @param path Path to JSON data document.
*/
public void addDataJsonFromFile(String path) throws RuntimeException {
nativeAddDataJsonFromFile(enginePtr, path);
}
/**
* Sets inline JSON input.
*
* @param input inline JSON input.
*/
public void setInputJson(String input) {
nativeSetInputJson(enginePtr, input);
}
/**
* Sets JSON input from given path.
*
* @param path Path to JSON input.
*/
public void setInputJsonFromFile(String path) {
nativeSetInputJsonFromFile(enginePtr, path);
}
/**
* Evaluates given Rego query and returns a JSON string as a result.
*
* @param query The Rego query.
*
* @return Query results as a JSON string.
*/
public String evalQuery(String query) {
return nativeEvalQuery(enginePtr, query);
}
/**
* Evaluates given Rego rule and returns a JSON string as a result.
*
* @param rule Path of the Rego rule.
*
* @return Value of the rule as a JSON string.
*/
public String evalRule(String rule) {
return nativeEvalRule(enginePtr, rule);
}
/**
* Enable/disable coverage.
*
* @param enable Whether to enable coverage or not.
*
*/
public void setEnableCoverage(boolean enable) {
nativeSetEnableCoverage(enginePtr, enable);
}
/**
* Clear coverage data.
*
*/
public void clearCoverageData() {
nativeClearCoverageData(enginePtr);
}
/**
* Get coverage report as json string.
*
*/
public String getCoverageReport() {
return nativeGetCoverageReport(enginePtr);
}
/**
* Get coverage report as ANSI color coded string.
*
*/
public String getCoverageReportPretty() {
return nativeGetCoverageReportPretty(enginePtr);
}
/**
* Enable/disable gathering prints.
*
* @param b Whether to gather prints or not.
*
*/
public void setGatherPrints(boolean b) {
nativeSetGatherPrints(enginePtr, b);
}
/**
* Take gathered prints.
*
*/
public String takePrints() {
return nativeTakePrints(enginePtr);
}
@Override
public void close() {
nativeDestroyEngine(enginePtr);
}
// Loading native library from JAR is adapted from:
// https://github.com/apache/opendal/blob/93e5f65bbf30df2fed4bdd95bb0685c73c6418c2/bindings/java/src/main/java/org/apache/opendal/NativeLibrary.java
// https://github.com/apache/opendal/blob/93e5f65bbf30df2fed4bdd95bb0685c73c6418c2/bindings/java/src/main/java/org/apache/opendal/Environment.java
static {
// Build a Rust target triple, like: 'aarch64-unknown-linux-gnu'.
final StringBuilder targetTripleBuilder = new StringBuilder();
final String arch = System.getProperty("os.arch").toLowerCase();
if (arch.equals("aarch64")) {
targetTripleBuilder.append("aarch64");
} else {
targetTripleBuilder.append("x86_64");
}
targetTripleBuilder.append("-");
final String os = System.getProperty("os.name").toLowerCase();
if (os.startsWith("windows")) {
targetTripleBuilder.append("pc-windows-msvc");
} else if (os.startsWith("mac")) {
targetTripleBuilder.append("apple-darwin");
} else {
targetTripleBuilder.append("unknown-linux-gnu");
}
loadNativeLibrary(targetTripleBuilder.toString());
}
private static void loadNativeLibrary(String targetTriple) {
try {
// try dynamic library - the search path can be configured via "-Djava.library.path"
System.loadLibrary("regorus_java");
return;
} catch (UnsatisfiedLinkError ignore) {
// ignore - try from classpath
}
// Native libraries will be bundles into JARs like:
// `aarch64-apple-darwin/libregorus_java.dylib`
final String libraryName = System.mapLibraryName("regorus_java");
final String libraryPath = "/" + targetTriple + "/" + libraryName;
try (final InputStream is = Engine.class.getResourceAsStream(libraryPath)) {
if (is == null) {
throw new RuntimeException("Cannot find " + libraryPath + "\nSee https://github.com/microsoft/regorus/tree/main/bindings/java for help.");
}
final int dot = libraryPath.indexOf('.');
final File tmpFile = File.createTempFile(libraryPath.substring(0, dot), libraryPath.substring(dot));
tmpFile.deleteOnExit();
Files.copy(is, tmpFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
System.load(tmpFile.getAbsolutePath());
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
**/
package com.microsoft.regorus;
import java.util.Map;
import java.util.ArrayList;
import junit.framework.TestCase;
import junit.framework.Assert;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
public class EngineTest extends TestCase
{
public void test_engine()
{
String resJson;
try (Engine engine = new Engine()) {
engine.addPolicy(
"hello.rego",
"package test\nmessage = concat(\", \", [input.message, data.message])"
);
engine.addDataJson("{\"message\":\"World!\"}");
engine.setInputJson("{\"message\":\"Hello\"}");
resJson = engine.evalQuery("data.test.message");
}
Gson gson = new Gson();
Map res = gson.fromJson(resJson, Map.class);
ArrayList results = (ArrayList) res.get("result");
ArrayList expressions = (ArrayList) ((Map) results.get(0)).get("expressions");
Map expression = (Map) expressions.get(0);
Assert.assertEquals("Hello, World!", expression.get("value"));
}
}

View File

@@ -0,0 +1,17 @@
#!/usr/bin/env python3
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
#
# Builds Regorus Java to use in Java tests. See `pom.xml`.
from pathlib import Path
import subprocess
if __name__ == "__main__":
basedir = Path(__file__).parent.parent
output = basedir / "target"
Path(output).mkdir(exist_ok=True, parents=True)
cmd = ["cargo", "build", "--target-dir", str(output)]
print("$ " + subprocess.list2cmdline(cmd))
subprocess.run(cmd, cwd=basedir, check=True)

View File

@@ -0,0 +1 @@
pyo3

View File

@@ -0,0 +1,14 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.1.0](https://github.com/microsoft/regorus/releases/tag/regoruspy-v0.1.0) - 2024-02-01
### Other
- Document bindings ([#119](https://github.com/microsoft/regorus/pull/119))
- Publish python packages ([#117](https://github.com/microsoft/regorus/pull/117))
- Python bindings ([#115](https://github.com/microsoft/regorus/pull/115))

View File

@@ -0,0 +1,23 @@
[package]
name = "regoruspy"
version = "0.1.5"
edition = "2021"
repository = "https://github.com/microsoft/regorus/bindings/python"
description = "Python bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
keywords = ["interpreter", "opa", "policy-as-code", "rego"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib"]
[features]
default = ["regorus/std", "regorus/full-opa"]
[dependencies]
anyhow = "1.0"
ordered-float = "4.2.0"
pyo3 = {version = "0.21.0", features = ["anyhow", "extension-module"] }
regorus = { path = "../..", default-features = false, features = ["arc"] }
serde_json = "1.0.112"

64
bindings/python/README.md Normal file
View File

@@ -0,0 +1,64 @@
# regorus
**Regorus** is
- *Rego*-*Rus(t)* - A fast, light-weight [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/)
interpreter written in Rust.
- *Rigorous* - A rigorous enforcer of well-defined Rego semantics.
Regorus can be used in Python via `regorus` package. (It is not yet available in PyPI, but can be manually built.)
See [Repository](https://github.com/microsoft/regorus).
To build this binding, see [building](https://github.com/microsoft/regorus/blob/main/bindings/python/building.md)
## Usage
```Python
import regorus
# Create engine
engine = regorus.Engine()
# Load policies
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')
# Add policy data
data = {
"metadata": {
"devices": {
"/run/layers/p0-layer0": "1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766",
"/run/layers/p0-layer1": "e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c",
"/run/layers/p0-layer2": "eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79",
"/run/layers/p0-layer3": "41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156",
"/run/layers/p0-layer4": "4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c",
"/run/layers/p0-layer5": "fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a"
}
}
}
engine.add_data(data)
# Set input
input = {
"containerID": "container0",
"layerPaths": [
"/run/layers/p0-layer0",
"/run/layers/p0-layer1",
"/run/layers/p0-layer2",
"/run/layers/p0-layer3",
"/run/layers/p0-layer4",
"/run/layers/p0-layer5"
],
"target": "/run/gcs/c/container0/rootfs"
}
engine.set_input(input)
# Eval rule
value = engine.eval_rule('data.framework.mount_overlay')
# Print value
print(value)
```

View File

@@ -0,0 +1,22 @@
- Install maturin
```
pipx install maturin
```
See [Maturin User Guide](https://www.maturin.rs)
- Build bindings for Python
```
cd bindings/python
maturin build --release --target-dir wheels
```
- Install python wheel
```
pip3 install ../../target/wheels/regorus*.whl --force-reinstall
```
- Run test script
```
python3 test.py
```

View File

@@ -0,0 +1,16 @@
[build-system]
requires = ["maturin>=1.4,<2.0"]
build-backend = "maturin"
[project]
name = "regorus"
requires-python = ">=3.8"
classifiers = [
"Programming Language :: Rust",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
dynamic = ["version"]
[tool.maturin]
features = ["pyo3/extension-module"]

358
bindings/python/src/lib.rs Normal file
View File

@@ -0,0 +1,358 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use anyhow::{anyhow, Result};
use pyo3::exceptions::PyTypeError;
use pyo3::prelude::*;
use pyo3::types::*;
use std::collections::{BTreeMap, BTreeSet};
use ::regorus::Value;
/// Regorus engine.
#[pyclass(unsendable)]
pub struct Engine {
engine: ::regorus::Engine,
}
impl Default for Engine {
fn default() -> Self {
Self::new()
}
}
fn from(ob: &Bound<'_, PyAny>) -> Result<Value, PyErr> {
// dicts
Ok(if let Ok(dict) = ob.downcast::<PyDict>() {
let mut map = BTreeMap::new();
for (k, v) in dict {
map.insert(from(&k)?, from(&v)?);
}
map.into()
}
// set
else if let Ok(pset) = ob.downcast::<PySet>() {
let mut set = BTreeSet::new();
for v in pset {
set.insert(from(&v)?);
}
set.into()
}
// frozen set
else if let Ok(pfset) = ob.downcast::<PyFrozenSet>() {
//
let mut set = BTreeSet::new();
for v in pfset {
set.insert(from(&v)?);
}
set.into()
}
// lists and tuples
else if let Ok(plist) = ob.downcast::<PyList>() {
let mut array = Vec::new();
for v in plist {
array.push(from(&v)?);
}
array.into()
} else if let Ok(ptuple) = ob.downcast::<PyTuple>() {
let mut array = Vec::new();
for v in ptuple {
array.push(from(&v)?);
}
array.into()
}
// String
else if let Ok(s) = ob.extract::<String>() {
s.into()
}
// Numeric
else if let Ok(v) = ob.extract::<i64>() {
v.into()
} else if let Ok(v) = ob.extract::<u64>() {
v.into()
} else if let Ok(v) = ob.extract::<f64>() {
v.into()
}
// Boolean
else if let Ok(b) = ob.extract::<bool>() {
b.into()
}
// None
else if ob.downcast::<PyNone>().is_ok() {
Value::Null
}
// Anything that is a sequence
else if let Ok(pseq) = ob.downcast::<PySequence>() {
let mut array = Vec::new();
for i in 0..pseq.len()? {
array.push(from(&pseq.get_item(i)?)?);
}
array.into()
}
// Anything that is a map
else if let Ok(pmap) = ob.downcast::<PyMapping>() {
let mut map = BTreeMap::new();
let keys = pmap.keys()?;
let values = pmap.values()?;
for i in 0..keys.len()? {
let key = keys.get_item(i)?;
let value = values.get_item(i)?;
map.insert(from(&key)?, from(&value)?);
}
map.into()
} else {
return Err(PyErr::new::<PyTypeError, _>(
"object cannot be converted to RegoValue",
));
})
}
fn to(mut v: Value, py: Python<'_>) -> Result<PyObject> {
Ok(match v {
Value::Null => None::<u64>.to_object(py),
// TODO: Revisit this mapping
Value::Undefined => None::<u64>.to_object(py),
Value::Bool(b) => b.to_object(py),
Value::String(s) => s.to_object(py),
Value::Number(_) => {
if let Ok(f) = v.as_f64() {
f.to_object(py)
} else if let Ok(u) = v.as_u64() {
u.to_object(py)
} else {
v.as_i64()?.to_object(py)
}
}
Value::Array(_) => {
let list = PyList::empty_bound(py);
for v in std::mem::take(v.as_array_mut()?) {
list.append(to(v, py)?)?;
}
list.into()
}
Value::Set(_) => {
let set = PySet::empty_bound(py)?;
for v in std::mem::take(v.as_set_mut()?) {
set.add(to(v, py)?)?;
}
set.into()
}
Value::Object(_) => {
let dict = PyDict::new_bound(py);
for (k, v) in std::mem::take(v.as_object_mut()?) {
dict.set_item(to(k, py)?, to(v, py)?)?;
}
dict.into()
}
})
}
#[pymethods]
impl Engine {
/// Construct a new Engine
#[new]
pub fn new() -> Self {
Self {
engine: ::regorus::Engine::new(),
}
}
/// Add a policy
///
/// The policy is parsed into AST.
///
/// * `path`: A filename to be associated with the policy.
/// * `rego`: Rego policy.
pub fn add_policy(&mut self, path: String, rego: String) -> Result<String> {
self.engine.add_policy(path, rego)
}
/// Add a policy from given file.
///
/// The policy is parsed into AST.
///
/// * `path`: Path to the policy file.
pub fn add_policy_from_file(&mut self, path: String) -> Result<String> {
self.engine.add_policy_from_file(path)
}
/// Get the list of packages defined by loaded policies.
///
pub fn get_packages(&mut self) -> Result<Vec<String>> {
self.engine.get_packages()
}
/// Add policy data.
///
/// * `data`: Rego value. A Rego value is a number, bool, string, None
/// or a list/set/map whose items themselves are Rego values.
pub fn add_data(&mut self, data: &Bound<'_, PyAny>) -> Result<()> {
let data = from(data)?;
self.engine.add_data(data)
}
/// Add policy data.
///
/// * `data`: JSON encoded value to be used as policy data.
pub fn add_data_json(&mut self, data: String) -> Result<()> {
let data = Value::from_json_str(&data)?;
self.engine.add_data(data)
}
/// Add policy data from file.
///
/// * `path`: Path to JSON policy data.
pub fn add_data_from_json_file(&mut self, path: String) -> Result<()> {
let data = Value::from_json_file(path)?;
self.engine.add_data(data)
}
/// Clear policy data.
pub fn clear_data(&mut self) -> Result<()> {
self.engine.clear_data();
Ok(())
}
/// Set input.
///
/// * `input`: Rego value. A Rego value is a number, bool, string, None
/// or a list/set/map whose items themselves are Rego values.
pub fn set_input(&mut self, input: &Bound<'_, PyAny>) -> Result<()> {
let input = from(input)?;
self.engine.set_input(input);
Ok(())
}
/// Set input.
///
/// * `input`: JSON encoded value to be used as input to query.
pub fn set_input_json(&mut self, input: String) -> Result<()> {
let input = Value::from_json_str(&input)?;
self.engine.set_input(input);
Ok(())
}
/// Set input.
///
/// * `path`: Path to JSON input data.
pub fn set_input_from_json_file(&mut self, path: String) -> Result<()> {
let input = Value::from_json_file(path)?;
self.engine.set_input(input);
Ok(())
}
/// Evaluate query.
///
/// * `query`: Rego expression to be evaluate.
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);
for result in results.result.into_iter() {
let rdict = PyDict::new_bound(py);
let elist = PyList::empty_bound(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 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))?;
edict.set_item("location".to_object(py), ldict)?;
elist.append(edict)?;
}
rdict.set_item("expressions".to_object(py), elist)?;
rdict.set_item("bindings".to_object(py), to(result.bindings, py)?)?;
rlist.append(rdict)?;
}
let dict = PyDict::new_bound(py);
dict.set_item("result".to_object(py), rlist)?;
Ok(dict.into())
}
/// Evaluate query. Returns result as JSON.
///
/// * `query`: Rego expression to be evaluate.
pub fn eval_query_as_json(&mut self, query: String) -> Result<String> {
let results = self.engine.eval_query(query, false)?;
serde_json::to_string_pretty(&results).map_err(|e| anyhow!("{e}"))
}
/// Evaluate rule.
///
/// * `rule`: Full path to the rule.
pub fn eval_rule(&mut self, rule: String, py: Python<'_>) -> Result<PyObject> {
to(self.engine.eval_rule(rule)?, py)
}
/// Evaluate rule and return value as json.
///
/// * `rule`: Full path to the rule.
pub fn eval_rule_as_json(&mut self, rule: String) -> Result<String> {
let v = self.engine.eval_rule(rule)?;
v.to_json_str()
}
/// Enable code coverage
///
/// * `enable`: Whether to enable coverage or not.
pub fn set_enable_coverage(&mut self, enable: bool) {
self.engine.set_enable_coverage(enable)
}
/// Get coverage report as json.
///
pub fn get_coverage_report_as_json(&self) -> Result<String> {
let report = self.engine.get_coverage_report()?;
serde_json::to_string_pretty(&report).map_err(|e| anyhow!("{e}"))
}
/// Get coverage report as pretty printable string.
///
pub fn get_coverage_report_pretty(&self) -> Result<String> {
self.engine.get_coverage_report()?.to_string_pretty()
}
/// Clear coverage data.
///
pub fn clear_coverage_data(&mut self) {
self.engine.clear_coverage_data();
}
/// Gather print statements instead of printing to stderr.
///
pub fn set_gather_prints(&mut self, b: bool) {
self.engine.set_gather_prints(b)
}
/// Take gathered prints.
///
pub fn take_prints(&mut self) -> Result<Vec<String>> {
self.engine.take_prints()
}
/// Clone a [`Engine`]
///
/// To avoid having to parse same policy again, the engine can be cloned
/// after policies and data have been added.
fn clone(&self) -> Self {
Self {
engine: self.engine.clone(),
}
}
}
#[pymodule]
pub fn regorus(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<crate::Engine>()
}

94
bindings/python/test.py Normal file
View File

@@ -0,0 +1,94 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
import regorus
# Create engine
engine = regorus.Engine()
# Load policies
pkg = engine.add_policy_from_file('../../tests/aci/framework.rego')
print(' Loaded package %s' % pkg)
pkg = engine.add_policy_from_file('../../tests/aci/api.rego')
print(' Loaded package %s' % pkg)
pkg = engine.add_policy_from_file('../../tests/aci/policy.rego')
print(' Loaded package %s' % pkg)
# Add policy data
data = {
"metadata": {
"devices": {
"/run/layers/p0-layer0": "1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766",
"/run/layers/p0-layer1": "e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c",
"/run/layers/p0-layer2": "eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79",
"/run/layers/p0-layer3": "41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156",
"/run/layers/p0-layer4": "4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c",
"/run/layers/p0-layer5": "fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a"
}
}
}
engine.add_data(data)
# Set input
input = {
"containerID": "container0",
"layerPaths": [
"/run/layers/p0-layer0",
"/run/layers/p0-layer1",
"/run/layers/p0-layer2",
"/run/layers/p0-layer3",
"/run/layers/p0-layer4",
"/run/layers/p0-layer5"
],
"target": "/run/gcs/c/container0/rootfs"
}
engine.set_input(input)
# Eval query
results = engine.eval_query('data.framework.mount_overlay=x')
# Print results
print(results['result'][0])
# Eval query as json
results_json = engine.eval_query_as_json('data.framework.mount_overlay=x')
print(results_json)
# Eval rule
v = engine.eval_rule('data.framework.mount_overlay')
print(v)
# Eval rule as json
v = engine.eval_rule_as_json('data.framework.mount_overlay')
print(v)
# Enable coverage
engine.set_enable_coverage(True)
engine.eval_rule('data.framework.mount_overlay')
# Print coverage
report_json = engine.get_coverage_report_as_json()
print(report_json)
# Pretty coverage report
report = engine.get_coverage_report_pretty()
print(report)
# Clone engine
engine1 = engine.clone()
# Clear coverage data
engine.clear_coverage_data();
print(engine1.get_coverage_report_pretty())
# Enable gathering prints
engine1.set_gather_prints(True)
# Gather prints
engine1.eval_query('print("Hello")')
ps = engine1.take_prints()
print(ps)

14
bindings/ruby/.gitignore vendored Normal file
View File

@@ -0,0 +1,14 @@
/.bundle/
/.yardoc
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/
*.bundle
*.so
*.o
*.a
mkmf.log
target/

View File

@@ -0,0 +1,29 @@
require:
- rubocop-minitest
- rubocop-rake
AllCops:
TargetRubyVersion: 3.0
NewCops: enable
Layout/LineLength:
Max: 180
Lint/EmptyClass:
Enabled: false
Metrics/ClassLength:
Exclude:
- 'test/**/*.rb'
Metrics/MethodLength:
Enabled: false
Style/StringLiterals:
EnforcedStyle: double_quotes
Style/StringLiteralsInInterpolation:
EnforcedStyle: double_quotes
Style/WordArray:
Enabled: false

View File

@@ -0,0 +1 @@
ruby 3.3.1

View File

@@ -0,0 +1,5 @@
## [Unreleased]
## [0.1.0] - 2024-03-29
- Initial release

3
bindings/ruby/Cargo.toml Normal file
View File

@@ -0,0 +1,3 @@
[workspace]
members = ["ext/regorusrb"]
resolver = "2"

16
bindings/ruby/Gemfile Normal file
View File

@@ -0,0 +1,16 @@
# frozen_string_literal: true
source "https://rubygems.org"
# Specify your gem's dependencies in regorusrb.gemspec
gemspec
# These gems are required for local development and testing,
# but won't be included in the published gem
gem "minitest", "~> 5.23"
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

View File

@@ -0,0 +1,65 @@
PATH
remote: .
specs:
regorusrb (0.1.5)
rb_sys (~> 0.9.97)
GEM
remote: https://rubygems.org/
specs:
ast (2.4.2)
json (2.7.2)
language_server-protocol (3.17.0.3)
minitest (5.23.1)
parallel (1.24.0)
parser (3.3.1.0)
ast (~> 2.4.1)
racc
racc (1.8.0)
rainbow (3.1.1)
rake (13.2.1)
rake-compiler (1.2.7)
rake
rake-compiler-dock (1.5.0)
rb_sys (0.9.97)
regexp_parser (2.9.2)
rexml (3.2.8)
strscan (>= 3.0.9)
rubocop (1.64.0)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.31.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.31.3)
parser (>= 3.3.1.0)
rubocop-minitest (0.35.0)
rubocop (>= 1.61, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rake (0.6.0)
rubocop (~> 1.0)
ruby-progressbar (1.13.0)
strscan (3.1.0)
unicode-display_width (2.5.0)
PLATFORMS
ruby
x86_64-linux
DEPENDENCIES
minitest (~> 5.23)
rake (~> 13.2)
rake-compiler
rake-compiler-dock
regorusrb!
rubocop (~> 1.64)
rubocop-minitest
rubocop-rake
BUNDLED WITH
2.5.10

21
bindings/ruby/LICENSE.txt Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE

99
bindings/ruby/README.md Normal file
View File

@@ -0,0 +1,99 @@
# Regorusrb
**Regorus** is
- *Rego*-*Rus(t)* - A fast, light-weight [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/)
interpreter written in Rust.
- *Rigorous* - A rigorous enforcer of well-defined Rego semantics.
## Installation
Regorus can be used in Ruby by configuring bundler to build from the remote git source.
If using [Bundler](https://bundler.io/) to manage gems (recommended), edit your gemfile to include the following
`
gem "regorusrb", git: "https://github.com/microsoft/regorus/", glob: "bindings/ruby/*.gemspec"
`
or manually install checkout the source and build the gem
`
git clone https://github.com/microsoft/regorus/
cd regorus/bindings/ruby
rake && rake build # should eventually output 'regorusrb 0.1.0 built to pkg/regorusrb-0.1.0.gem.'
gem install --local ./pkg/regorusrb-0.1.0.gem
`
It is not yet available in rubygems.
See [Repository](https://github.com/microsoft/regorus).
To build this gem locally without bundler,
`rake build`
then to install the gem and build the native extensions
`gem install --local ./pkg/regorusrb-0.1.0.gem`
## Usage
```ruby
require "regorus"
engine = Regorus::Engine.new
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')
# can be strings or symbols
data = {
metadata: {
devices: {
"/run/layers/p0-layer0": "1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766",
"/run/layers/p0-layer1": "e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c",
"/run/layers/p0-layer2": "eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79",
"/run/layers/p0-layer3": "41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156",
"/run/layers/p0-layer4": "4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c",
"/run/layers/p0-layer5": "fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a"
}
}
}
engine.add_data(data)
input = {
"containerID": "container0",
"layerPaths": [
"/run/layers/p0-layer0",
"/run/layers/p0-layer1",
"/run/layers/p0-layer2",
"/run/layers/p0-layer3",
"/run/layers/p0-layer4",
"/run/layers/p0-layer5"
],
"target": "/run/gcs/c/container0/rootfs"
}
engine.set_input(input)
# Evaluate a specife rule
rule_results = engine.eval_rule('data.framework.mount_overlay')
puts rule_results # { "allowed" => true, "metadata" => [...]}
# Or evalute a full policy document
query_results = engine.eval_query('data.framework')
puts query_results[:result][0]
# Query results can can also be returned as JSON strings instead of Ruby Hash structure
results_json = engine.eval_query_as_json('data.framework.mount_overlay=x')
puts results_json
```
## Development
To update the gem version or dependencies within `bindings/ruby/Gemfile.lock`, run `bundle update --gemfile=bindings/ruby/Gemfile` from the project directory, or `cd bindings/ruby && bundle update`
After checking out the repo, `cd bindings/ruby` and run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.

29
bindings/ruby/Rakefile Normal file
View File

@@ -0,0 +1,29 @@
# frozen_string_literal: true
require "bundler/gem_tasks"
require "minitest/test_task"
require "rake/extensiontask"
require "rubocop/rake_task"
require "rb_sys/extensiontask"
Minitest::TestTask.create
RuboCop::RakeTask.new
desc "build the .gem file, including native extensions, according to the .gemspec"
task build: :compile
GEMSPEC = Gem::Specification.load("regorusrb.gemspec")
RbSys::ExtensionTask.new("regorusrb", GEMSPEC) do |ext|
ext.lib_dir = "lib/regorus"
ext.cross_compile = true
ext.cross_platform = %w[x86-mingw32 x64-mingw-ucrt x64-mingw32 x86-linux x86_64-linux x86_64-darwin arm64-darwin]
end
task default: %i[compile test rubocop]
desc "Build native extension for a given platform (i.e. rake 'native[x86_64-linux]')"
task :native, [:platform] do |_t, platform:|
sh "bundle", "exec", "rb-sys-dock", "--platform", platform, "--build"
end

11
bindings/ruby/bin/console Executable file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require "bundler/setup"
require "regorus"
# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.
require "irb"
IRB.start(__FILE__)

8
bindings/ruby/bin/setup Executable file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
set -vx
bundle install
# Do any other automated setup that you need to do here

View File

@@ -0,0 +1,19 @@
[package]
name = "regorusrb"
version = "0.1.5"
edition = "2021"
description = "Ruby bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
publish = false
[lib]
crate-type = ["cdylib"]
path = "src/lib.rs"
[features]
default = ["regorus/std", "regorus/full-opa"]
[dependencies]
magnus = { version = "0.6.4" }
regorus = { git = "https://github.com/microsoft/regorus", default-features = false, features = ["arc"] }
serde_json = "1.0.117"
serde_magnus = "0.8.1"

View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
require "mkmf"
require "rb_sys/mkmf"
create_rust_makefile("regorus/regorusrb") do |r|
r.auto_install_rust_toolchain = true
end

View File

@@ -0,0 +1,329 @@
use magnus::{exception::runtime_error, method, module, prelude::*, Error, Ruby};
use regorus::Engine as RegorusEngine;
use std::cell::RefCell;
use std::cmp::Ordering;
// `Value` exists under magnus, regorus, and serde_json, so be explicit
#[derive(Default)]
#[magnus::wrap(class = "Regorus::Engine")]
pub struct Engine {
engine: RefCell<RegorusEngine>,
}
impl Clone for Engine {
fn clone(&self) -> Self {
Self {
engine: self.engine.clone(),
}
}
}
impl Engine {
fn initialize(&self) {
let engine = RegorusEngine::new();
*self.engine.borrow_mut() = engine;
}
fn compare(&self, other: &Self) -> Result<i32, Error> {
let self_ptr: *const _ = &*self.engine.borrow();
let other_ptr: *const _ = &*other.engine.borrow();
match self_ptr.partial_cmp(&other_ptr) {
Some(Ordering::Less) => Ok(-1),
Some(Ordering::Equal) => Ok(0),
Some(Ordering::Greater) => Ok(1),
None => Err(Error::new(runtime_error(), "Comparison failed")),
}
}
fn add_policy(&self, path: String, rego: String) -> Result<String, Error> {
self.engine
.borrow_mut()
.add_policy(path, rego)
.map_err(|e| Error::new(runtime_error(), format!("Failed to add policy: {}", e)))
}
fn add_policy_from_file(&self, path: String) -> Result<String, Error> {
self.engine
.borrow_mut()
.add_policy_from_file(path)
.map_err(|e| Error::new(runtime_error(), format!("Failed to add policy: {}", e)))
}
fn add_data(&self, ruby_hash: magnus::RHash) -> Result<(), Error> {
let data_value: regorus::Value = serde_magnus::deserialize(ruby_hash).map_err(|e| {
Error::new(
runtime_error(),
format!("Failed to deserialize Ruby value: {}", e),
)
})?;
self.engine
.borrow_mut()
.add_data(data_value)
.map_err(|e| Error::new(runtime_error(), format!("Failed to add data: {}", e)))
}
fn add_data_json(&self, json_string: String) -> Result<(), Error> {
self.engine
.borrow_mut()
.add_data_json(&json_string)
.map_err(|e| Error::new(runtime_error(), format!("Failed to add data json: {}", e)))
}
fn add_data_from_json_file(&self, path: String) -> Result<(), Error> {
let json_data = regorus::Value::from_json_file(path).map_err(|e| {
Error::new(
runtime_error(),
format!("Failed to parse JSON data file: {}", e),
)
})?;
self.engine.borrow_mut().add_data(json_data).map_err(|e| {
Error::new(
runtime_error(),
format!("Failed to add data from file: {}", e),
)
})
}
fn clear_data(&self) -> Result<(), Error> {
self.engine.borrow_mut().clear_data();
Ok(())
}
fn set_input(&self, ruby_hash: magnus::RHash) -> Result<(), Error> {
let input_value: regorus::Value = serde_magnus::deserialize(ruby_hash).map_err(|e| {
Error::new(
runtime_error(),
format!("Failed to deserialize Ruby value: {}", e),
)
})?;
self.engine.borrow_mut().set_input(input_value);
Ok(())
}
fn set_input_json(&self, json_string: String) -> Result<(), Error> {
self.engine
.borrow_mut()
.set_input_json(&json_string)
.map_err(|e| Error::new(runtime_error(), format!("Failed to set input JSON: {}", e)))
}
fn add_input_from_json_file(&self, path: String) -> Result<(), Error> {
let json_data = regorus::Value::from_json_file(path).map_err(|e| {
Error::new(
runtime_error(),
format!("Failed to parse JSON input file: {}", e),
)
})?;
self.engine.borrow_mut().set_input(json_data);
Ok(())
}
fn eval_query(&self, query: String) -> Result<magnus::Value, Error> {
let results = self
.engine
.borrow_mut()
.eval_query(query, false)
.map_err(|e| Error::new(runtime_error(), format!("Failed to evaluate query: {}", e)))?;
serde_magnus::serialize(&results).map_err(|e| {
Error::new(
runtime_error(),
format!("Failed to serailzie query results: {}", e),
)
})
}
fn eval_query_as_json(&self, query: String) -> Result<String, Error> {
let results = self
.engine
.borrow_mut()
.eval_query(query, false)
.map_err(|e| {
Error::new(
runtime_error(),
format!("Failed to evaluate query as json: {}", e),
)
})?;
serde_json::to_string(&results).map_err(|e| {
Error::new(
runtime_error(),
format!("Failed to serialize query results: {}", e),
)
})
}
fn eval_rule(&self, query: String) -> Result<Option<magnus::Value>, Error> {
let result =
self.engine.borrow_mut().eval_rule(query).map_err(|e| {
Error::new(runtime_error(), format!("Failed to evaluate rule: {}", e))
})?;
match result {
regorus::Value::Undefined => Ok(None), // Convert undefined to Ruby's nil
_ => serde_magnus::serialize(&result) // Serialize other results normally
.map(Some)
.map_err(|e| {
magnus::Error::new(
runtime_error(),
format!("Failed to serialize the rule evaluation result: {}", e),
)
}),
}
}
fn eval_bool_query(&self, query: String) -> Result<bool, Error> {
self.engine
.borrow_mut()
.eval_bool_query(query, false)
.map_err(|e| Error::new(runtime_error(), format!("Failed to evaluate query: {}", e)))
}
fn eval_allow_query(&self, query: String) -> Result<bool, Error> {
Ok(self.engine.borrow_mut().eval_allow_query(query, false))
}
fn eval_deny_query(&self, query: String) -> Result<bool, Error> {
Ok(self.engine.borrow_mut().eval_deny_query(query, false))
}
fn set_enable_coverage(&self, enable: bool) -> Result<(), Error> {
self.engine.borrow_mut().set_enable_coverage(enable);
Ok(())
}
fn get_coverage_report_as_json(&self) -> Result<String, Error> {
let report = self
.engine
.borrow_mut()
.get_coverage_report()
.map_err(|e| {
Error::new(
runtime_error(),
format!("Failed to get coverage report as json: {}", e),
)
})?;
serde_json::to_string(&report).map_err(|e| {
Error::new(
runtime_error(),
format!("Failed to serialize coverage report: {}", e),
)
})
}
fn get_coverage_report_pretty(&self) -> Result<String, Error> {
let report = self
.engine
.borrow_mut()
.get_coverage_report()
.map_err(|e| {
Error::new(
runtime_error(),
format!("Failed to get coverage report: {}", e),
)
})?;
report.to_string_pretty().map_err(|e| {
Error::new(
runtime_error(),
format!("Failed to convert report to colored string: {}", e),
)
})
}
fn clear_coverage_data(&self) -> Result<(), Error> {
self.engine.borrow_mut().clear_coverage_data();
Ok(())
}
// Print statements can be gathered async instead of printing to stderr
fn set_gather_prints(&self, enable: bool) -> Result<(), Error> {
self.engine.borrow_mut().set_gather_prints(enable);
Ok(())
}
fn take_prints(&self) -> Result<Vec<String>, Error> {
self.engine.borrow_mut().take_prints().map_err(|e| {
Error::new(
runtime_error(),
format!("Failed to gather print statement: {}", e),
)
})
}
}
#[magnus::init]
fn init(ruby: &Ruby) -> Result<(), Error> {
let regorus_module = ruby.define_module("Regorus")?;
let engine_class = regorus_module.define_class("Engine", ruby.class_object())?;
// ruby object methods
engine_class.define_alloc_func::<Engine>();
engine_class.define_method("initialize", method!(Engine::initialize, 0))?;
engine_class.define_method("clone", method!(Engine::clone, 0))?;
engine_class.define_method("<=>", method!(Engine::compare, 1))?;
// defines <, <=, >, >=, and == based on <=>
engine_class.include_module(module::comparable())?;
// policy operations
engine_class.define_method("add_policy", method!(Engine::add_policy, 2))?;
engine_class.define_method(
"add_policy_from_file",
method!(Engine::add_policy_from_file, 1),
)?;
// data operations
engine_class.define_method("add_data", method!(Engine::add_data, 1))?;
engine_class.define_method("add_data_json", method!(Engine::add_data_json, 1))?;
engine_class.define_method(
"add_data_from_json_file",
method!(Engine::add_data_from_json_file, 1),
)?;
engine_class.define_method("clear_data", method!(Engine::clear_data, 0))?;
// input operations
engine_class.define_method("set_input", method!(Engine::set_input, 1))?;
engine_class.define_method("set_input_json", method!(Engine::set_input_json, 1))?;
engine_class.define_method(
"add_input_from_json_file",
method!(Engine::add_input_from_json_file, 1),
)?;
// query operations
engine_class.define_method("eval_query", method!(Engine::eval_query, 1))?;
engine_class.define_method("eval_query_as_json", method!(Engine::eval_query_as_json, 1))?;
engine_class.define_method("eval_rule", method!(Engine::eval_rule, 1))?;
engine_class.define_method("eval_bool_query", method!(Engine::eval_bool_query, 1))?;
engine_class.define_method("eval_allow_query", method!(Engine::eval_allow_query, 1))?;
engine_class.define_method("eval_deny_query", method!(Engine::eval_deny_query, 1))?;
// coverage operations
engine_class.define_method(
"set_enable_coverage",
method!(Engine::set_enable_coverage, 1),
)?;
engine_class.define_method(
"get_coverage_report_as_json",
method!(Engine::get_coverage_report_as_json, 0),
)?;
engine_class.define_method(
"get_coverage_report_pretty",
method!(Engine::get_coverage_report_pretty, 0),
)?;
engine_class.define_method(
"clear_coverage_data",
method!(Engine::clear_coverage_data, 0),
)?;
// print statements
engine_class.define_method("set_gather_prints", method!(Engine::set_gather_prints, 1))?;
engine_class.define_method("take_prints", method!(Engine::take_prints, 0))?;
Ok(())
}

View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
require_relative "regorus/version"
require_relative "regorus/regorusrb"
module Regorus
class Engine; end
end

View File

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

View File

@@ -0,0 +1,30 @@
# frozen_string_literal: true
require_relative "lib/regorus/version"
Gem::Specification.new do |spec|
spec.name = "regorusrb"
spec.version = Regorus::VERSION
spec.authors = ["David Marshall"]
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.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/bindings/ruby/CHANGELOG.md"
spec.metadata["rubygems_mfa_required"] = "true"
spec.files = Dir["lib/*.rb", "lib/regorus/*.rb", "ext/**/*.{rs,rb,lock,toml}", "Cargo.{lock,toml}", "LICENSE.txt", "README.md"]
spec.bindir = "exe"
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"
end

View File

@@ -0,0 +1,4 @@
module Regorus
VERSION: String
# See the writing guide of rbs: https://github.com/ruby/rbs#guides
end

View File

@@ -0,0 +1,6 @@
# frozen_string_literal: true
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
require "regorus"
require "minitest/autorun"

View File

@@ -0,0 +1,232 @@
# frozen_string_literal: true
require "test_helper"
require "json"
class TestRegorus < Minitest::Test
ALICE = "Alice"
BOB = "Bob"
CARLOS = "Carlos"
def setup
@engine = ::Regorus::Engine.new
@engine.add_policy("regorus_test.rego", example_policy)
@engine.add_data(example_data)
end
def example_policy
<<~REGO
package regorus_test
is_manager {
input.name == data.managers[_]
}
is_employee {
input.name == data.employees[_]
}
# Set a default value for to return false instead of nil
default is_manager_bool = false
default is_employee_bool = false
is_manager_bool {
is_manager
}
is_employee_bool {
is_employee
}
REGO
end
def example_data
{
"managers" => [ALICE],
"employees" => [ALICE, BOB]
}
end
def input_for(name)
{ "name" => name }
end
def test_version_number_presence
refute_nil ::Regorus::VERSION
end
def test_engine_creation
assert_instance_of ::Regorus::Engine, ::Regorus::Engine.new
end
def test_policy_addition
# returns the package name from the REGO, not the filename
assert_equal "data.regorus_test", @engine.add_policy("example.rego", example_policy)
end
def test_object_creation_with_new
refute_same ::Regorus::Engine.new, ::Regorus::Engine.new
end
def test_data_addition
assert_silent { @engine.add_data(example_data) }
end
def test_data_addition_as_json
assert_silent { @engine.add_data_json(example_data.to_json) }
end
def test_query_evaluation_for_alice
@engine.set_input(input_for(ALICE))
assert_equal alice_results, @engine.eval_query("data.regorus_test")
end
def test_query_evaluation_for_bob
@engine.set_input(input_for(BOB))
assert_equal bob_results, @engine.eval_query("data.regorus_test")
end
def test_query_evaluation_as_json
@engine.set_input(input_for(ALICE))
assert_equal alice_results.to_json, @engine.eval_query_as_json("data.regorus_test")
end
def test_rule_evaluation_for_alice
@engine.set_input(input_for(ALICE))
assert @engine.eval_rule("data.regorus_test.is_employee")
assert @engine.eval_rule("data.regorus_test.is_employee_bool")
assert @engine.eval_rule("data.regorus_test.is_manager")
assert @engine.eval_rule("data.regorus_test.is_manager_bool")
end
def test_rule_evaluation_for_bob
@engine.set_input(input_for(BOB))
assert @engine.eval_rule("data.regorus_test.is_employee")
assert @engine.eval_rule("data.regorus_test.is_employee_bool")
assert_nil @engine.eval_rule("data.regorus_test.is_manager")
refute @engine.eval_rule("data.regorus_test.is_manager_bool")
end
def test_rule_evaluation_for_carlos
@engine.set_input(input_for(CARLOS))
assert_nil @engine.eval_rule("data.regorus_test.is_employee")
refute @engine.eval_rule("data.regorus_test.is_employee_bool")
assert_nil @engine.eval_rule("data.regorus_test.is_manager")
refute @engine.eval_rule("data.regorus_test.is_manager_bool")
end
def test_eval_bool_query
assert @engine.eval_bool_query("1 < 2")
refute @engine.eval_bool_query("1 > 2")
assert_raises(RuntimeError) { @engine.eval_bool_query("1 + 1") }
assert_raises(RuntimeError) { @engine.eval_bool_query("true; true") }
assert_raises(RuntimeError) { @engine.eval_bool_query("true; false; true") }
end
def test_eval_allow_query
assert @engine.eval_allow_query("1 < 2")
refute @engine.eval_allow_query("1 > 2")
refute @engine.eval_allow_query("1 + 1")
refute @engine.eval_allow_query("true; true")
refute @engine.eval_allow_query("true; false; true")
end
def test_eval_deny_query
assert @engine.eval_deny_query("1 < 2")
refute @engine.eval_deny_query("1 > 2")
assert @engine.eval_deny_query("1 + 1")
assert @engine.eval_deny_query("true; true")
assert @engine.eval_deny_query("true; false; true")
end
def test_missing_rules_handling
@engine.set_input(input_for(ALICE))
assert_raises(RuntimeError) { @engine.eval_rule("data.regorus_test.not_a_rule") }
end
def test_engine_cloning
cloned_engine = @engine.clone
assert_instance_of ::Regorus::Engine, cloned_engine
refute_same @engine, cloned_engine
end
def test_coverage_printing_json
@engine.set_input(input_for(ALICE))
@engine.set_enable_coverage(true)
@engine.eval_rule("data.regorus_test.is_employee")
assert_match(/covered":\[7\],"not_covered":\[3,11,12,15,19\]/, @engine.get_coverage_report_as_json)
end
def test_coverage_printing_pretty
@engine.set_input(input_for(ALICE))
@engine.set_enable_coverage(true)
@engine.eval_rule("data.regorus_test.is_employee")
# to see the colors in the ruby terminal, use `puts @engine.get_coverage_report``
pretty_coverage_report = @engine.get_coverage_report_pretty
assert_match(/\e\[31m 3 input.name == data.managers\[_\]/, pretty_coverage_report)
assert_match(/\e\[32m 7 input.name == data.employees\[_\]/, pretty_coverage_report)
end
def test_gather_print_statements
@engine.set_gather_prints(true)
@engine.eval_query('print("Hello")')
assert_equal ["<query.rego>:1: Hello"], @engine.take_prints
end
def alice_results
{
result: [
{
expressions: [
{
value: {
"is_employee" => true,
"is_employee_bool" => true,
"is_manager" => true,
"is_manager_bool" => true
},
text: "data.regorus_test",
location: {
row: 1,
col: 1
}
}
]
}
]
}
end
def bob_results
{
result: [
{
expressions: [
{
value: {
"is_employee" => true,
"is_employee_bool" => true,
"is_manager_bool" => false
},
text: "data.regorus_test",
location: {
row: 1,
col: 1
}
}
]
}
]
}
end
end

View File

@@ -0,0 +1,14 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.1.0](https://github.com/microsoft/regorus/releases/tag/regorusjs-v0.1.0) - 2024-02-01
### Other
- Document bindings ([#119](https://github.com/microsoft/regorus/pull/119))
- Python bindings ([#115](https://github.com/microsoft/regorus/pull/115))
- WASM binding ([#114](https://github.com/microsoft/regorus/pull/114))

22
bindings/wasm/Cargo.toml Normal file
View File

@@ -0,0 +1,22 @@
[package]
name = "regorusjs"
version = "0.1.5"
edition = "2021"
repository = "https://github.com/microsoft/regorus/bindings/wasm"
description = "WASM bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
keywords = ["interpreter", "opa", "policy-as-code", "rego"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib"]
[features]
default = ["regorus/std", "regorus/full-opa"]
[dependencies]
regorus = { path = "../..", default-features = false, features = ["arc"] }
serde_json = "1.0.111"
wasm-bindgen = "0.2.90"
[dev-dependencies]
wasm-bindgen-test = "0.3.40"

19
bindings/wasm/README.md Normal file
View File

@@ -0,0 +1,19 @@
# regorusjs
**Regorus** is
- *Rego*-*Rus(t)* - A fast, light-weight [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/)
interpreter written in Rust.
- *Rigorous* - A rigorous enforcer of well-defined Rego semantics.
`regorusjs` is Regorus compiled into WASM.
See [Repository](https://github.com/microsoft/regorus).
To build this binding, see [building.md](https://github.com/microsoft/regorus/blob/main/bindings/wasm/building.md)
## Usage
See [test.js](https://github.com/microsoft/regorus/blob/main/bindings/wasm/test.js) for example usage.

34
bindings/wasm/building.md Normal file
View File

@@ -0,0 +1,34 @@
- Install `wasm-pack`
```
cargo install wasm-pack
```
- Build `regorusjs` for nodejs.
```
cd bindings/wasm
wasm-pack build --target nodejs --release
```
- Install [nodejs](https://nodejs.org/en/download)
- Run the test script
```
$ node test.js
\\{
\\ "result": [
\\ {
\\ "expressions": [
\\ {
\\ "value": "Hello, World!",
\\ "text": "data.test.message",
\\ "location": {
\\ "row": 1,
\\ "col": 1
\\ }
\\ }
\\ ]
\\ }
\\ ]
\\}
```

251
bindings/wasm/src/lib.rs Normal file
View File

@@ -0,0 +1,251 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#![allow(non_snake_case)]
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
/// WASM wrapper for [`regorus::Engine`]
pub struct Engine {
engine: regorus::Engine,
}
fn error_to_jsvalue<E: std::fmt::Display>(e: E) -> JsValue {
JsValue::from_str(&format!("{e}"))
}
impl Default for Engine {
fn default() -> Self {
Self::new()
}
}
impl Clone for Engine {
/// Clone a [`Engine`]
///
/// To avoid having to parse same policy again, the engine can be cloned
/// after policies and data have been added.
fn clone(&self) -> Self {
Self {
engine: self.engine.clone(),
}
}
}
#[wasm_bindgen]
impl Engine {
#[wasm_bindgen(constructor)]
/// Construct a new Engine
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html
pub fn new() -> Self {
Self {
engine: regorus::Engine::new(),
}
}
/// Add a policy
///
/// The policy is parsed into AST.
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.add_policy
///
/// * `path`: A filename to be associated with the policy.
/// * `rego`: Rego policy.
pub fn addPolicy(&mut self, path: String, rego: String) -> Result<String, JsValue> {
self.engine.add_policy(path, rego).map_err(error_to_jsvalue)
}
/// Add policy data.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.add_data
/// * `data`: JSON encoded value to be used as policy data.
pub fn addDataJson(&mut self, data: String) -> Result<(), JsValue> {
let data = regorus::Value::from_json_str(&data).map_err(error_to_jsvalue)?;
self.engine.add_data(data).map_err(error_to_jsvalue)
}
/// Get the list of packages defined by loaded policies.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_packages
pub fn getPackages(&self) -> Result<Vec<String>, JsValue> {
self.engine.get_packages().map_err(error_to_jsvalue)
}
/// Clear policy data.
///
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.clear_data
pub fn clearData(&mut self) -> Result<(), JsValue> {
self.engine.clear_data();
Ok(())
}
/// Set input.
///
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.set_input
/// * `input`: JSON encoded value to be used as input to query.
pub fn setInputJson(&mut self, input: String) -> Result<(), JsValue> {
let input = regorus::Value::from_json_str(&input).map_err(error_to_jsvalue)?;
self.engine.set_input(input);
Ok(())
}
/// Evaluate query.
///
/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.eval_query
/// * `query`: Rego expression to be evaluate.
pub fn evalQuery(&mut self, query: String) -> Result<String, JsValue> {
let results = self
.engine
.eval_query(query, false)
.map_err(error_to_jsvalue)?;
serde_json::to_string_pretty(&results).map_err(error_to_jsvalue)
}
/// Evaluate rule(s) at given path.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.eval_rule
///
/// * `path`: The full path to the rule(s).
pub fn evalRule(&mut self, path: String) -> Result<String, JsValue> {
let v = self.engine.eval_rule(path).map_err(error_to_jsvalue)?;
v.to_json_str().map_err(error_to_jsvalue)
}
/// Gather output from print statements instead of emiting to stderr.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_gather_prints
/// * `b`: Whether to enable gathering prints or not.
pub fn setGatherPrints(&mut self, b: bool) {
self.engine.set_gather_prints(b)
}
/// Take the gathered output of print statements.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.take_prints
pub fn takePrints(&mut self) -> Result<Vec<String>, JsValue> {
self.engine.take_prints().map_err(error_to_jsvalue)
}
/// Enable/disable policy coverage.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_enable_coverage
/// * `b`: Whether to enable gathering coverage or not.
pub fn setEnableCoverage(&mut self, enable: bool) {
self.engine.set_enable_coverage(enable)
}
/// Get the coverage report as json.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_coverage_report
pub fn getCoverageReport(&self) -> Result<String, JsValue> {
let report = self
.engine
.get_coverage_report()
.map_err(error_to_jsvalue)?;
serde_json::to_string_pretty(&report).map_err(error_to_jsvalue)
}
/// Clear gathered coverage data.
///
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.clear_coverage_data
pub fn clearCoverageData(&mut self) {
self.engine.clear_coverage_data()
}
/// Get ANSI color coded coverage report.
///
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Report.html#method.to_string_pretty
pub fn getCoverageReportPretty(&self) -> Result<String, JsValue> {
let report = self
.engine
.get_coverage_report()
.map_err(error_to_jsvalue)?;
report.to_string_pretty().map_err(error_to_jsvalue)
}
}
#[cfg(test)]
mod tests {
use wasm_bindgen::prelude::*;
use wasm_bindgen_test::wasm_bindgen_test;
#[wasm_bindgen_test]
pub fn basic() -> Result<(), JsValue> {
let mut engine = crate::Engine::new();
engine.setEnableCoverage(true);
// Exercise all APIs.
engine.addDataJson(
r#"
{
"foo" : "bar"
}
"#
.to_string(),
)?;
engine.setInputJson(
r#"
{
"message" : "Hello"
}
"#
.to_string(),
)?;
let pkg = engine.addPolicy(
"hello.rego".to_string(),
r#"
package test
message = input.message"#
.to_string(),
)?;
assert_eq!(pkg, "data.test");
let results = engine.evalQuery("data".to_string())?;
let r = regorus::Value::from_json_str(&results).map_err(crate::error_to_jsvalue)?;
let v = &r["result"][0]["expressions"][0]["value"];
// Ensure that input and policy were evaluated.
assert_eq!(v["test"]["message"], regorus::Value::from("Hello"));
// Test that data was set.
assert_eq!(v["foo"], regorus::Value::from("bar"));
// Use eval_rule to perform same query.
let v = engine.evalRule("data.test.message".to_owned())?;
let v = regorus::Value::from_json_str(&v).map_err(crate::error_to_jsvalue)?;
// Ensure that input and policy were evaluated.
assert_eq!(v, regorus::Value::from("Hello"));
let pkgs = engine.getPackages()?;
assert_eq!(pkgs, vec!["data.test"]);
engine.setGatherPrints(true);
let _ = engine.evalQuery("print(\"Hello\")".to_owned());
let prints = engine.takePrints()?;
assert_eq!(prints, vec!["<query.rego>:1: Hello"]);
// Test clone.
let mut engine1 = engine.clone();
// Test code coverage.
let report = engine1.getCoverageReport()?;
let r = regorus::Value::from_json_str(&report).map_err(crate::error_to_jsvalue)?;
assert_eq!(
r["files"][0]["covered"]
.as_array()
.map_err(crate::error_to_jsvalue)?,
&vec![regorus::Value::from(3)]
);
println!("{}", engine1.getCoverageReportPretty()?);
engine1.clearCoverageData();
Ok(())
}
}

87
bindings/wasm/test.js Normal file
View File

@@ -0,0 +1,87 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
var regorus = require('./pkg/regorusjs');
// Create an engine.
var engine = new regorus.Engine();
// Enable code coverage
engine.setEnableCoverage(true);
// Add Rego policy.
var pkg = engine.addPolicy(
// Associate this file name with policy
'hello.rego',
// Rego policy
`
package test
x = 10
# Join messages
message = concat(", ", [input.message, data.message])
`);
console.log(pkg);
// data.test
// Set policy data
engine.addDataJson(`
{
"message" : "World!"
}
`);
// Set policy input
engine.setInputJson(`
{
"message" : "Hello"
}
`);
// Eval rule as json
var value = engine.evalRule('data.test.message');
value = JSON.parse(value);
// Display value
console.log(value);
// Hello, World!
// Eval query
results = engine.evalQuery('data.test.message');
// Display
console.log(results);
// {
// "result": [
// {
// "expressions": [
// {
// "value": "Hello, World!",
// "text": "data.test.message",
// "location": {
// "row": 1,
// "col": 1
// }
// }
// ]
// }
// ]
// }
// Convert results to object
results = JSON.parse(results);
// Process result
console.log(results.result[0].expressions[0].value);
// Hello, World!
// Print coverage report
report = engine.getCoverageReport();
console.log(report);
// Print pretty report.
report = engine.getCoverageReportPretty();
console.log(report);

View File

@@ -3,7 +3,6 @@
use anyhow::Result;
use std::path::Path;
use std::process::Command;
fn main() -> Result<()> {
// Copy hooks to appropriate location so that git will run them.
@@ -14,12 +13,15 @@ fn main() -> Result<()> {
}
// Supply information as compile-time environment variables.
let output = Command::new("git")
.args(["rev-parse", "HEAD"])
.output()
.unwrap();
let git_hash = String::from_utf8(output.stdout).unwrap();
println!("cargo:rustc-env=GIT_HASH={}", git_hash);
#[cfg(feature = "opa-runtime")]
{
let output = std::process::Command::new("git")
.args(["rev-parse", "HEAD"])
.output()
.expect("`git rev-parse HEAD` failed.");
let git_hash = String::from_utf8(output.stdout).unwrap();
println!("cargo:rustc-env=GIT_HASH={}", git_hash);
}
Ok(())
}

240
docs/builtins.md Normal file
View File

@@ -0,0 +1,240 @@
# Built-in Functions
This page lists all the supported Rego built-in functions and the cargo feature that is needed to enable each builtin.
Those builtins that are not need for a specific use of the Regorus crate can be excluded from the binary by not specifying
the corresponding feature. This is useful in Confidential Computing scenarios where
- There needs to be control over what a policy execution can and cannot do.
- There needs to be control over exactly what goes into the [Trusted Computing Base](https://en.wikipedia.org/wiki/Trusted_computing_base).
Currently many builtins are `baked-in`, i.e. there is no way to exclude them from the TCB.
In future, each builtin will be associated with a feature (many builtins could be associated with the same feature).
- [Comparison](https://www.openpolicyagent.org/docs/latest/policy-reference/#comparison)
| Builtin | Feature |
|--------------------------------------------------------------------------------------------------|---------|
| [x == y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-comparison-equal) | _ |
| [x > y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-comparison-gt) | _ |
| [x >= y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-comparison-gte) | _ |
| [x < y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-comparison-lt) | _ |
| [x <= y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-comparison-lte) | _ |
| [x != y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-comparison-neq) | _ |
- [Numbers](https://www.openpolicyagent.org/docs/latest/policy-reference/#numbers)
| Builtin | Feature |
|-----------------------------------------------------------------------------------------------------------------------|---------|
| [abs](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-abs) | _ |
| [ceil](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-ceil) | _ |
| [x / y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-div) | _ |
| [floor](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-floor) | _ |
| [x - y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-minus) | _ |
| [x * y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-mul) | _ |
| [numbers.range](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-numbersrange) | _ |
| [numbers.range_step](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-numbersrange_step) | _ |
| [x + y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-plus) | _ |
| [rand.intn](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-randintn) | _ |
| [x % y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-rem) | _ |
| [round](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-round) | _ |
- [Aggregates](https://www.openpolicyagent.org/docs/latest/policy-reference/#aggregates)
| Builtin | Feature |
|-----------------------------------------------------------------------------------------------------|---------|
| [count](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-aggregates-count) | _ |
| [max](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-aggregates-max) | _ |
| [min](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-aggregates-min) | _ |
| [product](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-aggregates-product) | _ |
| [sort](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-aggregates-sort) | _ |
| [sum](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-aggregates-sum) | _ |
- [Arrays](https://www.openpolicyagent.org/docs/latest/policy-reference/#arrays-2)
| Builtin | Feature |
|-----------------------------------------------------------------------------------------------------------|---------|
| [array.concat](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-array-arrayconcat) | _ |
| [array.reverse](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-array-arrayreverse) | _ |
| [array.slice](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-array-arrayslice) | _ |
- [Sets](https://www.openpolicyagent.org/docs/latest/policy-reference/#sets-2)
| Builtin | Feature |
|---------------------------------------------------------------------------------------------------------|---------|
| [x & y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-sets-and) | _ |
| [intersection](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-sets-intersection) | _ |
| [x - y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-sets-minus) | _ |
| [x \| y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-sets-or) | _ |
| [union](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-sets-union) | _ |
- [Objects](https://www.openpolicyagent.org/docs/latest/policy-reference/#object)
| Builtin | Feature |
|----------------------------------------------------------------------------------------------------------------------|--------------|
| [json.filter](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-jsonfilter) | _ |
| [json.match_schema](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-jsonmatch_schema) | `jsonschema` |
| [json.remove](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-jsonremove) | _ |
| [json.verify_schema](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-jsonverify_schema) | `jsonschema` |
| [object.filter](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-objectfilter) | _ |
| [object.get](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-objectget) | _ |
| [object.keys](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-objectkeys) | _ |
| [object.remove](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-objectremove) | _ |
| [object.subset](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-objectsubset) | _ |
| [object.union](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-objectunion) | _ |
| [object.union_n](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-object-objectunion_n) | _ |
- [Strings](https://www.openpolicyagent.org/docs/latest/policy-reference/#strings)
| Builtin | Feature |
|-----------------------------------------------------------------------------------------------------------------------------------|---------|
| [concat](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-concat) | _ |
| [contains](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-contains) | _ |
| [endswith](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-endswith) | _ |
| [format_int](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-format_int) | _ |
| [indexof](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-indexof) | _ |
| [indexof_n](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-indexof_n) | _ |
| [lower](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-lower) | _ |
| [replace](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-replace) | _ |
| [split](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-split) | _ |
| [sprintf](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-sprintf) | _ |
| [startswith](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-startswith) | _ |
| [strings.any_prefix_match](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-stringsany_prefix_match) | _ |
| [strings.any_suffix_match](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-stringsany_suffix_match) | _ |
| [strings.render_template](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-stringsrender_template) | _ |
| [strings.replace_n](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-stringsreplace_n) | _ |
| [strings.reverse](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-stringsreverse) | _ |
| [substring](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-substring) | _ |
| [trim](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-trim) | _ |
| [trim_left](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-trim_left) | _ |
| [trim_prefix](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-trim_prefix) | _ |
| [trim_right](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-trim_right) | _ |
| [trim_space](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-trim_space) | _ |
| [trim_suffix](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-trim_suffix) | _ |
| [upper](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-strings-upper) | _ |
- [Regex](https://www.openpolicyagent.org/docs/latest/policy-reference/#regex)
| Builtin | Feature |
|-------------------------------------------------------------------------------------------------------------------------------------------------|---------|
| [regex.find_all_string_submatch_n](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-regex-regexfind_all_string_submatch_n) | `regex` |
| [regex.find_n](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-regex-regexfind_n) | `regex` |
| [regex.globs_match](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-regex-regexglobs_match) | `regex` |
| [regex.is_valid](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-regex-regexis_valid) | `regex` |
| [regex.match](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-regex-regexmatch) | `regex` |
| [regex.replace](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-regex-regexreplace) | `regex` |
| [regex.split](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-regex-regexsplit) | `regex` |
| [regex.template_match](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-regex-regextemplate_match) | `regex` |
- [Glob](https://www.openpolicyagent.org/docs/latest/policy-reference/#regex)
| Builtin | Feature |
|--------------------------------------------------------------------------------------------------------------|---------|
| [glob.match](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-glob-globmatch) | `glob` |
| [glob.quote_meta](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-glob-globquote_meta) | `glob` |
- [Bitwise](https://www.openpolicyagent.org/docs/latest/policy-reference/#regex)
| Builtin | Feature |
|------------------------------------------------------------------------------------------------------|---------|
| [bits.and](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-bits-bitsand) | _ |
| [bits.lsh](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-bits-bitslsh) | _ |
| [bits.negate](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-bits-bitsnegate) | _ |
| [bits.or](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-bits-bitsor) | _ |
| [bits.rsh](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-bits-bitsrsh) | _ |
| [bits.xor](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-bits-bitsxor) | _ |
- [Conversions](https://www.openpolicyagent.org/docs/latest/policy-reference/#conversions)
| Builtin | Feature |
|-------|---------|
[to_number](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-conversions-to_number) | _ |
|
- [Units](https://www.openpolicyagent.org/docs/latest/policy-reference/#units)
| Builtin | Feature |
|-------------------------------------------------------------------------------------------------------------------|---------|
| [units.parse](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-units-unitsparse) | _ |
| [units.parse_bytes](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-units-unitsparse_bytes) | _ |
- [Types](https://www.openpolicyagent.org/docs/latest/policy-reference/#types)
| Builtin | Feature |
|------------------------------------------------------------------------------------------------------|---------|
| [is_array](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-types-is_array) | _ |
| [is_boolean](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-types-is_boolean) | _ |
| [is_null](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-types-is_null) | _ |
| [is_number](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-types-is_number) | _ |
| [is_object](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-types-is_object) | _ |
| [is_set](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-types-is_set) | _ |
| [is_string](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-types-is_string) | _ |
| [type_name](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-types-type_name) | _ |
- [Encoding](https://www.openpolicyagent.org/docs/latest/policy-reference/#encoding)
| Builtin | Feature |
|--------------------------------------------------------------------------------------------------------------------------------------|-------------|
| [base64.is_valid](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-base64is_valid) | `base64` |
| [base64url.decode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-base64urldecode) | `base64` |
| [base64url.encode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-base64urlencode) | `base64url` |
| [base64url.encode_no_pad](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-base64urlencode_no_pad) | `base64url` |
| [hex.decode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-hexdecode) | `hex` |
| [hex.encode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-hexencode) | `hex` |
| [json.is_valid](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-jsonis_valid) | _ |
| [json.marshal](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-jsonmarshal) | _ |
| [json.marshal_with_options](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-jsonmarshal_with_options) | _ |
| [json.unmarshal](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-jsonunmarshal) | _ |
| [urlquery.decode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-urlquerydecode) | `urlquery` |
| [urlquery.decode_object](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-urlquerydecode_object) | `urlquery` |
| [urlquery.encode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-urlqueryencode) | `urlquery` |
| [urlquery.encode_object](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-urlqueryencode_object) | `urlquery` |
| [yaml.is_valid](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-yamlis_valid) | `yaml` |
| [yaml.marshal](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-yamlmarshal) | `yaml` |
| [yaml.unmarshal](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-yamlunmarshal) | `yaml` |
- [Time](https://www.openpolicyagent.org/docs/latest/policy-reference/#time)
| Builtin | Feature |
|----------------------------------------------------------------------------------------------------------------------------|---------|
| ([time.add_date](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timeadd_date) | `time` |
| [time.add_date](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timeadd_date) | `time` |
| [time.clock](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timeclock) | `time` |
| [time.date](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timedate) | `time` |
| [time.diff](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timediff) | `time` |
| [time.format](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timeformat) | `time` |
| [time.now_ns](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timenow_ns) | `time` |
| [time.parse_duration_ns](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timeparse_duration_ns) | `time` |
| [time.parse_ns](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timeparse_ns) | `time` |
| [time.parse_rfc3339_ns](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timeparse_rfc3339_ns) | `time` |
| [time.weekday](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-time-timeweekday) | `time` |
- [Cryptography](https://www.openpolicyagent.org/docs/latest/policy-reference/#crypto)
| Builtin | Feature |
|---------------------------------------------------------------------------------------------------------------------|----------|
| [crypto.hmac.equal](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-crypto-cryptohmacequal) | `crypto` |
| [crypto.hmac.md5](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-crypto-cryptohmacmd5) | `crypto` |
| [crypto.hmac.sha1](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-crypto-cryptohmacsha1) | `crypto` |
| [crypto.hmac.sha256](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-crypto-cryptohmacsha256) | `crypto` |
| [crypto.hmac.sha512](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-crypto-cryptohmacsha512) | `crypto` |
| [crypto.md5](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-crypto-cryptomd5) | `crypto` |
| [crypto.sha1](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-crypto-cryptosha1) | `crypto` |
| [crypto.sha256](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-crypto-cryptosha256) | `crypto` |
- [Graphs](https://www.openpolicyagent.org/docs/latest/policy-reference/#graph)
| Builtin | Feature |
|---------------------------------------------------------------------------------------------------------------|---------|
| [graph.reachable](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-graph-graphreachable) | `graph` |
| [walk](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-graph-walk) | `graph` |
- [UUID](https://www.openpolicyagent.org/docs/latest/policy-reference/#uuid)
| Builtin | Feature |
|--------------------------------------------------------------------------------------------------------|---------|
| [uuid.parse](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-uuid-uuidparse) | `uuid` |
| [uuid.rfc4122](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-uuid-uuidrfc4122) | `uuid` |
- [Semantic Versions](https://www.openpolicyagent.org/docs/latest/policy-reference/#semver)
| Builtin | Feature |
|----------------------------------------------------------------------------------------------------------------|----------|
| [semver.compare](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-semver-semvercompare) | `semver` |
| [semver.is_valid](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-semver-semveris_valid) | `semver` |
- [OPA](https://www.openpolicyagent.org/docs/latest/policy-reference/#opa
| Builtin | Feature |
|-----------------------------------------------------------------------------------------------------|---------|
| [opa.runtime](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-opa-oparuntime) | _ |
- [Debugging](https://www.openpolicyagent.org/docs/latest/policy-reference/#opa)
| Builtin | Feature |
|---------------------------------------------------------------------------------|---------|
| [print(...)](https://www.openpolicyagent.org/docs/latest/policy-reference/#opa) | _ |
- [Tracing](https://www.openpolicyagent.org/docs/latest/policy-reference/#tracing)
| Builtin | Feature |
|----------------------------------------------------------------------------------------------|---------|
| [trace](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-tracing-trace) | _ |

BIN
docs/coverage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 KiB

View File

@@ -1,7 +1,37 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use anyhow::{bail, Result};
use anyhow::{anyhow, bail, Result};
#[allow(dead_code)]
fn read_file(path: &String) -> Result<String> {
std::fs::read_to_string(path).map_err(|_| anyhow!("could not read {path}"))
}
#[allow(unused_variables)]
fn read_value_from_yaml_file(path: &String) -> Result<regorus::Value> {
#[cfg(feature = "yaml")]
return regorus::Value::from_yaml_file(path);
#[cfg(not(feature = "yaml"))]
bail!("regorus has not been built with yaml support");
}
fn read_value_from_json_file(path: &String) -> Result<regorus::Value> {
#[cfg(feature = "std")]
return regorus::Value::from_json_file(path);
#[cfg(not(feature = "std"))]
regorus::Value::from_json_str(&read_file(path)?)
}
fn add_policy_from_file(engine: &mut regorus::Engine, path: String) -> Result<String> {
#[cfg(feature = "std")]
return engine.add_policy_from_file(path);
#[cfg(not(feature = "std"))]
engine.add_policy(path.clone(), read_file(&path)?)
}
fn rego_eval(
bundles: &[String],
@@ -10,12 +40,16 @@ fn rego_eval(
query: String,
enable_tracing: bool,
non_strict: bool,
#[cfg(feature = "coverage")] coverage: bool,
) -> Result<()> {
// Create engine.
let mut engine = regorus::Engine::new();
engine.set_strict_builtin_errors(!non_strict);
#[cfg(feature = "coverage")]
engine.set_enable_coverage(coverage);
// Load files from given bundles.
for dir in bundles.iter() {
let entries =
@@ -31,7 +65,7 @@ fn rego_eval(
_ => continue,
}
engine.add_policy_from_file(entry.path())?;
let _package = add_policy_from_file(&mut engine, entry.path().display().to_string())?;
}
}
@@ -39,15 +73,15 @@ fn rego_eval(
for file in files.iter() {
if file.ends_with(".rego") {
// Read policy file.
engine.add_policy_from_file(file)?;
let _package = add_policy_from_file(&mut engine, file.clone())?;
} else {
// Read data file.
let data = if file.ends_with(".json") {
regorus::Value::from_json_file(file)?
read_value_from_json_file(file)?
} else if file.ends_with(".yaml") {
regorus::Value::from_yaml_file(file)?
read_value_from_yaml_file(file)?
} else {
bail!("Unsupported data file `{file}`. Must be rego, json or yaml.")
bail!("Unsupported data file `{file}`. Must be rego, json or yaml.");
};
// Merge given data.
@@ -57,19 +91,33 @@ fn rego_eval(
if let Some(file) = input {
let input = if file.ends_with(".json") {
regorus::Value::from_json_file(&file)?
read_value_from_json_file(&file)?
} else if file.ends_with(".yaml") {
regorus::Value::from_yaml_file(&file)?
read_value_from_yaml_file(&file)?
} else {
bail!("Unsupported input file `{file}`. Must be json or yaml.")
};
engine.set_input(input);
}
// Evaluate query.
// Note: The `eval_query` function is used below since it produces output
// in the same format as OPA. It also allows evaluating arbitrary statements
// as queries.
//
// Most applications will want to use `eval_rule` instead.
// It is faster since it does not have to parse the query string.
// It also returns the value of the rule directly and thus is easier
// to use.
let results = engine.eval_query(query, enable_tracing)?;
println!("{}", serde_json::to_string_pretty(&results)?);
#[cfg(feature = "coverage")]
if coverage {
let report = engine.get_coverage_report()?;
println!("{}", report.to_string_pretty()?);
}
Ok(())
}
@@ -77,8 +125,12 @@ fn rego_lex(file: String, verbose: bool) -> Result<()> {
use regorus::unstable::*;
// Create source.
#[cfg(feature = "std")]
let source = Source::from_file(file)?;
#[cfg(not(feature = "std"))]
let source = Source::from_contents(file.clone(), read_file(&file)?)?;
// Create lexer.
let mut lexer = Lexer::new(&source);
@@ -104,8 +156,12 @@ fn rego_parse(file: String) -> Result<()> {
use regorus::unstable::*;
// Create source.
#[cfg(feature = "std")]
let source = Source::from_file(file)?;
#[cfg(not(feature = "std"))]
let source = Source::from_contents(file.clone(), read_file(&file)?)?;
// Create a parser and parse the source.
let mut parser = Parser::new(&source)?;
let ast = parser.parse()?;
@@ -137,9 +193,14 @@ enum RegorusCommand {
#[arg(long, short)]
trace: bool,
// Non strict execution
/// Perform non-strict evaluation. (default behavior of OPA).
#[arg(long, short)]
non_strict: bool,
/// Display coverage information
#[cfg(feature = "coverage")]
#[arg(long, short)]
coverage: bool,
},
/// Tokenize a Rego policy.
@@ -168,10 +229,6 @@ struct Cli {
fn main() -> Result<()> {
use clap::Parser;
env_logger::builder()
.format_level(false)
.format_timestamp(None)
.init();
// Parse and dispatch command.
let cli = Cli::parse();
@@ -183,7 +240,18 @@ fn main() -> Result<()> {
query,
trace,
non_strict,
} => rego_eval(&bundles, &data, input, query, trace, non_strict),
#[cfg(feature = "coverage")]
coverage,
} => rego_eval(
&bundles,
&data,
input,
query,
trace,
non_strict,
#[cfg(feature = "coverage")]
coverage,
),
RegorusCommand::Lex { file, verbose } => rego_lex(file, verbose),
RegorusCommand::Parse { file } => rego_parse(file),
}

10
release-plz.toml Normal file
View File

@@ -0,0 +1,10 @@
[workspace]
git_release_enable = false
git_tag_enable = false
changelog_update = false
[[package]]
name = "regorus"
git_tag_enable = true
git_release_enable = true
changelog_update = true

View File

@@ -1,71 +0,0 @@
#!/bin/bash
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
set -e
if ! command -v grcov > /dev/null; then
cargo install grcov
fi
if ! command -v llvm-profdata > /dev/null; then
rustup component add llvm-tools-preview
fi
#export LLVM_PROFILE_FILE='target/cargo-test-%p-%m.profraw'
#export CARGO_INCREMENTAL=1
#export RUSTFLAGS='-Cinstrument-coverage'
echo "Building with instrumentation"
cargo build --all-targets
if [ "$1" == "--no-run" ]; then
exit 0
fi
# Remove existing coverage information.
rm -f target/*.profraw
rm -rf target/coverage
mkdir -p target/coverage
echo "Running tests"
cargo test
# Generate html
grcov target/ --binary-path ./target/debug/deps -s src/ -t html \
--branch --ignore-not-existing --ignore '../*' --ignore "/*" -o target/coverage/html
if [ "$1" == "--show" ]; then
echo "Opening report in browser"
xdg-open target/coverage/html/index.html 2>/dev/null
echo "Done"
fi
# Generate markdown
grcov target/ --binary-path ./target/debug/deps -s src/ -t markdown \
--branch --ignore-not-existing --ignore '../*' --ignore "/*" -o target/coverage/markdown
cat target/coverage/markdown
# Print small-form table of files without 100% coverage.
echo "Files without 100% coverage"
while read p; do
file=$(echo "$p" | cut -f 2 -d '|')
percent=$(echo "$p" | cut -f 3 -d '|')
missing=$(echo "$p" | cut -f 5 -d '|')
if [ -z "$file" ]; then
break
fi
# Trim percentage using xargs.
case $(echo "$percent" | xargs) in
"100%"|"100.00%")
continue
esac
echo "| $file | $percent |"
done < target/coverage/markdown
#TODO: Maybe use coveralls format (json) and query data to lockdown code coverage.

View File

@@ -1,15 +0,0 @@
#!/bin/bash
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
git stash
cargo doc --no-deps
git checkout docs
rm -rf docs
cp -r target/x86_64-unknown-linux-musl/doc ./docs
echo "<meta http-equiv=\"refresh\" content=\"0; url=regorus/index.html\">" > docs/index.html
git add docs
git commit -s
git push
git checkout -
git stash pop

View File

@@ -6,7 +6,7 @@ set -eo pipefail
if [ -f Cargo.toml ]; then
# Ensure that all targets can be built.
cargo build --all-targets
cargo build -r --all-targets
#Ensure that code is correctly formatted.
cargo fmt --check || (echo "Run cargo fmt to fix formatting" && exit 1)

View File

@@ -5,16 +5,31 @@
set -eo pipefail
if [ -f Cargo.toml ]; then
# Run precommit checks
# Run precommit checks.
dir=$(dirname "${BASH_SOURCE[0]}")
"$dir/pre-commit"
# Ensure that all tests pass
# Also generate coverage information.
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
scripts/coverage
# Ensure that the public API works.
cargo test -r --doc
# Ensure that no_std build succeeds.
# Build for a target that has no std available.
if command -v rustup > /dev/null; then
rustup target add thumbv7m-none-eabi
(cd tests/ensure_no_std; cargo build -r --target thumbv7m-none-eabi)
fi
# Ensure that we can build with only std.
cargo build -r --example regorus --no-default-features --features std
# Ensure that we can build with all features.
cargo build -r --all-features
# Ensure that all tests pass.
cargo test -r
cargo test -r --test aci
cargo test -r --test kata
# Ensure that OPA conformance tests don't regress.
cargo test -r --test opa -- $(tr '\n' ' ' < tests/opa.passing)
cargo test -r --features opa-testutil,serde_json/arbitrary_precision --test opa -- $(tr '\n' ' ' < tests/opa.passing)
fi

View File

@@ -1,8 +0,0 @@
#!/bin/bash
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
set -e
yaml=$(realpath -e $1)
RUST_BACKTRACE=1 cargo test interpreter::one_yaml -- --include-ignored --nocapture "$yaml"

View File

@@ -1,8 +0,0 @@
#!/bin/bash
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
set -e
yaml=$(realpath -e $1)
RUST_BACKTRACE=1 cargo test parser::one_yaml -- --include-ignored --nocapture "$yaml"

Some files were not shown because too many files have changed in this diff Show More