mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
Compare commits
47 Commits
regorus-v0
...
verus2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5112ccf492 | ||
|
|
8f740e2f6f | ||
|
|
687be2850b | ||
|
|
95bffcb5f9 | ||
|
|
db8a9abf13 | ||
|
|
421ee6af9b | ||
|
|
64f71dee34 | ||
|
|
648ba40126 | ||
|
|
126cc12eb5 | ||
|
|
1a8fc08773 | ||
|
|
c164917d63 | ||
|
|
6a6cc659b7 | ||
|
|
a86cf1119f | ||
|
|
989ca6df2e | ||
|
|
d36f952133 | ||
|
|
35fb5d5953 | ||
|
|
296b34171a | ||
|
|
f9d54cd436 | ||
|
|
5b60daabd9 | ||
|
|
f69974dc1b | ||
|
|
942dd47163 | ||
|
|
ac701b4933 | ||
|
|
86088d2049 | ||
|
|
83891d7782 | ||
|
|
898643129e | ||
|
|
50c0215fdb | ||
|
|
ee3dff9a3d | ||
|
|
b8e15f46f3 | ||
|
|
37144968c8 | ||
|
|
7ee503ccdc | ||
|
|
006e819d52 | ||
|
|
b6f11c5602 | ||
|
|
72033e77da | ||
|
|
be34063dba | ||
|
|
04bf417c06 | ||
|
|
bc23cd08ac | ||
|
|
1c607dc1d3 | ||
|
|
47cc27ff49 | ||
|
|
8814eda0ae | ||
|
|
b4a69a13ba | ||
|
|
e83a47497a | ||
|
|
241c1d445b | ||
|
|
4054d1b6b6 | ||
|
|
8f7ca44bdf | ||
|
|
96360fa9d8 | ||
|
|
455d2aa588 | ||
|
|
0e5fe9b9ac |
92
.github/dependabot.yml
vendored
92
.github/dependabot.yml
vendored
@@ -1,3 +1,5 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
# 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:
|
||||
@@ -5,7 +7,95 @@
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
# All Rust/Cargo directories are grouped into a single entry so that
|
||||
# when a dependency is updated, Dependabot bumps it across the root
|
||||
# workspace AND every binding, preventing version skew.
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "/" # Location of package manifests
|
||||
directories:
|
||||
- "/"
|
||||
- "/bindings/ffi"
|
||||
- "/bindings/java"
|
||||
- "/bindings/python"
|
||||
- "/bindings/ruby"
|
||||
- "/bindings/wasm"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
prefix: "build(deps)"
|
||||
groups:
|
||||
# Bundle all Cargo dependency updates into a single PR. Without this,
|
||||
# dependabot creates a separate PR per directory for the same dependency,
|
||||
# and each individual PR fails to build due to version skew.
|
||||
rust-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
# Ignore vendored mimalloc crates; updates are managed manually.
|
||||
ignore:
|
||||
- dependency-name: "regorus-mimalloc"
|
||||
- dependency-name: "regorus-mimalloc-sys"
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/bindings/go"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
prefix: "build(deps)"
|
||||
groups:
|
||||
per-dependency:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "maven"
|
||||
directory: "/bindings/java"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
prefix: "build(deps)"
|
||||
groups:
|
||||
per-dependency:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "nuget"
|
||||
directory: "/bindings/csharp"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
prefix: "build(deps)"
|
||||
groups:
|
||||
per-dependency:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/bindings/python"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
prefix: "build(deps)"
|
||||
groups:
|
||||
per-dependency:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "bundler"
|
||||
directory: "/bindings/ruby"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
prefix: "build(deps)"
|
||||
groups:
|
||||
per-dependency:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
prefix: "ci(deps)"
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
22
.github/workflows/codeql.yml
vendored
22
.github/workflows/codeql.yml
vendored
@@ -1,3 +1,5 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
name: "CodeQL Security Analysis"
|
||||
|
||||
on:
|
||||
@@ -60,14 +62,14 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
# Setup language-specific dependencies BEFORE CodeQL init for proper tracing setup
|
||||
- name: Setup Rust
|
||||
uses: ./.github/actions/toolchains/rust
|
||||
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus
|
||||
|
||||
@@ -84,26 +86,26 @@ jobs:
|
||||
|
||||
- name: Setup Python
|
||||
if: matrix.language == 'python'
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Setup Java
|
||||
if: matrix.language == 'java-kotlin'
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'corretto'
|
||||
java-version: '8'
|
||||
|
||||
- name: Setup Go
|
||||
if: matrix.language == 'go'
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||
with:
|
||||
go-version: '1.21'
|
||||
|
||||
- name: Setup .NET
|
||||
if: matrix.language == 'csharp'
|
||||
uses: actions/setup-dotnet@v4
|
||||
uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0
|
||||
with:
|
||||
global-json-file: ./bindings/csharp/global.json
|
||||
|
||||
@@ -113,12 +115,12 @@ jobs:
|
||||
|
||||
- name: Setup Node.js
|
||||
if: matrix.language == 'javascript-typescript'
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version: '18'
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
@@ -139,7 +141,7 @@ jobs:
|
||||
|
||||
- name: Setup Ruby
|
||||
if: matrix.language == 'rust' && contains(matrix.working-directory, 'ruby')
|
||||
uses: ruby/setup-ruby@v1
|
||||
uses: ruby/setup-ruby@3ff19f5e2baf30647122352b96108b1fbe250c64 # v1.299.0
|
||||
with:
|
||||
ruby-version: '3.4.2'
|
||||
bundler-cache: true
|
||||
@@ -186,6 +188,6 @@ jobs:
|
||||
run: cargo xtask build-wasm --release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
129
.github/workflows/dependabot-refresh-cargo-lockfiles.yml
vendored
Normal file
129
.github/workflows/dependabot-refresh-cargo-lockfiles.yml
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
name: dependabot/refresh-cargo-lockfiles
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened]
|
||||
branches: ["main"]
|
||||
|
||||
concurrency:
|
||||
group: dependabot-refresh-cargo-lockfiles-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
refresh-cargo-lockfiles:
|
||||
permissions:
|
||||
contents: write
|
||||
if: >-
|
||||
github.event.pull_request.user.login == 'dependabot[bot]' &&
|
||||
github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
|
||||
with:
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
run: |
|
||||
rustup toolchain install 1.92.0 --profile minimal
|
||||
rustup override set 1.92.0
|
||||
cargo --version
|
||||
rustc --version
|
||||
|
||||
- name: Refresh affected Cargo lockfiles
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
base_sha="${{ github.event.pull_request.base.sha }}"
|
||||
head_sha="${{ github.event.pull_request.head.sha }}"
|
||||
|
||||
mapfile -t changed_files < <(git diff --name-only "$base_sha" "$head_sha" -- ':(glob)**/Cargo.toml' ':(glob)**/Cargo.lock')
|
||||
|
||||
if [ "${#changed_files[@]}" -eq 0 ]; then
|
||||
echo "No Cargo manifest or lockfile changes detected."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
declare -A manifests=()
|
||||
for path in "${changed_files[@]}"; do
|
||||
case "$path" in
|
||||
bindings/ffi/*)
|
||||
manifests["bindings/ffi/Cargo.toml"]=1
|
||||
;;
|
||||
bindings/java/*)
|
||||
manifests["bindings/java/Cargo.toml"]=1
|
||||
;;
|
||||
bindings/python/*)
|
||||
manifests["bindings/python/Cargo.toml"]=1
|
||||
;;
|
||||
bindings/ruby/*)
|
||||
manifests["bindings/ruby/Cargo.toml"]=1
|
||||
;;
|
||||
bindings/wasm/*)
|
||||
manifests["bindings/wasm/Cargo.toml"]=1
|
||||
;;
|
||||
*)
|
||||
manifests["Cargo.toml"]=1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
for manifest in "${!manifests[@]}"; do
|
||||
echo "Refreshing lockfile for $manifest"
|
||||
cargo metadata \
|
||||
--config 'build.rustc="rustc"' \
|
||||
--config 'build.rustc-wrapper=""' \
|
||||
--config 'build.rustc-workspace-wrapper=""' \
|
||||
--format-version 1 \
|
||||
--all-features \
|
||||
--manifest-path "$manifest" > /dev/null
|
||||
done
|
||||
|
||||
if [[ -n "${manifests[Cargo.toml]+x}" ]]; then
|
||||
echo "Refreshing lockfile for tests/ensure_no_std/Cargo.toml (thumbv7m-none-eabi)"
|
||||
cargo metadata \
|
||||
--config 'build.rustc="rustc"' \
|
||||
--config 'build.rustc-wrapper=""' \
|
||||
--config 'build.rustc-workspace-wrapper=""' \
|
||||
--format-version 1 \
|
||||
--manifest-path tests/ensure_no_std/Cargo.toml \
|
||||
--filter-platform thumbv7m-none-eabi > /dev/null
|
||||
fi
|
||||
|
||||
- name: Commit lockfile refresh
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
mapfile -t lockfiles < <(git ls-files -m -o --exclude-standard -- ':(glob)**/Cargo.lock')
|
||||
|
||||
for lockfile in "${lockfiles[@]}"; do
|
||||
git add "$lockfile"
|
||||
done
|
||||
|
||||
if git diff --cached --quiet; then
|
||||
echo "No Cargo lockfile changes required."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
auth_header=$(printf 'x-access-token:%s' "$GH_TOKEN" | base64 | tr -d '\n')
|
||||
trap 'git config --unset-all http.https://github.com/.extraheader' EXIT
|
||||
git config http.https://github.com/.extraheader "AUTHORIZATION: basic ${auth_header}"
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git commit -m "build(deps): refresh Cargo lockfiles"
|
||||
git push origin HEAD:${{ github.event.pull_request.head.ref }}
|
||||
66
.github/workflows/dependency-audit.yml
vendored
Normal file
66
.github/workflows/dependency-audit.yml
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
name: Dependency Audits
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: ["main"]
|
||||
schedule:
|
||||
- cron: "0 6 * * 1"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
cargo-audit:
|
||||
name: Cargo Audit (${{ matrix.lockfile }})
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
lockfile:
|
||||
- Cargo.lock
|
||||
- bindings/ffi/Cargo.lock
|
||||
- bindings/java/Cargo.lock
|
||||
- bindings/python/Cargo.lock
|
||||
- bindings/ruby/Cargo.lock
|
||||
- bindings/wasm/Cargo.lock
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Run cargo audit
|
||||
uses: rustsec/audit-check@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
lockfile: ${{ matrix.lockfile }}
|
||||
|
||||
cargo-deny:
|
||||
name: Cargo Deny (${{ matrix.manifest }})
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
manifest:
|
||||
- Cargo.toml
|
||||
- bindings/ffi/Cargo.toml
|
||||
- bindings/java/Cargo.toml
|
||||
- bindings/python/Cargo.toml
|
||||
- bindings/ruby/Cargo.toml
|
||||
- bindings/ruby/ext/regorusrb/Cargo.toml
|
||||
- bindings/wasm/Cargo.toml
|
||||
- tests/ensure_no_std/Cargo.toml
|
||||
- xtask/Cargo.toml
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Rust
|
||||
uses: ./.github/actions/toolchains/rust
|
||||
|
||||
- name: Run cargo deny
|
||||
uses: EmbarkStudios/cargo-deny-action@v2
|
||||
with:
|
||||
command: check
|
||||
command-arguments: advisories bans
|
||||
manifest-path: ${{ matrix.manifest }}
|
||||
82
.github/workflows/feature-matrix.yml
vendored
Normal file
82
.github/workflows/feature-matrix.yml
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
# Thorough weekly test of non-default feature combinations.
|
||||
# Catches regressions from dependency updates and feature-gating issues
|
||||
# that the fast PR CI checks (cargo check only) would miss at runtime.
|
||||
name: tests/feature-matrix
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# Run at 3:42 AM UTC every Saturday.
|
||||
- cron: "42 3 * * 6"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
feature-matrix:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# Bare minimum: validates that the core interpreter works
|
||||
# without any builtins or optional subsystems.
|
||||
- name: minimal (std + arc)
|
||||
features: std,arc
|
||||
|
||||
# Common library usage pattern (issue #595): consumer enables
|
||||
# std + arc + rvm and relies on indexmap/std propagation.
|
||||
- name: library (std + arc + rvm)
|
||||
features: std,arc,rvm
|
||||
|
||||
# New default after removing mimalloc from full-opa.
|
||||
# Ensures all builtins compile without the allocator.
|
||||
- name: full-opa (no mimalloc)
|
||||
features: std,arc,full-opa
|
||||
|
||||
# Binding-style usage: full-opa with the vendored allocator.
|
||||
# Mirrors how ffi/java/python/ruby bindings are built.
|
||||
- name: full-opa + allocator
|
||||
features: std,arc,full-opa,allocator-memory-limits
|
||||
|
||||
# Selective builtins without full-opa: validates that popular
|
||||
# features can be cherry-picked independently.
|
||||
- name: cherry-picked builtins
|
||||
features: std,arc,rvm,regex,time,semver,cache
|
||||
|
||||
# Observability features only: coverage + cache without the
|
||||
# heavier builtins (regex, time, etc.).
|
||||
- name: observability
|
||||
features: std,arc,rvm,coverage,cache
|
||||
|
||||
# Azure Policy adds jsonschema + dashmap; test it compiles
|
||||
# and runs on top of full-opa.
|
||||
- name: azure-policy
|
||||
features: std,arc,full-opa,azure_policy
|
||||
|
||||
# Azure RBAC adds regex + time + net on top of full-opa.
|
||||
- name: azure-rbac
|
||||
features: std,arc,full-opa,azure-rbac
|
||||
|
||||
# no_std with the OPA-compatible feature set: exercises the
|
||||
# spin_no_std codepath and absence of std-only dependencies.
|
||||
- name: no_std
|
||||
features: arc,opa-no-std
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Rust toolchain
|
||||
uses: ./.github/actions/toolchains/rust
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus-features
|
||||
- name: Fetch dependencies
|
||||
run: cargo fetch --locked
|
||||
- name: Build
|
||||
run: cargo build --no-default-features --features "${{ matrix.features }}" --frozen
|
||||
- name: Test
|
||||
run: cargo test --no-default-features --features "${{ matrix.features }}" --frozen
|
||||
29
.github/workflows/miri.yml
vendored
Normal file
29
.github/workflows/miri.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: miri
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# Run at 6:30 AM UTC every Wednesday
|
||||
- cron: "30 6 * * 3"
|
||||
|
||||
jobs:
|
||||
miri-test:
|
||||
name: miri (nightly)
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
MIRIFLAGS: "-Zmiri-disable-isolation"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
- uses: ./.github/actions/toolchains/rust
|
||||
with:
|
||||
toolchain: nightly
|
||||
components: miri rust-src
|
||||
- name: Set up Miri
|
||||
run: cargo miri setup
|
||||
- name: Run Miri tests
|
||||
run: cargo miri test -p regorus
|
||||
- name: Run Miri ACI tests
|
||||
run: cargo miri test -p regorus --test aci
|
||||
- name: Run Miri kata tests
|
||||
run: cargo miri test -p regorus --test kata
|
||||
6
.github/workflows/pr-extensions.yml
vendored
6
.github/workflows/pr-extensions.yml
vendored
@@ -1,3 +1,5 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
name: tests/release-extensions
|
||||
|
||||
on:
|
||||
@@ -18,11 +20,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Rust toolchain
|
||||
uses: ./.github/actions/toolchains/rust
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus
|
||||
- name: Fetch dependencies
|
||||
|
||||
6
.github/workflows/pr.yml
vendored
6
.github/workflows/pr.yml
vendored
@@ -1,3 +1,5 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
name: tests/release
|
||||
|
||||
on:
|
||||
@@ -18,11 +20,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Rust toolchain
|
||||
uses: ./.github/actions/toolchains/rust
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus
|
||||
- name: Fetch dependencies
|
||||
|
||||
21
.github/workflows/publish-java.yml
vendored
21
.github/workflows/publish-java.yml
vendored
@@ -1,6 +1,9 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
name: publish-java
|
||||
|
||||
on: workflow_dispatch
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -32,10 +35,10 @@ jobs:
|
||||
os: windows-latest
|
||||
extension: dll
|
||||
steps:
|
||||
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
|
||||
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
java-version: 8
|
||||
distribution: "corretto"
|
||||
@@ -43,7 +46,7 @@ jobs:
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
- if: ${{ matrix.build_cmd == 'zigbuild' }}
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: "3.11"
|
||||
- if: ${{ matrix.build_cmd == 'zigbuild' }}
|
||||
@@ -53,7 +56,7 @@ jobs:
|
||||
- run: cargo ${{ matrix.build_cmd || 'build' }} --release --frozen --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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: native-libraries-${{ matrix.target }}
|
||||
path: native/
|
||||
@@ -63,24 +66,24 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
|
||||
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
java-version: 8
|
||||
distribution: "corretto"
|
||||
server-id: ossrh
|
||||
server-username: MAVEN_USERNAME
|
||||
server-password: MAVEN_PASSWORD
|
||||
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
pattern: native-libraries-*
|
||||
merge-multiple: true
|
||||
path: ./bindings/java/native/
|
||||
- run: mvn package
|
||||
working-directory: ./bindings/java
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: built-jars
|
||||
path: ./bindings/java/target/regorus-java-*.jar
|
||||
|
||||
30
.github/workflows/publish-python.yml
vendored
30
.github/workflows/publish-python.yml
vendored
@@ -1,3 +1,5 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
# This file is autogenerated by maturin v1.4.0
|
||||
# To update, run
|
||||
#
|
||||
@@ -18,8 +20,8 @@ jobs:
|
||||
matrix:
|
||||
target: [x86_64, x86, aarch64, armv7, s390x, ppc64le]
|
||||
steps:
|
||||
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- uses: ./.github/actions/toolchains/rust
|
||||
@@ -32,14 +34,14 @@ jobs:
|
||||
working-directory: bindings/python
|
||||
|
||||
- name: Build wheels
|
||||
uses: PyO3/maturin-action@63b75c597b83e247fbf4fb7719801cc4220ae9f3 # v1.43.0
|
||||
uses: PyO3/maturin-action@04ac600d27cdf7a9a280dadf7147097c42b757ad # v1.43.0
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
args: --release --out dist --manifest-path bindings/python/Cargo.toml --offline --strip
|
||||
sccache: 'true'
|
||||
manylinux: auto
|
||||
- name: Upload wheels
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: wheels-linux-${{ matrix.target }}
|
||||
path: dist
|
||||
@@ -50,8 +52,8 @@ jobs:
|
||||
matrix:
|
||||
target: [x64, x86]
|
||||
steps:
|
||||
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: '3.10'
|
||||
architecture: ${{ matrix.target }}
|
||||
@@ -65,13 +67,13 @@ jobs:
|
||||
working-directory: bindings/python
|
||||
|
||||
- name: Build wheels
|
||||
uses: PyO3/maturin-action@63b75c597b83e247fbf4fb7719801cc4220ae9f3 # v1.43.0
|
||||
uses: PyO3/maturin-action@04ac600d27cdf7a9a280dadf7147097c42b757ad # v1.43.0
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
args: --release --out dist --manifest-path bindings/python/Cargo.toml --frozen --strip
|
||||
sccache: 'true'
|
||||
- name: Upload wheels
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: wheels-windows-${{ matrix.target }}
|
||||
path: dist
|
||||
@@ -82,8 +84,8 @@ jobs:
|
||||
matrix:
|
||||
target: [x86_64, aarch64, universal2-apple-darwin]
|
||||
steps:
|
||||
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- uses: ./.github/actions/toolchains/rust
|
||||
@@ -96,13 +98,13 @@ jobs:
|
||||
working-directory: bindings/python
|
||||
|
||||
- name: Build wheels
|
||||
uses: PyO3/maturin-action@63b75c597b83e247fbf4fb7719801cc4220ae9f3 # v1.43.0
|
||||
uses: PyO3/maturin-action@04ac600d27cdf7a9a280dadf7147097c42b757ad # v1.43.0
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
args: --release --out dist --manifest-path bindings/python/Cargo.toml --offline --strip
|
||||
sccache: 'true'
|
||||
- name: Upload wheels
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: wheels-macos-${{ matrix.host.target }}
|
||||
path: dist
|
||||
@@ -114,13 +116,13 @@ jobs:
|
||||
# if: "startsWith(github.ref, 'refs/tags/')"
|
||||
needs: [linux, windows, macos]
|
||||
steps:
|
||||
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
pattern: wheels-*
|
||||
merge-multiple: true
|
||||
path: wheels
|
||||
- name: Publish to PyPI
|
||||
uses: PyO3/maturin-action@63b75c597b83e247fbf4fb7719801cc4220ae9f3 # v1.43.0
|
||||
uses: PyO3/maturin-action@04ac600d27cdf7a9a280dadf7147097c42b757ad # v1.43.0
|
||||
env:
|
||||
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
||||
with:
|
||||
|
||||
9
.github/workflows/publish-wasm.yml
vendored
9
.github/workflows/publish-wasm.yml
vendored
@@ -1,10 +1,13 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
name: publish-wasm
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
|
||||
on: workflow_dispatch
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish-wasm:
|
||||
@@ -12,11 +15,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# Setup .npmrc file to publish to npm
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version: '20.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
9
.github/workflows/release-plz.yml
vendored
9
.github/workflows/release-plz.yml
vendored
@@ -1,10 +1,13 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
name: Release-plz
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
|
||||
on: workflow_dispatch
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release-plz:
|
||||
@@ -14,13 +17,13 @@ jobs:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install Rust toolchain
|
||||
uses: ./.github/actions/toolchains/rust
|
||||
- name: Run release-plz
|
||||
uses: MarcoIeni/release-plz-action@8724d33cd97b8295051102e2e19ca592962238f5 #v0.5.108
|
||||
uses: MarcoIeni/release-plz-action@1528104d2ca23787631a1c1f022abb64b34c1e11 # v0.5.128
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||
|
||||
10
.github/workflows/rust-clippy.yml
vendored
10
.github/workflows/rust-clippy.yml
vendored
@@ -1,3 +1,5 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
# 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
|
||||
@@ -30,12 +32,12 @@ jobs:
|
||||
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@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: ./.github/actions/toolchains/rust
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus
|
||||
|
||||
@@ -47,10 +49,10 @@ jobs:
|
||||
|
||||
- name: Run rust-clippy
|
||||
run: cargo xtask clippy --sarif rust-clippy-results.sarif
|
||||
continue-on-error: true
|
||||
|
||||
- name: Upload analysis results to GitHub
|
||||
uses: github/codeql-action/upload-sarif@c298edae2d512d807fe4bdc57c0ac5a036f61501 # v3.29.11
|
||||
if: ${{ hashFiles('rust-clippy-results.sarif') != '' }}
|
||||
uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.11
|
||||
with:
|
||||
sarif_file: rust-clippy-results.sarif
|
||||
wait-for-processing: true
|
||||
|
||||
6
.github/workflows/test-c-cpp.yml
vendored
6
.github/workflows/test-c-cpp.yml
vendored
@@ -1,3 +1,5 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
name: bindings/c-cpp
|
||||
|
||||
on:
|
||||
@@ -14,13 +16,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: ./.github/actions/toolchains/rust
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus
|
||||
- name: Fetch dependencies
|
||||
|
||||
47
.github/workflows/test-csharp.yml
vendored
47
.github/workflows/test-csharp.yml
vendored
@@ -1,3 +1,5 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
name: bindings/csharp
|
||||
|
||||
on:
|
||||
@@ -31,19 +33,20 @@ jobs:
|
||||
target: x86_64-unknown-linux-gnu
|
||||
libpath: |
|
||||
**/release/libregorus_ffi.so
|
||||
# Disabled for now
|
||||
#- os: macos-latest
|
||||
# target: aarch64-apple-darwin
|
||||
# libpath: |
|
||||
# **/release/libregorus_ffi.dylib
|
||||
- os: macos-latest
|
||||
target: aarch64-apple-darwin
|
||||
libpath: |
|
||||
**/release/libregorus_ffi.dylib
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/toolchains/rust
|
||||
with:
|
||||
targets: ${{ matrix.runtime.target }}
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus
|
||||
- name: Fetch dependencies
|
||||
@@ -56,7 +59,7 @@ jobs:
|
||||
run: cargo xtask build-ffi --release --target ${{ matrix.runtime.target }}
|
||||
|
||||
- name: Upload regorus ffi shared library
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: regorus-ffi-artifacts-${{ matrix.runtime.target }}
|
||||
# Note: The full path of each artifact relative to . is preserved.
|
||||
@@ -70,18 +73,18 @@ jobs:
|
||||
needs: build-ffi
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/toolchains/rust
|
||||
- uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 # v4.1.0
|
||||
- uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0
|
||||
with:
|
||||
global-json-file: ./bindings/csharp/global.json
|
||||
|
||||
- run: echo '${{ steps.stepid.outputs.dotnet-version }}'
|
||||
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus
|
||||
|
||||
@@ -89,7 +92,7 @@ jobs:
|
||||
run: cargo fetch --locked
|
||||
|
||||
- name: Download regorus ffi shared libraries
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
pattern: regorus-ffi-artifacts-*
|
||||
merge-multiple: true
|
||||
@@ -99,13 +102,15 @@ jobs:
|
||||
run: ls -R ./bindings/csharp/Regorus/tmp
|
||||
|
||||
- name: Build Regorus nuget via xtask
|
||||
run: cargo xtask build-csharp --release --clean --artifacts-dir ./bindings/csharp/Regorus/tmp/bindings/ffi/target --enforce-artifacts
|
||||
run: cargo xtask build-csharp --release --clean --artifacts-dir ./bindings/csharp/Regorus/tmp/bindings/ffi/target --enforce-artifacts --repository-commit ${{ github.sha }} --include-symbols
|
||||
|
||||
- name: Upload Regorus nuget
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: regorus-nuget
|
||||
path: bindings/csharp/Regorus/bin/Release/Regorus*.nupkg
|
||||
path: |
|
||||
bindings/csharp/Regorus/bin/Release/Regorus*.nupkg
|
||||
bindings/csharp/Regorus/bin/Release/Regorus*.snupkg
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
@@ -122,24 +127,24 @@ jobs:
|
||||
target: x86_64-pc-windows-msvc
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
#- os: macos-latest
|
||||
# target: aarch64-apple-darwin
|
||||
- os: macos-latest
|
||||
target: aarch64-apple-darwin
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: ./.github/actions/toolchains/rust
|
||||
|
||||
- uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 # v4.1.0
|
||||
- uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0
|
||||
with:
|
||||
global-json-file: ./bindings/csharp/global.json
|
||||
|
||||
- run: echo '${{ steps.stepid.outputs.dotnet-version }}'
|
||||
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus
|
||||
|
||||
@@ -147,7 +152,7 @@ jobs:
|
||||
run: cargo fetch --locked
|
||||
|
||||
- name: Download regorus nuget
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: regorus-nuget
|
||||
path: ./bindings/csharp/Regorus/bin/Release
|
||||
|
||||
6
.github/workflows/test-ffi.yml
vendored
6
.github/workflows/test-ffi.yml
vendored
@@ -1,3 +1,5 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
name: bindings/ffi
|
||||
|
||||
on:
|
||||
@@ -14,12 +16,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/toolchains/rust
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus
|
||||
- name: Fetch dependencies
|
||||
|
||||
8
.github/workflows/test-go.yml
vendored
8
.github/workflows/test-go.yml
vendored
@@ -1,3 +1,5 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
name: bindings/go
|
||||
|
||||
on:
|
||||
@@ -14,12 +16,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/toolchains/rust
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus
|
||||
- name: Fetch dependencies
|
||||
@@ -28,7 +30,7 @@ jobs:
|
||||
- name: Fetch FFI crate dependencies
|
||||
run: cargo fetch --locked --manifest-path bindings/ffi/Cargo.toml
|
||||
|
||||
- uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
|
||||
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||
with:
|
||||
architecture: x64
|
||||
|
||||
|
||||
8
.github/workflows/test-java.yml
vendored
8
.github/workflows/test-java.yml
vendored
@@ -1,3 +1,5 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
name: bindings/java
|
||||
|
||||
on:
|
||||
@@ -14,17 +16,17 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
|
||||
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
java-version: 8
|
||||
distribution: "corretto"
|
||||
- uses: ./.github/actions/toolchains/rust
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus
|
||||
- name: Fetch dependencies
|
||||
|
||||
6
.github/workflows/test-musl.yml
vendored
6
.github/workflows/test-musl.yml
vendored
@@ -1,3 +1,5 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
name: musl
|
||||
|
||||
on:
|
||||
@@ -18,12 +20,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: ./.github/actions/toolchains/rust
|
||||
with:
|
||||
targets: x86_64-unknown-linux-musl
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus
|
||||
- name: Fetch dependencies
|
||||
|
||||
6
.github/workflows/test-no-std.yml
vendored
6
.github/workflows/test-no-std.yml
vendored
@@ -1,3 +1,5 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
name: bindings/no-std
|
||||
|
||||
on:
|
||||
@@ -18,12 +20,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: ./.github/actions/toolchains/rust
|
||||
with:
|
||||
targets: thumbv7m-none-eabi
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus
|
||||
- name: Fetch dependencies
|
||||
|
||||
16
.github/workflows/test-python.yml
vendored
16
.github/workflows/test-python.yml
vendored
@@ -1,3 +1,5 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
name: bindings/python
|
||||
|
||||
on:
|
||||
@@ -21,14 +23,14 @@ jobs:
|
||||
runs-on: ${{ matrix.host.name }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/toolchains/rust
|
||||
with:
|
||||
targets: ${{ matrix.host.target }}
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus
|
||||
- name: Fetch dependencies
|
||||
@@ -37,7 +39,7 @@ jobs:
|
||||
- name: Fetch Python crate dependencies
|
||||
run: cargo fetch --locked --manifest-path bindings/python/Cargo.toml --target ${{ matrix.host.target }}
|
||||
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: "3.10"
|
||||
architecture: x64
|
||||
@@ -49,7 +51,7 @@ jobs:
|
||||
run: cargo xtask build-python --release --target ${{ matrix.host.target }} --target-dir bindings/python/dist --frozen
|
||||
|
||||
- name: Upload wheel artefacts
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: regorus-wheel-${{ matrix.host.name }}
|
||||
path: bindings/python/dist/regorus-*.whl
|
||||
@@ -63,12 +65,12 @@ jobs:
|
||||
runs-on: ${{ matrix.host }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/toolchains/rust
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus
|
||||
- name: Fetch dependencies
|
||||
@@ -77,7 +79,7 @@ jobs:
|
||||
- name: Fetch Python crate dependencies
|
||||
run: cargo fetch --locked --manifest-path bindings/python/Cargo.toml
|
||||
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
architecture: x64
|
||||
|
||||
8
.github/workflows/test-ruby.yml
vendored
8
.github/workflows/test-ruby.yml
vendored
@@ -1,3 +1,5 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
name: bindings/ruby
|
||||
|
||||
on:
|
||||
@@ -12,12 +14,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Ruby and Rust
|
||||
uses: oxidize-rb/actions/setup-ruby-and-rust@7ca44a16e287e5ff7dd72ab53f4bd41cbf34a571 #v1.26
|
||||
uses: oxidize-rb/actions/setup-ruby-and-rust@e5f9a49a7812a078584072f6e3f657ad247c8771 # v1.26
|
||||
with:
|
||||
bundler: 2.6.5
|
||||
rubygems: 3.6.5
|
||||
@@ -28,7 +30,7 @@ jobs:
|
||||
working-directory: "bindings/ruby"
|
||||
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus
|
||||
|
||||
|
||||
8
.github/workflows/test-wasm.yml
vendored
8
.github/workflows/test-wasm.yml
vendored
@@ -1,3 +1,5 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
name: bindings/wasm
|
||||
|
||||
on:
|
||||
@@ -14,14 +16,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: ./.github/actions/toolchains/rust
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus
|
||||
- name: Fetch dependencies
|
||||
@@ -31,7 +33,7 @@ jobs:
|
||||
run: cargo fetch --locked --manifest-path bindings/wasm/Cargo.toml
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
|
||||
6
.github/workflows/tests-debug.yml
vendored
6
.github/workflows/tests-debug.yml
vendored
@@ -1,3 +1,5 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
name: tests/debug
|
||||
|
||||
on:
|
||||
@@ -18,11 +20,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Rust toolchain
|
||||
uses: ./.github/actions/toolchains/rust
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
shared-key: ${{ runner.os }}-regorus
|
||||
- name: Fetch dependencies
|
||||
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -25,6 +25,12 @@ bindings/ffi/regorus.ffi.hpp
|
||||
|
||||
bindings/*/target
|
||||
|
||||
# Temporary commit message files
|
||||
.commit-msg.txt
|
||||
|
||||
# Local planning docs
|
||||
docs/plans/
|
||||
|
||||
# C# build folders
|
||||
**bin
|
||||
**obj
|
||||
@@ -48,3 +54,6 @@ bindings/ruby/bin/
|
||||
bindings/java/.classpath
|
||||
bindings/java/.project
|
||||
bindings/java/.settings/
|
||||
|
||||
# Emacs temporary files
|
||||
*~
|
||||
|
||||
17
CHANGELOG.md
17
CHANGELOG.md
@@ -6,6 +6,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Azure RBAC condition interpreter with builtin evaluation coverage and YAML test suite, including quantifier (ForAnyOfAnyValues/ForAllOfAllValues), datetime (DateTimeEquals), IP (IpInRange), GUID (GuidEquals), list (ListContains), and string (StringEquals) semantics.
|
||||
- FFI surface for Azure RBAC condition evaluation (see bindings changelog for language-specific wrappers).
|
||||
|
||||
### Changed
|
||||
- [**breaking**] Switch RVM binary serialization to postcard, bump the format to v4, and mark v1-3 loads as partial (recompile required).
|
||||
|
||||
## [0.9.1](https://github.com/microsoft/regorus/compare/regorus-v0.9.0...regorus-v0.9.1) - 2026-02-06
|
||||
|
||||
### Fixed
|
||||
- Release native C# handles reliably to avoid memory growth ([#571](https://github.com/microsoft/regorus/pull/571)).
|
||||
- Centralize C# handle gating with a short dispose wait and deferred release to avoid leaks while blocking new calls ([#571](https://github.com/microsoft/regorus/pull/571)).
|
||||
|
||||
### Added
|
||||
- Manual C# memory growth tests for both `using` and finalizer paths ([#571](https://github.com/microsoft/regorus/pull/571)).
|
||||
- C# test runner options for filtered tests, console logging, and skipping sample apps ([#571](https://github.com/microsoft/regorus/pull/571)).
|
||||
|
||||
## [0.5.0](https://github.com/microsoft/regorus/compare/regorus-v0.4.0...regorus-v0.5.0) - 2025-07-08
|
||||
|
||||
### Added
|
||||
|
||||
796
Cargo.lock
generated
796
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
59
Cargo.toml
59
Cargo.toml
@@ -8,12 +8,17 @@ members = [
|
||||
[package]
|
||||
name = "regorus"
|
||||
description = "A fast, lightweight Rego (OPA policy language) interpreter"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
edition = "2021"
|
||||
license = "MIT AND Apache-2.0 AND BSD-3-Clause"
|
||||
repository = "https://github.com/microsoft/regorus"
|
||||
keywords = ["interpreter", "no_std", "opa", "policy-as-code", "rego"]
|
||||
|
||||
# Enable verification with Verus
|
||||
|
||||
[package.metadata.verus]
|
||||
verify = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
@@ -24,8 +29,8 @@ default = ["full-opa", "arc", "rvm"]
|
||||
|
||||
arc = []
|
||||
ast = []
|
||||
azure_policy = ["dep:jsonschema", "arc", "dashmap"]
|
||||
azure-rbac = []
|
||||
azure_policy = ["dep:jsonschema", "dep:chrono", "dep:ipnet", "dep:icu_casemap", "dep:hashbrown", "arc", "dashmap"]
|
||||
azure-rbac = ["regex", "time", "net"]
|
||||
base64 = ["dep:data-encoding"]
|
||||
base64url = ["dep:data-encoding"]
|
||||
coverage = []
|
||||
@@ -39,10 +44,11 @@ net = ["dep:ipnet"]
|
||||
no_std = ["lazy_static/spin_no_std"]
|
||||
opa-runtime = []
|
||||
regex = ["dep:regex"]
|
||||
rvm = ["dep:bincode", "dep:indexmap"]
|
||||
cache = ["dep:lru"]
|
||||
rvm = ["dep:postcard", "dep:indexmap"]
|
||||
semver = ["dep:semver"]
|
||||
allocator-memory-limits = ["std", "mimalloc", "mimalloc/allocator-memory-limits"]
|
||||
std = ["rand/std", "rand/std_rng", "serde_json/std", "msvc_spectre_libs" ]
|
||||
std = ["rand/std", "rand/std_rng", "serde_json/std", "indexmap?/std", "msvc_spectre_libs", "dep:parking_lot" ]
|
||||
time = ["dep:chrono", "dep:chrono-tz"]
|
||||
uuid = ["dep:uuid"]
|
||||
urlquery = ["dep:url"]
|
||||
@@ -56,11 +62,10 @@ full-opa = [
|
||||
"hex",
|
||||
"http",
|
||||
"jsonschema",
|
||||
"allocator-memory-limits",
|
||||
"mimalloc",
|
||||
"net",
|
||||
"opa-runtime",
|
||||
"regex",
|
||||
"cache",
|
||||
"semver",
|
||||
"std",
|
||||
"time",
|
||||
@@ -96,42 +101,49 @@ opa-testutil = []
|
||||
rand = ["dep:rand"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = { version = "1.0.45", default-features = false }
|
||||
anyhow = { version = "1.0.102", default-features = false }
|
||||
serde = {version = "1.0.150", default-features = false, features = ["derive", "rc", "alloc"] }
|
||||
serde_json = { version = "1.0.89", default-features = false, features = ["alloc"] }
|
||||
hashbrown = { version = "0.16", default-features = false, features = ["default-hasher"], optional = true }
|
||||
lazy_static = { version = "1.4.0", default-features = false }
|
||||
thiserror = { version = "2.0", default-features = false }
|
||||
|
||||
data-encoding = { version = "2.8.0", optional = true, default-features=false, features = ["alloc"] }
|
||||
num-bigint = { version = "0.4", default-features = false }
|
||||
num-traits = { version = "0.2", default-features = false }
|
||||
spin = { version = "0.9.8", default-features = false, features = ["mutex", "spin_mutex"] }
|
||||
parking_lot = { version = "0.12", optional = true }
|
||||
spin = { version = "0.10.0", default-features = false, features = ["mutex", "spin_mutex"] }
|
||||
|
||||
globset = { version = "0.4.16", features = ["simd-accel"], default-features = false, optional = true }
|
||||
regex = {version = "1.11.1", optional = true, default-features = false }
|
||||
regex = {version = "1.12.3", optional = true, default-features = false }
|
||||
semver = {version = "1.0.25", optional = true, default-features = false }
|
||||
url = { version = "2.5.4", optional = true }
|
||||
uuid = { version = "1.15.1", default-features = false, features = ["v4", "fast-rng"], optional = true }
|
||||
jsonschema = { version = "0.30.0", default-features = false, optional = true }
|
||||
chrono = { version = "0.4.40", optional = true }
|
||||
uuid = { version = "1.22.0", default-features = false, features = ["v4", "fast-rng"], optional = true }
|
||||
jsonschema = { version = "0.45.0", default-features = false, optional = true }
|
||||
chrono = { version = "0.4.44", optional = true }
|
||||
chrono-tz = { version = "0.10.1", optional = true }
|
||||
ipnet = { version = "2.11.0", optional = true, default-features = false }
|
||||
ipnet = { version = "2.12.0", optional = true, default-features = false }
|
||||
icu_casemap = { version = "2.1", optional = true, default-features = false, features = ["compiled_data"] }
|
||||
|
||||
serde_yaml = {version = "0.9.16", default-features = false, optional = true }
|
||||
# Specify thread_rng for in order to use random_range
|
||||
rand = { version = "0.9.0", default-features = false, features = ["thread_rng"], optional = true }
|
||||
rand = { version = "0.10.0", default-features = false, features = ["thread_rng"], optional = true }
|
||||
|
||||
# Causes the project to link with the Spectre-mitigated CRT and libs.
|
||||
msvc_spectre_libs = { version = "0.1", features = ["error"], optional = true }
|
||||
dashmap = { version = "6.1", default-features = false, optional = true }
|
||||
lru = { version = "0.16", default-features = false, optional = true }
|
||||
mimalloc = { package = "regorus-mimalloc", path = "mimalloc", version = "2.2.6", optional = true }
|
||||
|
||||
# rvm related deps
|
||||
indexmap = { version = "2.12.1", default-features = false, features = ["serde"], optional = true }
|
||||
bincode = { version = "2.0.1", default-features = false, features = ["alloc", "serde"], optional = true }
|
||||
postcard = { version = "1.1.3", default-features = false, features = ["alloc"], optional = true }
|
||||
|
||||
# Use Verus for verification
|
||||
vstd = { version = "0.0.0-2026-03-17-2326" }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.45"
|
||||
anyhow = "1.0.102"
|
||||
cfg-if = "1.0.0"
|
||||
clap = { version = "4.5.53", features = ["derive"] }
|
||||
prettydiff = { version = "0.9.0", default-features = false }
|
||||
@@ -189,6 +201,16 @@ harness = false
|
||||
name = "aci_benchmark"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "rvm_benchmark"
|
||||
harness = false
|
||||
required-features = ["rvm"]
|
||||
|
||||
[[bench]]
|
||||
name = "normalization_benchmark"
|
||||
harness = false
|
||||
required-features = ["azure_policy"]
|
||||
|
||||
[[example]]
|
||||
name="regorus"
|
||||
harness=false
|
||||
@@ -200,3 +222,6 @@ doctest=false
|
||||
# RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features --no-deps
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(verus_keep_ghost)'] }
|
||||
|
||||
560
benches/normalization_benchmark.rs
Normal file
560
benches/normalization_benchmark.rs
Normal file
@@ -0,0 +1,560 @@
|
||||
use std::hint::black_box;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||
use regorus::languages::azure_policy::aliases::{denormalizer, normalizer, AliasRegistry};
|
||||
use regorus::Value;
|
||||
use serde_json::json;
|
||||
|
||||
// ─── Alias catalog (reused across benchmarks) ───────────────────────────────
|
||||
|
||||
const ALIASES_JSON: &str = r#"[
|
||||
{
|
||||
"namespace": "Microsoft.Network",
|
||||
"resourceTypes": [
|
||||
{
|
||||
"resourceType": "networkSecurityGroups",
|
||||
"aliases": [
|
||||
{
|
||||
"name": "Microsoft.Network/networkSecurityGroups/securityRules[*].protocol",
|
||||
"defaultPath": "properties.securityRules[*].properties.protocol",
|
||||
"paths": []
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Network/networkSecurityGroups/securityRules[*].access",
|
||||
"defaultPath": "properties.securityRules[*].properties.access",
|
||||
"paths": []
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Network/networkSecurityGroups/securityRules[*].priority",
|
||||
"defaultPath": "properties.securityRules[*].properties.priority",
|
||||
"paths": []
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Network/networkSecurityGroups/securityRules[*].direction",
|
||||
"defaultPath": "properties.securityRules[*].properties.direction",
|
||||
"paths": []
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Network/networkSecurityGroups/securityRules[*].sourceAddressPrefix",
|
||||
"defaultPath": "properties.securityRules[*].properties.sourceAddressPrefix",
|
||||
"paths": []
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Network/networkSecurityGroups/securityRules[*].destinationPortRange",
|
||||
"defaultPath": "properties.securityRules[*].properties.destinationPortRange",
|
||||
"paths": []
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Network/networkSecurityGroups/securityRules[*].name",
|
||||
"defaultPath": "properties.securityRules[*].name",
|
||||
"paths": []
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Network/networkSecurityGroups/defaultSecurityRules[*].protocol",
|
||||
"defaultPath": "properties.defaultSecurityRules[*].properties.protocol",
|
||||
"paths": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"namespace": "Microsoft.Storage",
|
||||
"resourceTypes": [
|
||||
{
|
||||
"resourceType": "storageAccounts",
|
||||
"aliases": [
|
||||
{
|
||||
"name": "Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly",
|
||||
"defaultPath": "properties.supportsHttpsTrafficOnly",
|
||||
"paths": []
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Storage/storageAccounts/accessTier",
|
||||
"defaultPath": "properties.accessTier",
|
||||
"paths": []
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Storage/storageAccounts/isHnsEnabled",
|
||||
"defaultPath": "properties.isHnsEnabled",
|
||||
"paths": []
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Storage/storageAccounts/minimumTlsVersion",
|
||||
"defaultPath": "properties.minimumTlsVersion",
|
||||
"paths": []
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Storage/storageAccounts/allowBlobPublicAccess",
|
||||
"defaultPath": "properties.allowBlobPublicAccess",
|
||||
"paths": []
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Storage/storageAccounts/sku.name",
|
||||
"defaultPath": "sku.name",
|
||||
"paths": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]"#;
|
||||
|
||||
fn build_registry() -> AliasRegistry {
|
||||
let mut reg = AliasRegistry::new();
|
||||
reg.load_from_json(ALIASES_JSON).unwrap();
|
||||
reg
|
||||
}
|
||||
|
||||
/// Convert a serde_json::Value to regorus::Value.
|
||||
fn to_regorus(v: serde_json::Value) -> Value {
|
||||
Value::from(v)
|
||||
}
|
||||
|
||||
// ─── Input resources ────────────────────────────────────────────────────────
|
||||
|
||||
fn simple_storage_resource() -> Value {
|
||||
to_regorus(json!({
|
||||
"name": "myStorageAccount",
|
||||
"type": "Microsoft.Storage/storageAccounts",
|
||||
"location": "westus2",
|
||||
"kind": "StorageV2",
|
||||
"sku": { "name": "Standard_LRS", "tier": "Standard" },
|
||||
"tags": { "environment": "production", "team": "platform" },
|
||||
"properties": {
|
||||
"supportsHttpsTrafficOnly": true,
|
||||
"accessTier": "Hot",
|
||||
"isHnsEnabled": false,
|
||||
"minimumTlsVersion": "TLS1_2",
|
||||
"allowBlobPublicAccess": false
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
fn nsg_resource(rule_count: usize) -> Value {
|
||||
let rules: Vec<serde_json::Value> = (0..rule_count)
|
||||
.map(|i| {
|
||||
json!({
|
||||
"name": format!("rule-{}", i),
|
||||
"properties": {
|
||||
"protocol": "Tcp",
|
||||
"access": if i % 2 == 0 { "Allow" } else { "Deny" },
|
||||
"priority": 100 + i,
|
||||
"direction": "Inbound",
|
||||
"sourceAddressPrefix": format!("10.0.{}.0/24", i % 256),
|
||||
"destinationPortRange": format!("{}", 80 + i)
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
to_regorus(json!({
|
||||
"name": "myNsg",
|
||||
"type": "Microsoft.Network/networkSecurityGroups",
|
||||
"location": "eastus",
|
||||
"properties": {
|
||||
"securityRules": rules
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
// ─── Benchmarks ─────────────────────────────────────────────────────────────
|
||||
|
||||
fn bench_normalize_simple(c: &mut Criterion) {
|
||||
let registry = build_registry();
|
||||
let resource = simple_storage_resource();
|
||||
|
||||
c.bench_function("normalize/simple_storage", |b| {
|
||||
b.iter(|| normalizer::normalize(black_box(&resource), Some(®istry), None))
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_normalize_no_aliases(c: &mut Criterion) {
|
||||
let resource = simple_storage_resource();
|
||||
|
||||
c.bench_function("normalize/simple_no_aliases", |b| {
|
||||
b.iter(|| normalizer::normalize(black_box(&resource), None, None))
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_normalize_nsg_scaling(c: &mut Criterion) {
|
||||
let registry = build_registry();
|
||||
let mut group = c.benchmark_group("normalize/nsg_rules");
|
||||
|
||||
for rule_count in [5, 20, 100] {
|
||||
let resource = nsg_resource(rule_count);
|
||||
group.bench_with_input(
|
||||
BenchmarkId::from_parameter(rule_count),
|
||||
&resource,
|
||||
|b, res| b.iter(|| normalizer::normalize(black_box(res), Some(®istry), None)),
|
||||
);
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
fn bench_denormalize_simple(c: &mut Criterion) {
|
||||
let registry = build_registry();
|
||||
let resource = simple_storage_resource();
|
||||
let normalized = normalizer::normalize(&resource, Some(®istry), None);
|
||||
|
||||
c.bench_function("denormalize/simple_storage", |b| {
|
||||
b.iter(|| denormalizer::denormalize(black_box(&normalized), Some(®istry), None))
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_denormalize_nsg_scaling(c: &mut Criterion) {
|
||||
let registry = build_registry();
|
||||
let mut group = c.benchmark_group("denormalize/nsg_rules");
|
||||
|
||||
for rule_count in [5, 20, 100] {
|
||||
let resource = nsg_resource(rule_count);
|
||||
let normalized = normalizer::normalize(&resource, Some(®istry), None);
|
||||
group.bench_with_input(
|
||||
BenchmarkId::from_parameter(rule_count),
|
||||
&normalized,
|
||||
|b, norm| b.iter(|| denormalizer::denormalize(black_box(norm), Some(®istry), None)),
|
||||
);
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
fn bench_round_trip(c: &mut Criterion) {
|
||||
let registry = build_registry();
|
||||
let resource = nsg_resource(20);
|
||||
|
||||
c.bench_function("round_trip/nsg_20_rules", |b| {
|
||||
b.iter(|| {
|
||||
let n = normalizer::normalize(black_box(&resource), Some(®istry), None);
|
||||
denormalizer::denormalize(&n, Some(®istry), None)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_normalize_and_wrap(c: &mut Criterion) {
|
||||
let registry = build_registry();
|
||||
let resource = nsg_resource(20);
|
||||
let context = to_regorus(json!({"resourceGroup": {"name": "rg1"}}));
|
||||
let parameters = to_regorus(json!({"env": "prod"}));
|
||||
|
||||
c.bench_function("normalize_and_wrap/nsg_20_rules", |b| {
|
||||
b.iter(|| {
|
||||
registry.normalize_and_wrap(
|
||||
black_box(&resource),
|
||||
None,
|
||||
Some(context.clone()),
|
||||
Some(parameters.clone()),
|
||||
)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_registry_load(c: &mut Criterion) {
|
||||
c.bench_function("registry/load_from_json", |b| {
|
||||
b.iter(|| {
|
||||
let mut reg = AliasRegistry::new();
|
||||
reg.load_from_json(black_box(ALIASES_JSON)).unwrap();
|
||||
reg
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Large-payload benchmarks ───────────────────────────────────────────────
|
||||
//
|
||||
// These stress the hot paths identified in the performance analysis:
|
||||
// - Nested set helpers (alias-heavy catalog with deep properties)
|
||||
// - Array element remap/cleanup/rewrap (large sub-resource arrays)
|
||||
// - Scalar denormalization lookups (many aliases × many fields)
|
||||
|
||||
/// Build a large alias catalog with `n` scalar aliases for storage accounts.
|
||||
/// Each alias maps to a nested `properties.section_i.field_j` path, creating
|
||||
/// deep nested-set workloads.
|
||||
fn large_alias_catalog(n: usize) -> String {
|
||||
let mut aliases = Vec::new();
|
||||
for i in 0..n {
|
||||
let section = i / 10;
|
||||
let field = i % 10;
|
||||
aliases.push(format!(
|
||||
r#"{{
|
||||
"name": "Microsoft.Storage/storageAccounts/section{section}Field{field}",
|
||||
"defaultPath": "properties.section{section}.field{field}",
|
||||
"paths": []
|
||||
}}"#,
|
||||
));
|
||||
}
|
||||
format!(
|
||||
r#"[{{
|
||||
"namespace": "Microsoft.Storage",
|
||||
"resourceTypes": [{{
|
||||
"resourceType": "storageAccounts",
|
||||
"aliases": [{aliases}]
|
||||
}}]
|
||||
}}]"#,
|
||||
aliases = aliases.join(",")
|
||||
)
|
||||
}
|
||||
|
||||
/// Build a storage account resource whose `properties` contain nested sections
|
||||
/// matching the large alias catalog.
|
||||
fn large_storage_resource(alias_count: usize) -> Value {
|
||||
let mut sections = serde_json::Map::new();
|
||||
for i in 0..alias_count {
|
||||
let section = i / 10;
|
||||
let field = i % 10;
|
||||
let section_key = format!("section{section}");
|
||||
let section_obj = sections
|
||||
.entry(section_key)
|
||||
.or_insert_with(|| serde_json::Value::Object(serde_json::Map::new()));
|
||||
if let serde_json::Value::Object(m) = section_obj {
|
||||
m.insert(format!("field{field}"), serde_json::Value::from(i));
|
||||
}
|
||||
}
|
||||
Value::from(json!({
|
||||
"name": "bigStorage",
|
||||
"type": "Microsoft.Storage/storageAccounts",
|
||||
"location": "westus2",
|
||||
"properties": sections
|
||||
}))
|
||||
}
|
||||
|
||||
fn bench_normalize_large_catalog(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("normalize/large_catalog");
|
||||
for alias_count in [50, 200] {
|
||||
let catalog_json = large_alias_catalog(alias_count);
|
||||
let mut reg = AliasRegistry::new();
|
||||
reg.load_from_json(&catalog_json).unwrap();
|
||||
let resource = large_storage_resource(alias_count);
|
||||
group.bench_with_input(
|
||||
BenchmarkId::from_parameter(alias_count),
|
||||
&(reg, resource),
|
||||
|b, (reg, res)| b.iter(|| normalizer::normalize(black_box(res), Some(reg), None)),
|
||||
);
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
fn bench_denormalize_large_catalog(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("denormalize/large_catalog");
|
||||
for alias_count in [50, 200] {
|
||||
let catalog_json = large_alias_catalog(alias_count);
|
||||
let mut reg = AliasRegistry::new();
|
||||
reg.load_from_json(&catalog_json).unwrap();
|
||||
let resource = large_storage_resource(alias_count);
|
||||
let normalized = normalizer::normalize(&resource, Some(®), None);
|
||||
group.bench_with_input(
|
||||
BenchmarkId::from_parameter(alias_count),
|
||||
&(reg, normalized),
|
||||
|b, (reg, norm)| b.iter(|| denormalizer::denormalize(black_box(norm), Some(reg), None)),
|
||||
);
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
fn bench_nsg_large_subarrays(c: &mut Criterion) {
|
||||
let registry = build_registry();
|
||||
let mut group = c.benchmark_group("round_trip/nsg_sub_resource");
|
||||
for rule_count in [50, 200, 500] {
|
||||
let resource = nsg_resource(rule_count);
|
||||
group.bench_with_input(
|
||||
BenchmarkId::from_parameter(rule_count),
|
||||
&resource,
|
||||
|b, res| {
|
||||
b.iter(|| {
|
||||
let n = normalizer::normalize(black_box(res), Some(®istry), None);
|
||||
denormalizer::denormalize(&n, Some(®istry), None)
|
||||
})
|
||||
},
|
||||
);
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
// ─── Versioned-path benchmarks ──────────────────────────────────────────────
|
||||
//
|
||||
// Exercise the precomputed versioned-path aggregates by building a catalog
|
||||
// where wildcard (array) aliases have version-specific paths that differ from
|
||||
// the default, then running normalize/denormalize with an explicit api_version.
|
||||
|
||||
/// NSG-like alias catalog where wildcard aliases have versioned paths that
|
||||
/// differ from the default. This forces the normalize/denormalize path through
|
||||
/// the versioned aggregate lookup rather than the default-aggregate fast path.
|
||||
const VERSIONED_ALIASES_JSON: &str = r#"[
|
||||
{
|
||||
"namespace": "Microsoft.Network",
|
||||
"resourceTypes": [
|
||||
{
|
||||
"resourceType": "networkSecurityGroups",
|
||||
"aliases": [
|
||||
{
|
||||
"name": "Microsoft.Network/networkSecurityGroups/securityRules[*].protocol",
|
||||
"defaultPath": "properties.securityRules[*].properties.protocol",
|
||||
"paths": [
|
||||
{ "path": "properties.securityRules[*].properties.transportProtocol", "apiVersions": ["2020-01-01"] },
|
||||
{ "path": "properties.securityRules[*].properties.protocol", "apiVersions": ["2022-01-01"] }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Network/networkSecurityGroups/securityRules[*].access",
|
||||
"defaultPath": "properties.securityRules[*].properties.access",
|
||||
"paths": [
|
||||
{ "path": "properties.securityRules[*].properties.accessLevel", "apiVersions": ["2020-01-01"] },
|
||||
{ "path": "properties.securityRules[*].properties.access", "apiVersions": ["2022-01-01"] }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Network/networkSecurityGroups/securityRules[*].priority",
|
||||
"defaultPath": "properties.securityRules[*].properties.priority",
|
||||
"paths": [
|
||||
{ "path": "properties.securityRules[*].properties.rulePriority", "apiVersions": ["2020-01-01"] },
|
||||
{ "path": "properties.securityRules[*].properties.priority", "apiVersions": ["2022-01-01"] }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Network/networkSecurityGroups/securityRules[*].direction",
|
||||
"defaultPath": "properties.securityRules[*].properties.direction",
|
||||
"paths": []
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Network/networkSecurityGroups/securityRules[*].sourceAddressPrefix",
|
||||
"defaultPath": "properties.securityRules[*].properties.sourceAddressPrefix",
|
||||
"paths": []
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Network/networkSecurityGroups/securityRules[*].destinationPortRange",
|
||||
"defaultPath": "properties.securityRules[*].properties.destinationPortRange",
|
||||
"paths": []
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Network/networkSecurityGroups/securityRules[*].name",
|
||||
"defaultPath": "properties.securityRules[*].name",
|
||||
"paths": []
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.Network/networkSecurityGroups/provisioningState",
|
||||
"defaultPath": "properties.provisioningState",
|
||||
"paths": [
|
||||
{ "path": "properties.state", "apiVersions": ["2020-01-01"] },
|
||||
{ "path": "properties.provisioningState", "apiVersions": ["2022-01-01"] }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]"#;
|
||||
|
||||
fn build_versioned_registry() -> AliasRegistry {
|
||||
let mut reg = AliasRegistry::new();
|
||||
reg.load_from_json(VERSIONED_ALIASES_JSON).unwrap();
|
||||
reg
|
||||
}
|
||||
|
||||
/// Build an NSG resource for versioned-path benchmarks.
|
||||
/// Uses the 2020-01-01 field names (`transportProtocol`, `accessLevel`,
|
||||
/// `rulePriority`) so that versioned path resolution actually differs from
|
||||
/// the default.
|
||||
fn nsg_versioned_resource(rule_count: usize) -> Value {
|
||||
let rules: Vec<serde_json::Value> = (0..rule_count)
|
||||
.map(|i| {
|
||||
json!({
|
||||
"name": format!("rule-{}", i),
|
||||
"properties": {
|
||||
"transportProtocol": "Tcp",
|
||||
"accessLevel": if i % 2 == 0 { "Allow" } else { "Deny" },
|
||||
"rulePriority": 100 + i,
|
||||
"direction": "Inbound",
|
||||
"sourceAddressPrefix": format!("10.0.{}.0/24", i % 256),
|
||||
"destinationPortRange": format!("{}", 80 + i)
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
to_regorus(json!({
|
||||
"name": "myNsg",
|
||||
"type": "Microsoft.Network/networkSecurityGroups",
|
||||
"location": "eastus",
|
||||
"properties": {
|
||||
"state": "Succeeded",
|
||||
"securityRules": rules
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
fn bench_normalize_versioned(c: &mut Criterion) {
|
||||
let registry = build_versioned_registry();
|
||||
let mut group = c.benchmark_group("normalize_versioned/nsg_rules");
|
||||
|
||||
for rule_count in [5, 20, 100] {
|
||||
let resource = nsg_versioned_resource(rule_count);
|
||||
group.bench_with_input(
|
||||
BenchmarkId::from_parameter(rule_count),
|
||||
&resource,
|
||||
|b, res| {
|
||||
b.iter(|| {
|
||||
normalizer::normalize(black_box(res), Some(®istry), Some("2020-01-01"))
|
||||
})
|
||||
},
|
||||
);
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
fn bench_denormalize_versioned(c: &mut Criterion) {
|
||||
let registry = build_versioned_registry();
|
||||
let mut group = c.benchmark_group("denormalize_versioned/nsg_rules");
|
||||
|
||||
for rule_count in [5, 20, 100] {
|
||||
let resource = nsg_versioned_resource(rule_count);
|
||||
let normalized = normalizer::normalize(&resource, Some(®istry), Some("2020-01-01"));
|
||||
group.bench_with_input(
|
||||
BenchmarkId::from_parameter(rule_count),
|
||||
&normalized,
|
||||
|b, norm| {
|
||||
b.iter(|| {
|
||||
denormalizer::denormalize(black_box(norm), Some(®istry), Some("2020-01-01"))
|
||||
})
|
||||
},
|
||||
);
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
fn bench_round_trip_versioned(c: &mut Criterion) {
|
||||
let registry = build_versioned_registry();
|
||||
let mut group = c.benchmark_group("round_trip_versioned/nsg_rules");
|
||||
|
||||
for rule_count in [20, 100] {
|
||||
let resource = nsg_versioned_resource(rule_count);
|
||||
group.bench_with_input(
|
||||
BenchmarkId::from_parameter(rule_count),
|
||||
&resource,
|
||||
|b, res| {
|
||||
b.iter(|| {
|
||||
let n =
|
||||
normalizer::normalize(black_box(res), Some(®istry), Some("2020-01-01"));
|
||||
denormalizer::denormalize(&n, Some(®istry), Some("2020-01-01"))
|
||||
})
|
||||
},
|
||||
);
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
normalization_benches,
|
||||
bench_normalize_simple,
|
||||
bench_normalize_no_aliases,
|
||||
bench_normalize_nsg_scaling,
|
||||
bench_denormalize_simple,
|
||||
bench_denormalize_nsg_scaling,
|
||||
bench_round_trip,
|
||||
bench_normalize_and_wrap,
|
||||
bench_registry_load,
|
||||
bench_normalize_large_catalog,
|
||||
bench_denormalize_large_catalog,
|
||||
bench_nsg_large_subarrays,
|
||||
bench_normalize_versioned,
|
||||
bench_denormalize_versioned,
|
||||
bench_round_trip_versioned,
|
||||
);
|
||||
criterion_main!(normalization_benches);
|
||||
680
benches/rvm_benchmark.rs
Normal file
680
benches/rvm_benchmark.rs
Normal file
@@ -0,0 +1,680 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
//! Comprehensive RVM benchmarks covering all aspects of the Rego Virtual Machine.
|
||||
//!
|
||||
//! # Policy families
|
||||
//!
|
||||
//! | Family | Source | Policies | Inputs/policy |
|
||||
//! |------------|-------------------------------|----------|---------------|
|
||||
//! | Synthetic | `benches/evaluation/test_data`| 9 | 3 each |
|
||||
//! | ACI | `tests/aci` | 9 | 1 each |
|
||||
//!
|
||||
//! # Benchmark groups
|
||||
//!
|
||||
//! | Group | What it measures |
|
||||
//! |--------------------------|-------------------------------------------------------|
|
||||
//! | `cold/{case}/{config}` | Cold: new VM + load + data + input + execute |
|
||||
//! | `hot/{case}/{config}` | Hot: set_input + execute (VM reused across iters) |
|
||||
//! | `compilation` | Rego CompiledPolicy → RVM Program |
|
||||
//! | `serialization` | Program binary serialize / deserialize roundtrip |
|
||||
//! | `startup` | Isolated VM creation & setup overhead |
|
||||
//! | `stats` | Instruction/literal counts (reported as throughput) |
|
||||
//! | `end_to_end` | Full roundtrip: compile → serialize → deserialize → eval |
|
||||
//!
|
||||
//! # Running subsets
|
||||
//!
|
||||
//! ```sh
|
||||
//! cargo bench --bench rvm_benchmark # everything
|
||||
//! cargo bench --bench rvm_benchmark -- cold # all cold eval
|
||||
//! cargo bench --bench rvm_benchmark -- hot # all hot eval
|
||||
//! cargo bench --bench rvm_benchmark -- regular_with_limits # one config across cases
|
||||
//! cargo bench --bench rvm_benchmark -- cold/aci/ # all ACI cold benchmarks
|
||||
//! cargo bench --bench rvm_benchmark -- rbac # one policy family
|
||||
//! cargo bench --bench rvm_benchmark -- compilation # compilation only
|
||||
//! cargo bench --bench rvm_benchmark -- serialization # serialization only
|
||||
//! cargo bench --bench rvm_benchmark -- startup # startup overhead
|
||||
//! ```
|
||||
|
||||
use std::hint::black_box;
|
||||
use std::num::NonZeroU32;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use regorus::languages::rego::compiler::Compiler;
|
||||
use regorus::rvm::program::Program;
|
||||
use regorus::rvm::vm::{ExecutionMode, RegoVM};
|
||||
use regorus::utils::limits::ExecutionTimerConfig;
|
||||
use regorus::{Engine, Rc, Value};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Limit constants – generous ceilings that still exercise the limit-checking
|
||||
// hot path (memory_check, execution_timer_tick, instruction-limit compare).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[cfg(feature = "allocator-memory-limits")]
|
||||
const MEMORY_LIMIT_BYTES: u64 = 256 * 1024 * 1024;
|
||||
const TIME_LIMIT: Duration = Duration::from_secs(30);
|
||||
const TIMER_CHECK_INTERVAL: NonZeroU32 = NonZeroU32::new(16).unwrap();
|
||||
const INSTRUCTION_LIMIT: usize = 10_000_000;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct EvalConfig {
|
||||
name: &'static str,
|
||||
mode: ExecutionMode,
|
||||
limits: bool,
|
||||
}
|
||||
|
||||
const EVAL_CONFIGS: [EvalConfig; 4] = [
|
||||
EvalConfig {
|
||||
name: "regular_no_limits",
|
||||
mode: ExecutionMode::RunToCompletion,
|
||||
limits: false,
|
||||
},
|
||||
EvalConfig {
|
||||
name: "regular_with_limits",
|
||||
mode: ExecutionMode::RunToCompletion,
|
||||
limits: true,
|
||||
},
|
||||
EvalConfig {
|
||||
name: "suspendable_no_limits",
|
||||
mode: ExecutionMode::Suspendable,
|
||||
limits: false,
|
||||
},
|
||||
EvalConfig {
|
||||
name: "suspendable_with_limits",
|
||||
mode: ExecutionMode::Suspendable,
|
||||
limits: true,
|
||||
},
|
||||
];
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Data types
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// A compiled benchmark program ready for RVM execution.
|
||||
struct BenchmarkProgram {
|
||||
/// Human-readable name (e.g. "rbac_policy" or "aci/create_container").
|
||||
name: String,
|
||||
/// Pre-compiled RVM program.
|
||||
program: Arc<Program>,
|
||||
/// Compiled policy (kept for compilation benchmarks).
|
||||
compiled_policy: regorus::CompiledPolicy,
|
||||
/// Entry-point rule path.
|
||||
entry_point: String,
|
||||
/// Data object (Some for policies that require external data like ACI).
|
||||
data: Option<Value>,
|
||||
/// Named inputs for this policy.
|
||||
inputs: Vec<(String, Value)>,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ACI YAML types
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct AciTestCase {
|
||||
note: String,
|
||||
data: Value,
|
||||
input: Value,
|
||||
modules: Vec<String>,
|
||||
query: String,
|
||||
want_result: Value,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct AciYamlTest {
|
||||
cases: Vec<AciTestCase>,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Synthetic policy loading
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Policy ↔ input file mapping for synthetic policies.
|
||||
const SYNTHETIC_POLICIES: &[(&str, &str, &[&str])] = &[
|
||||
(
|
||||
"rbac_policy",
|
||||
"rbac_policy.rego",
|
||||
&["rbac_input.json", "rbac_input2.json", "rbac_input3.json"],
|
||||
),
|
||||
(
|
||||
"api_access",
|
||||
"api_access_policy.rego",
|
||||
&[
|
||||
"api_access_input.json",
|
||||
"api_access_input2.json",
|
||||
"api_access_input3.json",
|
||||
],
|
||||
),
|
||||
(
|
||||
"data_sensitivity",
|
||||
"data_sensitivity_policy.rego",
|
||||
&[
|
||||
"data_sensitivity_input.json",
|
||||
"data_sensitivity_input2.json",
|
||||
"data_sensitivity_input3.json",
|
||||
],
|
||||
),
|
||||
(
|
||||
"time_based",
|
||||
"time_based_policy.rego",
|
||||
&[
|
||||
"time_based_input.json",
|
||||
"time_based_input2.json",
|
||||
"time_based_input3.json",
|
||||
],
|
||||
),
|
||||
(
|
||||
"data_processing",
|
||||
"data_processing_policy.rego",
|
||||
&[
|
||||
"data_processing_input.json",
|
||||
"data_processing_input2.json",
|
||||
"data_processing_input3.json",
|
||||
],
|
||||
),
|
||||
(
|
||||
"azure_vm",
|
||||
"azure_vm_policy.rego",
|
||||
&[
|
||||
"azure_vm_input.json",
|
||||
"azure_vm_input2.json",
|
||||
"azure_vm_input3.json",
|
||||
],
|
||||
),
|
||||
(
|
||||
"azure_storage",
|
||||
"azure_storage_policy.rego",
|
||||
&[
|
||||
"azure_storage_input.json",
|
||||
"azure_storage_input2.json",
|
||||
"azure_storage_input3.json",
|
||||
],
|
||||
),
|
||||
(
|
||||
"azure_keyvault",
|
||||
"azure_keyvault_policy.rego",
|
||||
&[
|
||||
"azure_keyvault_input.json",
|
||||
"azure_keyvault_input2.json",
|
||||
"azure_keyvault_input3.json",
|
||||
],
|
||||
),
|
||||
(
|
||||
"azure_nsg",
|
||||
"azure_nsg_policy.rego",
|
||||
&[
|
||||
"azure_nsg_input.json",
|
||||
"azure_nsg_input2.json",
|
||||
"azure_nsg_input3.json",
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
/// Compile synthetic Rego policies into RVM programs.
|
||||
fn compile_synthetic_programs() -> Vec<BenchmarkProgram> {
|
||||
let base_dir = Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("benches")
|
||||
.join("evaluation")
|
||||
.join("test_data");
|
||||
|
||||
let entry_point = "data.bench.allow";
|
||||
let entry_point_rc: Rc<str> = entry_point.into();
|
||||
|
||||
SYNTHETIC_POLICIES
|
||||
.iter()
|
||||
.map(|(name, policy_file, input_files)| {
|
||||
let policy_path = base_dir.join("policies").join(policy_file);
|
||||
let policy_content = std::fs::read_to_string(&policy_path)
|
||||
.unwrap_or_else(|e| panic!("Failed to read {policy_path:?}: {e}"));
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine
|
||||
.add_policy("policy.rego".to_string(), policy_content)
|
||||
.expect("failed to add policy");
|
||||
|
||||
let compiled_policy = engine
|
||||
.compile_with_entrypoint(&entry_point_rc)
|
||||
.expect("failed to compile policy");
|
||||
|
||||
let program = Compiler::compile_from_policy(&compiled_policy, &[entry_point])
|
||||
.expect("failed to compile to RVM program");
|
||||
|
||||
let inputs: Vec<(String, Value)> = input_files
|
||||
.iter()
|
||||
.map(|input_file| {
|
||||
let input_path = base_dir.join("inputs").join(input_file);
|
||||
let json = std::fs::read_to_string(&input_path)
|
||||
.unwrap_or_else(|e| panic!("Failed to read {input_path:?}: {e}"));
|
||||
let value = Value::from_json_str(&json).expect("failed to parse input JSON");
|
||||
let display = input_file.trim_end_matches(".json").to_string();
|
||||
(display, value)
|
||||
})
|
||||
.collect();
|
||||
|
||||
BenchmarkProgram {
|
||||
name: name.to_string(),
|
||||
program,
|
||||
compiled_policy,
|
||||
entry_point: entry_point.to_string(),
|
||||
data: None,
|
||||
inputs,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ACI policy loading
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Load all ACI test cases from YAML files.
|
||||
fn load_aci_cases(dir: &Path) -> Vec<AciTestCase> {
|
||||
let mut cases = Vec::new();
|
||||
for entry in WalkDir::new(dir)
|
||||
.sort_by_file_name()
|
||||
.into_iter()
|
||||
.filter_map(|e| e.ok())
|
||||
{
|
||||
let path = entry.path();
|
||||
if !path.to_string_lossy().ends_with(".yaml") {
|
||||
continue;
|
||||
}
|
||||
let yaml = std::fs::read(path).expect("failed to read yaml");
|
||||
let yaml = String::from_utf8_lossy(&yaml);
|
||||
let test: AciYamlTest = serde_yaml::from_str(&yaml).expect("failed to deserialize yaml");
|
||||
cases.extend(test.cases);
|
||||
}
|
||||
cases
|
||||
}
|
||||
|
||||
/// Build an Engine with policies loaded for a given ACI test case.
|
||||
fn build_aci_engine(dir: &Path, case: &AciTestCase) -> Engine {
|
||||
let mut engine = Engine::new();
|
||||
engine.set_rego_v0(true);
|
||||
engine
|
||||
.add_data(case.data.clone())
|
||||
.expect("failed to add data");
|
||||
engine.set_input(case.input.clone());
|
||||
for (idx, rego) in case.modules.iter().enumerate() {
|
||||
if rego.ends_with(".rego") {
|
||||
engine
|
||||
.add_policy_from_file(dir.join(rego).to_str().expect("invalid path"))
|
||||
.expect("failed to add policy");
|
||||
} else {
|
||||
engine
|
||||
.add_policy(format!("rego{idx}.rego"), rego.clone())
|
||||
.expect("failed to add policy");
|
||||
}
|
||||
}
|
||||
engine
|
||||
}
|
||||
|
||||
/// Compile ACI test cases into RVM programs.
|
||||
fn compile_aci_programs() -> Vec<BenchmarkProgram> {
|
||||
let dir = Path::new("tests/aci");
|
||||
load_aci_cases(dir)
|
||||
.into_iter()
|
||||
.map(|case| {
|
||||
let mut engine = build_aci_engine(dir, &case);
|
||||
let rule = case.query.replace("=x", "");
|
||||
let rule_rc: Rc<str> = rule.clone().into();
|
||||
let compiled_policy = engine
|
||||
.compile_with_entrypoint(&rule_rc)
|
||||
.expect("failed to compile");
|
||||
let program = Compiler::compile_from_policy(&compiled_policy, &[rule.as_str()])
|
||||
.expect("failed to compile to RVM");
|
||||
|
||||
BenchmarkProgram {
|
||||
name: format!("aci/{}", case.note),
|
||||
program,
|
||||
compiled_policy,
|
||||
entry_point: rule,
|
||||
data: Some(case.data),
|
||||
inputs: vec![("input".to_string(), case.input)],
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Compile all policies
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Compile all policies (synthetic + ACI) into RVM programs.
|
||||
fn compile_all_programs() -> Vec<BenchmarkProgram> {
|
||||
let mut programs = compile_synthetic_programs();
|
||||
programs.extend(compile_aci_programs());
|
||||
programs
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Limit helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Apply or remove production-style limits based on a boolean flag.
|
||||
fn configure_limits(vm: &mut RegoVM, limits: bool) {
|
||||
if limits {
|
||||
#[cfg(feature = "allocator-memory-limits")]
|
||||
regorus::set_global_memory_limit(Some(MEMORY_LIMIT_BYTES));
|
||||
vm.set_execution_timer_config(Some(ExecutionTimerConfig {
|
||||
limit: TIME_LIMIT,
|
||||
check_interval: TIMER_CHECK_INTERVAL,
|
||||
}));
|
||||
vm.set_max_instructions(INSTRUCTION_LIMIT);
|
||||
} else {
|
||||
#[cfg(feature = "allocator-memory-limits")]
|
||||
regorus::set_global_memory_limit(None);
|
||||
vm.set_execution_timer_config(None);
|
||||
vm.set_max_instructions(usize::MAX);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Cold evaluation — new VM per iteration (full setup + execute)
|
||||
//
|
||||
// Benchmarks are registered case-first so each workload is shown with all
|
||||
// config variants adjacent to one another, making per-case comparisons easier.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
fn bench_cold(c: &mut Criterion) {
|
||||
let programs = compile_all_programs();
|
||||
let mut group = c.benchmark_group("cold");
|
||||
|
||||
for bp in &programs {
|
||||
for (input_name, input_value) in &bp.inputs {
|
||||
let case_id = if bp.inputs.len() == 1 {
|
||||
bp.name.clone()
|
||||
} else {
|
||||
format!("{}/{}", bp.name, input_name)
|
||||
};
|
||||
let program = bp.program.clone();
|
||||
let data = bp.data.clone();
|
||||
let input = input_value.clone();
|
||||
|
||||
for config in EVAL_CONFIGS {
|
||||
group.bench_function(BenchmarkId::new(&case_id, config.name), |b| {
|
||||
b.iter(|| {
|
||||
let mut vm = RegoVM::new();
|
||||
vm.set_execution_mode(config.mode);
|
||||
vm.load_program(black_box(program.clone()));
|
||||
if let Some(ref d) = data {
|
||||
vm.set_data(black_box(d.clone())).unwrap();
|
||||
}
|
||||
vm.set_input(black_box(input.clone()));
|
||||
configure_limits(&mut vm, config.limits);
|
||||
black_box(vm.execute().unwrap())
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Hot evaluation — VM reused across iterations
|
||||
//
|
||||
// The VM is created once with program, data, mode, and limits. Each
|
||||
// iteration only calls set_input + execute, measuring pure execution
|
||||
// overhead with minimal setup. A warm-up execution fills the register
|
||||
// window pool so all iterations benefit from pooled allocations.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
fn bench_hot(c: &mut Criterion) {
|
||||
let programs = compile_all_programs();
|
||||
let mut group = c.benchmark_group("hot");
|
||||
|
||||
for bp in &programs {
|
||||
let program = bp.program.clone();
|
||||
let data = bp.data.clone();
|
||||
let inputs: Vec<Value> = bp.inputs.iter().map(|(_, v)| v.clone()).collect();
|
||||
let num_inputs = inputs.len();
|
||||
|
||||
for config in EVAL_CONFIGS {
|
||||
group.bench_function(BenchmarkId::new(&bp.name, config.name), |b| {
|
||||
let mut vm = RegoVM::new();
|
||||
vm.set_execution_mode(config.mode);
|
||||
vm.load_program(program.clone());
|
||||
if let Some(ref d) = data {
|
||||
vm.set_data(d.clone()).unwrap();
|
||||
}
|
||||
configure_limits(&mut vm, config.limits);
|
||||
|
||||
// Warm up: fill register window pools, caches, etc.
|
||||
vm.set_input(inputs[0].clone());
|
||||
vm.execute().expect("warm-up failed");
|
||||
|
||||
let mut i = 0usize;
|
||||
b.iter(|| {
|
||||
let input = &inputs[i % num_inputs];
|
||||
vm.set_input(black_box(input.clone()));
|
||||
black_box(vm.execute().unwrap());
|
||||
i += 1;
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Compilation — Rego CompiledPolicy → RVM Program
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
fn bench_compilation(c: &mut Criterion) {
|
||||
let programs = compile_all_programs();
|
||||
let mut group = c.benchmark_group("compilation");
|
||||
|
||||
for bp in &programs {
|
||||
let entry_point: &str = &bp.entry_point;
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("rego_to_rvm", &bp.name),
|
||||
&bp.compiled_policy,
|
||||
|b, compiled_policy| {
|
||||
b.iter(|| {
|
||||
Compiler::compile_from_policy(
|
||||
black_box(compiled_policy),
|
||||
black_box(&[entry_point]),
|
||||
)
|
||||
.unwrap();
|
||||
})
|
||||
},
|
||||
);
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Serialization — binary serialize / deserialize roundtrip
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
fn bench_serialization(c: &mut Criterion) {
|
||||
let programs = compile_all_programs();
|
||||
let mut group = c.benchmark_group("serialization");
|
||||
|
||||
for bp in &programs {
|
||||
let program = &bp.program;
|
||||
let serialized = program
|
||||
.serialize_binary()
|
||||
.expect("failed to serialize program");
|
||||
let byte_len = serialized.len() as u64;
|
||||
|
||||
group.throughput(Throughput::Bytes(byte_len));
|
||||
group.bench_function(BenchmarkId::new("serialize", &bp.name), |b| {
|
||||
b.iter(|| black_box(program.serialize_binary().unwrap()))
|
||||
});
|
||||
|
||||
group.throughput(Throughput::Bytes(byte_len));
|
||||
group.bench_function(BenchmarkId::new("deserialize", &bp.name), |b| {
|
||||
b.iter(|| black_box(Program::deserialize_binary(black_box(&serialized)).unwrap()))
|
||||
});
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Startup — isolated VM creation & setup overhead
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
fn bench_startup(c: &mut Criterion) {
|
||||
let programs = compile_all_programs();
|
||||
let mut group = c.benchmark_group("startup");
|
||||
|
||||
// Use the first program as representative for startup overhead.
|
||||
let bp = &programs[0];
|
||||
let program = bp.program.clone();
|
||||
let input = bp.inputs[0].1.clone();
|
||||
|
||||
// Bare VM creation
|
||||
group.bench_function("new", |b| b.iter(|| black_box(RegoVM::new())));
|
||||
|
||||
// load_program (Arc clone + internal setup)
|
||||
group.bench_function("load_program", |b| {
|
||||
b.iter(|| {
|
||||
let mut vm = RegoVM::new();
|
||||
vm.load_program(black_box(program.clone()));
|
||||
black_box(&vm);
|
||||
})
|
||||
});
|
||||
|
||||
// set_input
|
||||
group.bench_function("set_input", |b| {
|
||||
let mut vm = RegoVM::new();
|
||||
vm.load_program(program.clone());
|
||||
b.iter(|| {
|
||||
vm.set_input(black_box(input.clone()));
|
||||
})
|
||||
});
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Stats — instruction / literal counts (reported as throughput)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
fn bench_stats(c: &mut Criterion) {
|
||||
let programs = compile_all_programs();
|
||||
|
||||
eprintln!();
|
||||
eprintln!(
|
||||
"{:<30} {:>8} {:>8} {:>8} {:>10}",
|
||||
"program", "instrs", "lits", "entries", "bytes"
|
||||
);
|
||||
eprintln!("{}", "-".repeat(70));
|
||||
|
||||
let mut group = c.benchmark_group("stats");
|
||||
for bp in &programs {
|
||||
let serialized = bp.program.serialize_binary().expect("serialize failed");
|
||||
let byte_len = serialized.len();
|
||||
let instr_count = bp.program.instructions.len();
|
||||
let lit_count = bp.program.literals.len();
|
||||
let entry_count = bp.program.entry_points.len();
|
||||
|
||||
eprintln!(
|
||||
"{:<30} {:>8} {:>8} {:>8} {:>10}",
|
||||
bp.name, instr_count, lit_count, entry_count, byte_len,
|
||||
);
|
||||
|
||||
group.throughput(Throughput::Elements(instr_count as u64));
|
||||
group.bench_function(BenchmarkId::new("serialize", &bp.name), |b| {
|
||||
b.iter(|| black_box(bp.program.serialize_binary().unwrap()))
|
||||
});
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// End-to-end roundtrip (compile + serialize + deserialize + eval)
|
||||
//
|
||||
// Only runs for synthetic policies where we have direct access to rego
|
||||
// source files. ACI policies are loaded from YAML with module references
|
||||
// which makes the setup pipeline different.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
fn bench_end_to_end(c: &mut Criterion) {
|
||||
let base_dir = Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("benches")
|
||||
.join("evaluation")
|
||||
.join("test_data");
|
||||
|
||||
let entry_point = "data.bench.allow";
|
||||
let entry_point_rc: Rc<str> = entry_point.into();
|
||||
|
||||
let mut group = c.benchmark_group("end_to_end");
|
||||
|
||||
for &(name, policy_file, input_files) in SYNTHETIC_POLICIES {
|
||||
let policy_path = base_dir.join("policies").join(policy_file);
|
||||
let policy_content = std::fs::read_to_string(&policy_path)
|
||||
.unwrap_or_else(|e| panic!("Failed to read {policy_path:?}: {e}"));
|
||||
|
||||
// Use just the first input for end-to-end
|
||||
let input_path = base_dir.join("inputs").join(input_files[0]);
|
||||
let input_json = std::fs::read_to_string(&input_path)
|
||||
.unwrap_or_else(|e| panic!("Failed to read {input_path:?}: {e}"));
|
||||
|
||||
group.bench_function(BenchmarkId::new("roundtrip", name), |b| {
|
||||
b.iter(|| {
|
||||
// 1. Engine + parse
|
||||
let mut engine = Engine::new();
|
||||
engine
|
||||
.add_policy("policy.rego".to_string(), policy_content.clone())
|
||||
.unwrap();
|
||||
|
||||
// 2. Compile to CompiledPolicy
|
||||
let compiled_policy = engine.compile_with_entrypoint(&entry_point_rc).unwrap();
|
||||
|
||||
// 3. Compile to RVM Program
|
||||
let program =
|
||||
Compiler::compile_from_policy(&compiled_policy, &[entry_point]).unwrap();
|
||||
|
||||
// 4. Serialize
|
||||
let bytes = program.serialize_binary().unwrap();
|
||||
|
||||
// 5. Deserialize
|
||||
let deserialized = Program::deserialize_binary(&bytes).unwrap();
|
||||
let program = match deserialized {
|
||||
regorus::rvm::program::DeserializationResult::Complete(p) => Arc::new(p),
|
||||
regorus::rvm::program::DeserializationResult::Partial(p) => {
|
||||
Arc::new(Program::compile_from_partial(p).unwrap())
|
||||
}
|
||||
};
|
||||
|
||||
// 6. Execute
|
||||
let mut vm = RegoVM::new();
|
||||
vm.load_program(program);
|
||||
let input = Value::from_json_str(&input_json).unwrap();
|
||||
vm.set_input(input);
|
||||
black_box(vm.execute().unwrap());
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Criterion groups — organised for selective runs
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
criterion_group!(cold_benches, bench_cold);
|
||||
|
||||
criterion_group!(hot_benches, bench_hot);
|
||||
|
||||
criterion_group!(
|
||||
misc_benches,
|
||||
bench_compilation,
|
||||
bench_serialization,
|
||||
bench_startup,
|
||||
bench_stats,
|
||||
bench_end_to_end,
|
||||
);
|
||||
|
||||
criterion_main!(cold_benches, hot_benches, misc_benches);
|
||||
@@ -11,6 +11,20 @@ int main() {
|
||||
if (r.status != Ok)
|
||||
goto error;
|
||||
|
||||
// Configure the global pattern caches.
|
||||
RegorusCacheConfig cache_config = { .regex = 256, .glob = 128 };
|
||||
r = regorus_set_cache_config(cache_config);
|
||||
if (r.status != Ok)
|
||||
goto error;
|
||||
regorus_result_drop(r);
|
||||
|
||||
// Raise the default col limit to 2000
|
||||
RegorusPolicyLengthConfig len_config = { .max_col = 2000, .max_file_bytes = 1048576, .max_lines = 20000 };
|
||||
r = regorus_engine_set_policy_length_config(engine, len_config);
|
||||
if (r.status != Ok)
|
||||
goto error;
|
||||
regorus_result_drop(r);
|
||||
|
||||
// Load policies.
|
||||
r = regorus_engine_add_policy_from_file(engine, "../../../tests/aci/framework.rego");
|
||||
if (r.status != Ok)
|
||||
|
||||
@@ -6,8 +6,19 @@ void example()
|
||||
// Create engine
|
||||
regorus::Engine engine;
|
||||
|
||||
// Configure the global pattern caches.
|
||||
RegorusCacheConfig cache_config = { 256, 128 };
|
||||
regorus::set_cache_config(cache_config);
|
||||
|
||||
engine.set_rego_v0(true);
|
||||
engine.set_enable_coverage(true);
|
||||
|
||||
RegorusPolicyLengthConfig len_config;
|
||||
// Raise the default col limit to 2000
|
||||
len_config.max_col = 2000;
|
||||
len_config.max_file_bytes = 1048576;
|
||||
len_config.max_lines = 20000;
|
||||
engine.set_policy_length_config(len_config);
|
||||
|
||||
// Add policies.
|
||||
engine.add_policy("objects.rego",R"(package objects
|
||||
|
||||
@@ -131,7 +131,15 @@ namespace regorus {
|
||||
Result get_coverage_report_pretty() {
|
||||
return Result(regorus_engine_get_coverage_report_pretty(engine));
|
||||
}
|
||||
|
||||
|
||||
Result set_policy_length_config(RegorusPolicyLengthConfig config) {
|
||||
return Result(regorus_engine_set_policy_length_config(engine, config));
|
||||
}
|
||||
|
||||
Result clear_policy_length_config() {
|
||||
return Result(regorus_engine_clear_policy_length_config(engine));
|
||||
}
|
||||
|
||||
~Engine() {
|
||||
regorus_engine_drop(engine);
|
||||
}
|
||||
@@ -150,6 +158,14 @@ namespace regorus {
|
||||
Engine& operator=(const Engine&) = delete;
|
||||
};
|
||||
|
||||
inline Result set_cache_config(RegorusCacheConfig config) {
|
||||
return Result(regorus_set_cache_config(config));
|
||||
}
|
||||
|
||||
inline Result clear_cache() {
|
||||
return Result(regorus_clear_cache());
|
||||
}
|
||||
|
||||
class CompiledPolicy {
|
||||
public:
|
||||
explicit CompiledPolicy(RegorusCompiledPolicy* p) : policy(p) {}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Benchmarks
|
||||
public class CompiledPolicyEvaluationBenchmark
|
||||
{
|
||||
private static readonly string TestDataPath = Path.Combine(
|
||||
Directory.GetCurrentDirectory(),
|
||||
Directory.GetCurrentDirectory(),
|
||||
"..", "..", "..",
|
||||
"benches", "evaluation", "test_data"
|
||||
);
|
||||
@@ -33,7 +33,7 @@ namespace Benchmarks
|
||||
private static readonly string[] PolicyNames = new[]
|
||||
{
|
||||
"rbac_policy",
|
||||
"api_access_policy",
|
||||
"api_access_policy",
|
||||
"data_sensitivity_policy",
|
||||
"time_based_policy",
|
||||
"data_processing_policy",
|
||||
@@ -46,21 +46,21 @@ namespace Benchmarks
|
||||
private static List<(string Policy, string[] Inputs)> LoadPoliciesWithInputs()
|
||||
{
|
||||
var result = new List<(string Policy, string[] Inputs)>();
|
||||
|
||||
|
||||
foreach (var (policyFile, inputFiles) in PolicyInputFiles)
|
||||
{
|
||||
var policyPath = Path.Combine(TestDataPath, "policies", policyFile);
|
||||
var policy = File.ReadAllText(policyPath);
|
||||
|
||||
|
||||
var inputs = inputFiles.Select(inputFile =>
|
||||
{
|
||||
var inputPath = Path.Combine(TestDataPath, "inputs", inputFile);
|
||||
return File.ReadAllText(inputPath);
|
||||
}).ToArray();
|
||||
|
||||
|
||||
result.Add((policy, inputs));
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -68,14 +68,14 @@ namespace Benchmarks
|
||||
{
|
||||
var policiesWithInputs = LoadPoliciesWithInputs();
|
||||
var compiledPolicies = new List<CompiledPolicy>();
|
||||
|
||||
|
||||
foreach (var (policy, _) in policiesWithInputs)
|
||||
{
|
||||
var modules = new[] { new PolicyModule { Id = "policy.rego", Content = policy } };
|
||||
var modules = new[] { new PolicyModule("policy.rego", policy) };
|
||||
var compiled = Compiler.CompilePolicyWithEntrypoint("{}", modules, "data.bench.allow");
|
||||
compiledPolicies.Add(compiled);
|
||||
}
|
||||
|
||||
|
||||
return compiledPolicies;
|
||||
}
|
||||
|
||||
@@ -84,13 +84,13 @@ namespace Benchmarks
|
||||
var cpuCount = Environment.ProcessorCount;
|
||||
var maxThreads = cpuCount * 2;
|
||||
var threadCounts = new List<int> { 1, 2 };
|
||||
|
||||
|
||||
// Add even numbers from 4 to maxThreads
|
||||
for (int i = 4; i <= maxThreads; i += 2)
|
||||
{
|
||||
threadCounts.Add(i);
|
||||
}
|
||||
|
||||
|
||||
Console.WriteLine($"Running compiled policy benchmark with max_threads: {maxThreads}");
|
||||
Console.WriteLine($"Testing with thread counts: {string.Join(", ", threadCounts)}");
|
||||
Console.WriteLine();
|
||||
@@ -120,19 +120,19 @@ namespace Benchmarks
|
||||
const int durationSeconds = 3;
|
||||
var policiesWithInputs = LoadPoliciesWithInputs();
|
||||
List<CompiledPolicy>? compiledPolicies = null;
|
||||
|
||||
|
||||
if (useSharedPolicies)
|
||||
{
|
||||
compiledPolicies = PrepareSharedCompiledPolicies();
|
||||
}
|
||||
|
||||
|
||||
Console.WriteLine($"Warming up with {threads} threads for {warmupSeconds} seconds...");
|
||||
|
||||
|
||||
// Warmup phase
|
||||
var (_, _, _, _) = RunBenchmarkPhase(threads, warmupSeconds, policiesWithInputs, compiledPolicies, useSharedPolicies, isWarmup: true);
|
||||
|
||||
|
||||
Console.WriteLine($"Running benchmark with {threads} threads for {durationSeconds} seconds...");
|
||||
|
||||
|
||||
// Actual benchmark phase
|
||||
var (totalEvaluations, evaluationTime, policyCounters, allocatedBytes) = RunBenchmarkPhase(threads, durationSeconds, policiesWithInputs, compiledPolicies, useSharedPolicies, isWarmup: false);
|
||||
|
||||
@@ -155,7 +155,7 @@ namespace Benchmarks
|
||||
{
|
||||
foreach (var policy in compiledPolicies)
|
||||
{
|
||||
policy.Dispose();
|
||||
DisposeCompiledPolicy(policy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,8 +173,8 @@ namespace Benchmarks
|
||||
}
|
||||
|
||||
private static (int totalEvaluations, TimeSpan evaluationTime, Dictionary<string, int> policyCounters, long allocatedBytes) RunBenchmarkPhase(
|
||||
int threads,
|
||||
int durationSeconds,
|
||||
int threads,
|
||||
int durationSeconds,
|
||||
List<(string Policy, string[] Inputs)> policiesWithInputs,
|
||||
List<CompiledPolicy>? compiledPolicies,
|
||||
bool useSharedPolicies,
|
||||
@@ -208,10 +208,10 @@ namespace Benchmarks
|
||||
}
|
||||
|
||||
barrier.SignalAndWait();
|
||||
|
||||
|
||||
int evaluationCount = 0;
|
||||
var localEvaluationTime = TimeSpan.Zero;
|
||||
|
||||
|
||||
while (!stopExecution)
|
||||
{
|
||||
// Use different policy for each iteration
|
||||
@@ -226,23 +226,29 @@ namespace Benchmarks
|
||||
{
|
||||
// Measure only the evaluation call
|
||||
var evalStopwatch = Stopwatch.StartNew();
|
||||
|
||||
|
||||
if (useSharedPolicies)
|
||||
{
|
||||
var result = compiledPolicies![policyIdx].EvalWithInput(input);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compile policy in each iteration
|
||||
var modules = new[] { new PolicyModule { Id = "policy.rego", Content = policy } };
|
||||
// Compile policy in each iteration.
|
||||
var modules = new[] { new PolicyModule("policy.rego", policy) };
|
||||
var compiled = Compiler.CompilePolicyWithEntrypoint("{}", modules, "data.bench.allow");
|
||||
var result = compiled.EvalWithInput(input);
|
||||
compiled.Dispose();
|
||||
try
|
||||
{
|
||||
var result = compiled.EvalWithInput(input);
|
||||
}
|
||||
finally
|
||||
{
|
||||
DisposeCompiledPolicy(compiled);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
evalStopwatch.Stop();
|
||||
localEvaluationTime += evalStopwatch.Elapsed;
|
||||
|
||||
|
||||
// Track successful evaluations (only during actual benchmark, not warmup)
|
||||
if (!isWarmup)
|
||||
{
|
||||
@@ -256,10 +262,10 @@ namespace Benchmarks
|
||||
{
|
||||
// Ignore evaluation errors for benchmarking purposes
|
||||
}
|
||||
|
||||
|
||||
evaluationCount++;
|
||||
}
|
||||
|
||||
|
||||
// Store the actual evaluation time for this thread
|
||||
if (!isWarmup)
|
||||
{
|
||||
@@ -284,11 +290,23 @@ namespace Benchmarks
|
||||
|
||||
var totalEvaluations = policyCounters.Values.Sum();
|
||||
var totalEvaluationTime = evaluationTimes.Values.Aggregate(TimeSpan.Zero, (sum, time) => sum + time);
|
||||
|
||||
|
||||
// Use pure evaluation time (consistent with Rust benchmark)
|
||||
var evaluationTime = totalEvaluationTime == TimeSpan.Zero ? stopwatch.Elapsed : totalEvaluationTime;
|
||||
|
||||
|
||||
return (totalEvaluations, evaluationTime, policyCounters, allocatedBytes);
|
||||
}
|
||||
|
||||
private static void DisposeCompiledPolicy(CompiledPolicy policy)
|
||||
{
|
||||
try
|
||||
{
|
||||
policy.Dispose();
|
||||
}
|
||||
catch (TimeoutException ex)
|
||||
{
|
||||
Console.WriteLine($"Warning: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Benchmarks
|
||||
public class EngineEvaluationBenchmark
|
||||
{
|
||||
private static readonly string TestDataPath = Path.Combine(
|
||||
Directory.GetCurrentDirectory(),
|
||||
Directory.GetCurrentDirectory(),
|
||||
"..", "..", "..",
|
||||
"benches", "evaluation", "test_data"
|
||||
);
|
||||
@@ -33,7 +33,7 @@ namespace Benchmarks
|
||||
private static readonly string[] PolicyNames = new[]
|
||||
{
|
||||
"rbac_policy",
|
||||
"api_access_policy",
|
||||
"api_access_policy",
|
||||
"data_sensitivity_policy",
|
||||
"time_based_policy",
|
||||
"data_processing_policy",
|
||||
@@ -46,21 +46,21 @@ namespace Benchmarks
|
||||
private static List<(string Policy, string[] Inputs)> LoadPoliciesWithInputs()
|
||||
{
|
||||
var result = new List<(string Policy, string[] Inputs)>();
|
||||
|
||||
|
||||
foreach (var (policyFile, inputFiles) in PolicyInputFiles)
|
||||
{
|
||||
var policyPath = Path.Combine(TestDataPath, "policies", policyFile);
|
||||
var policy = File.ReadAllText(policyPath);
|
||||
|
||||
|
||||
var inputs = inputFiles.Select(inputFile =>
|
||||
{
|
||||
var inputPath = Path.Combine(TestDataPath, "inputs", inputFile);
|
||||
return File.ReadAllText(inputPath);
|
||||
}).ToArray();
|
||||
|
||||
|
||||
result.Add((policy, inputs));
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -68,12 +68,12 @@ namespace Benchmarks
|
||||
{
|
||||
var policiesWithInputs = LoadPoliciesWithInputs();
|
||||
var engines = new List<Engine>();
|
||||
|
||||
|
||||
foreach (var (policy, _) in policiesWithInputs)
|
||||
{
|
||||
var engine = new Engine();
|
||||
engine.AddPolicy("policy.rego", policy);
|
||||
|
||||
|
||||
// Warm up the engine to ensure it's fully prepared for evaluation
|
||||
// This prevents each cloned engine from repeating preparation work
|
||||
engine.SetInputJson("{}");
|
||||
@@ -85,10 +85,10 @@ namespace Benchmarks
|
||||
{
|
||||
// Ignore warmup errors
|
||||
}
|
||||
|
||||
|
||||
engines.Add(engine);
|
||||
}
|
||||
|
||||
|
||||
return engines;
|
||||
}
|
||||
|
||||
@@ -97,13 +97,13 @@ namespace Benchmarks
|
||||
var cpuCount = Environment.ProcessorCount;
|
||||
var maxThreads = cpuCount * 2;
|
||||
var threadCounts = new List<int> { 1, 2 };
|
||||
|
||||
|
||||
// Add even numbers from 4 to maxThreads
|
||||
for (int i = 4; i <= maxThreads; i += 2)
|
||||
{
|
||||
threadCounts.Add(i);
|
||||
}
|
||||
|
||||
|
||||
Console.WriteLine($"Running engine benchmark with max_threads: {maxThreads}");
|
||||
Console.WriteLine($"Testing with thread counts: {string.Join(", ", threadCounts)}");
|
||||
Console.WriteLine();
|
||||
@@ -132,14 +132,14 @@ namespace Benchmarks
|
||||
const int warmupSeconds = 3;
|
||||
const int durationSeconds = 3;
|
||||
var policiesWithInputs = LoadPoliciesWithInputs();
|
||||
|
||||
|
||||
Console.WriteLine($"Warming up with {threads} threads for {warmupSeconds} seconds...");
|
||||
|
||||
|
||||
// Warmup phase
|
||||
var (_, _, _) = RunBenchmarkPhase(threads, warmupSeconds, policiesWithInputs, useClonedEngines, isWarmup: true);
|
||||
|
||||
|
||||
Console.WriteLine($"Running benchmark with {threads} threads for {durationSeconds} seconds...");
|
||||
|
||||
|
||||
// Actual benchmark phase
|
||||
var (totalEvaluations, evaluationTime, policyCounters) = RunBenchmarkPhase(threads, durationSeconds, policiesWithInputs, useClonedEngines, isWarmup: false);
|
||||
|
||||
@@ -165,8 +165,8 @@ namespace Benchmarks
|
||||
}
|
||||
|
||||
private static (int totalEvaluations, TimeSpan evaluationTime, Dictionary<string, int> policyCounters) RunBenchmarkPhase(
|
||||
int threads,
|
||||
int durationSeconds,
|
||||
int threads,
|
||||
int durationSeconds,
|
||||
List<(string Policy, string[] Inputs)> policiesWithInputs,
|
||||
bool useClonedEngines,
|
||||
bool isWarmup)
|
||||
@@ -199,10 +199,10 @@ namespace Benchmarks
|
||||
tasks[threadId] = Task.Run(() =>
|
||||
{
|
||||
barrier.SignalAndWait();
|
||||
|
||||
|
||||
int evaluationCount = 0;
|
||||
var localEvaluationTime = TimeSpan.Zero;
|
||||
|
||||
|
||||
while (!stopExecution)
|
||||
{
|
||||
// Use different policy for each iteration
|
||||
@@ -217,7 +217,7 @@ namespace Benchmarks
|
||||
{
|
||||
// Measure only the engine operations
|
||||
var evalStopwatch = Stopwatch.StartNew();
|
||||
|
||||
|
||||
Engine engine;
|
||||
if (useClonedEngines)
|
||||
{
|
||||
@@ -228,14 +228,14 @@ namespace Benchmarks
|
||||
engine = new Engine();
|
||||
engine.AddPolicy("policy.rego", policy);
|
||||
}
|
||||
|
||||
|
||||
engine.SetInputJson(input);
|
||||
var result = engine.EvalRule("data.bench.allow");
|
||||
engine.Dispose();
|
||||
|
||||
|
||||
evalStopwatch.Stop();
|
||||
localEvaluationTime += evalStopwatch.Elapsed;
|
||||
|
||||
|
||||
// Track successful evaluations (only during actual benchmark, not warmup)
|
||||
if (!isWarmup)
|
||||
{
|
||||
@@ -249,10 +249,10 @@ namespace Benchmarks
|
||||
{
|
||||
// Ignore evaluation errors for benchmarking purposes
|
||||
}
|
||||
|
||||
|
||||
evaluationCount++;
|
||||
}
|
||||
|
||||
|
||||
// Store the actual evaluation time for this thread
|
||||
if (!isWarmup)
|
||||
{
|
||||
@@ -283,10 +283,10 @@ namespace Benchmarks
|
||||
|
||||
var totalEvaluations = policyCounters.Values.Sum();
|
||||
var totalEvaluationTime = evaluationTimes.Values.Aggregate(TimeSpan.Zero, (sum, time) => sum + time);
|
||||
|
||||
|
||||
// Use pure evaluation time (consistent with Rust benchmark)
|
||||
var evaluationTime = totalEvaluationTime == TimeSpan.Zero ? stopwatch.Elapsed : totalEvaluationTime;
|
||||
|
||||
|
||||
return (totalEvaluations, evaluationTime, policyCounters);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Benchmarks
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("=== Regorus C# Benchmarks ===\n");
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
Console.WriteLine("Running Engine Evaluation Benchmark...");
|
||||
@@ -17,9 +17,9 @@ namespace Benchmarks
|
||||
{
|
||||
Console.WriteLine($"Engine benchmark failed: {ex.Message}");
|
||||
}
|
||||
|
||||
|
||||
Console.WriteLine("\n" + new string('=', 80) + "\n");
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
Console.WriteLine("Running Compiled Policy Evaluation Benchmark...");
|
||||
@@ -29,7 +29,7 @@ namespace Benchmarks
|
||||
{
|
||||
Console.WriteLine($"Compiled policy benchmark failed: {ex.Message}");
|
||||
}
|
||||
|
||||
|
||||
Console.WriteLine("\n=== Benchmarks Complete ===");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
<RegorusPackageVersion>0.9.0</RegorusPackageVersion>
|
||||
<RegorusPackageVersion>0.9.1</RegorusPackageVersion>
|
||||
<RegorusPackageVersionSuffix Condition="'$(VersionSuffix)' != ''">-$(VersionSuffix)</RegorusPackageVersionSuffix>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -10,5 +10,6 @@
|
||||
<PackageVersion Include="Regorus" Version="$(RegorusPackageVersion)$(RegorusPackageVersionSuffix)" />
|
||||
<PackageVersion Include="MSTest" Version="3.8.2" />
|
||||
<PackageVersion Include="System.Text.Json" Version="8.0.5" />
|
||||
<PackageVersion Include="YamlDotNet" Version="13.7.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -104,3 +104,49 @@ vm.SetInputJson(Input);
|
||||
var result = vm.Execute();
|
||||
Console.WriteLine($"allow: {result}");
|
||||
```
|
||||
|
||||
## Azure RBAC Condition Evaluation
|
||||
|
||||
Evaluate Azure RBAC condition expressions directly with a JSON evaluation context:
|
||||
|
||||
```csharp
|
||||
using Regorus;
|
||||
|
||||
const string Condition = "@Resource[owner] StringEquals 'alice'";
|
||||
const string ContextJson = """
|
||||
{
|
||||
"principal": {
|
||||
"id": "user-1",
|
||||
"principal_type": "User",
|
||||
"custom_security_attributes": {}
|
||||
},
|
||||
"resource": {
|
||||
"id": "/subscriptions/s1",
|
||||
"resource_type": "Microsoft.Storage/storageAccounts",
|
||||
"scope": "/subscriptions/s1",
|
||||
"attributes": {
|
||||
"owner": "alice",
|
||||
"confidential": true
|
||||
}
|
||||
},
|
||||
"request": {
|
||||
"action": "Microsoft.Storage/storageAccounts/read",
|
||||
"data_action": null,
|
||||
"attributes": {
|
||||
"clientIP": "10.0.0.1"
|
||||
}
|
||||
},
|
||||
"environment": {
|
||||
"is_private_link": null,
|
||||
"private_endpoint": null,
|
||||
"subnet": null,
|
||||
"utc_now": "2023-05-01T12:00:00Z"
|
||||
},
|
||||
"action": "Microsoft.Storage/storageAccounts/read",
|
||||
"suboperation": null
|
||||
}
|
||||
""";
|
||||
|
||||
var allowed = RbacEngine.EvaluateCondition(Condition, ContextJson);
|
||||
Console.WriteLine($"RBAC condition allowed: {allowed}");
|
||||
```
|
||||
|
||||
191
bindings/csharp/Regorus.Tests/AliasRegistryTests.cs
Normal file
191
bindings/csharp/Regorus.Tests/AliasRegistryTests.cs
Normal file
@@ -0,0 +1,191 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Regorus;
|
||||
|
||||
namespace Regorus.Tests;
|
||||
|
||||
[TestClass]
|
||||
public class AliasRegistryTests
|
||||
{
|
||||
private const string AliasesJson = @"[{
|
||||
""namespace"": ""Microsoft.Storage"",
|
||||
""resourceTypes"": [{
|
||||
""resourceType"": ""storageAccounts"",
|
||||
""aliases"": [{
|
||||
""name"": ""Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly"",
|
||||
""defaultPath"": ""properties.supportsHttpsTrafficOnly"",
|
||||
""paths"": []
|
||||
}, {
|
||||
""name"": ""Microsoft.Storage/storageAccounts/accessTier"",
|
||||
""defaultPath"": ""properties.accessTier"",
|
||||
""paths"": []
|
||||
}]
|
||||
}]
|
||||
}]";
|
||||
|
||||
private const string ManifestJson = @"{
|
||||
""dataNamespace"": ""Microsoft.KeyVault.Data"",
|
||||
""aliases"": [],
|
||||
""resourceTypeAliases"": [{
|
||||
""resourceType"": ""vaults/certificates"",
|
||||
""aliases"": [{
|
||||
""name"": ""Microsoft.KeyVault.Data/vaults/certificates/keySize"",
|
||||
""paths"": [{ ""path"": ""keySize"", ""apiVersions"": [""7.0""] }]
|
||||
}]
|
||||
}]
|
||||
}";
|
||||
|
||||
[TestMethod]
|
||||
public void Create_and_dispose_succeeds()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
Assert.AreEqual(0, registry.Length);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void LoadJson_populates_registry()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadJson(AliasesJson);
|
||||
Assert.AreEqual(1, registry.Length);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void LoadManifest_populates_registry()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadManifest(ManifestJson);
|
||||
Assert.AreEqual(1, registry.Length);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void NormalizeAndWrap_produces_envelope()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadJson(AliasesJson);
|
||||
|
||||
var resource = @"{
|
||||
""name"": ""acct1"",
|
||||
""type"": ""Microsoft.Storage/storageAccounts"",
|
||||
""properties"": { ""supportsHttpsTrafficOnly"": true, ""accessTier"": ""Hot"" }
|
||||
}";
|
||||
|
||||
var result = registry.NormalizeAndWrap(resource, "2023-01-01", "{}", "{}");
|
||||
Assert.IsNotNull(result);
|
||||
|
||||
var envelope = JsonNode.Parse(result!)!;
|
||||
Assert.IsNotNull(envelope["resource"]);
|
||||
Assert.IsNotNull(envelope["parameters"]);
|
||||
Assert.IsNotNull(envelope["context"]);
|
||||
|
||||
// Normalized resource should have lowercased alias field names
|
||||
var res = envelope["resource"]!;
|
||||
Assert.AreEqual(true, res["supportshttpstrafficonly"]?.GetValue<bool>());
|
||||
Assert.AreEqual("Hot", res["accesstier"]?.GetValue<string>());
|
||||
Assert.AreEqual("acct1", res["name"]?.GetValue<string>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void NormalizeAndWrap_with_context_and_parameters()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadJson(AliasesJson);
|
||||
|
||||
var resource = @"{
|
||||
""name"": ""acct1"",
|
||||
""type"": ""Microsoft.Storage/storageAccounts"",
|
||||
""properties"": { ""supportsHttpsTrafficOnly"": true }
|
||||
}";
|
||||
var context = @"{""resourceGroup"": {""name"": ""rg1""}}";
|
||||
var parameters = @"{""env"": ""prod""}";
|
||||
|
||||
var result = registry.NormalizeAndWrap(resource, "2023-01-01", context, parameters);
|
||||
Assert.IsNotNull(result);
|
||||
|
||||
var envelope = JsonNode.Parse(result!)!;
|
||||
Assert.AreEqual("rg1", envelope["context"]!["resourceGroup"]!["name"]?.GetValue<string>());
|
||||
Assert.AreEqual("prod", envelope["parameters"]!["env"]?.GetValue<string>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Denormalize_restores_properties()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadJson(AliasesJson);
|
||||
|
||||
var normalized = @"{
|
||||
""name"": ""acct1"",
|
||||
""type"": ""Microsoft.Storage/storageAccounts"",
|
||||
""supportshttpstrafficonly"": true,
|
||||
""accesstier"": ""Hot""
|
||||
}";
|
||||
|
||||
var result = registry.Denormalize(normalized, "2023-01-01");
|
||||
Assert.IsNotNull(result);
|
||||
|
||||
var arm = JsonNode.Parse(result!)!;
|
||||
Assert.AreEqual("acct1", arm["name"]?.GetValue<string>());
|
||||
Assert.AreEqual(true, arm["properties"]!["supportsHttpsTrafficOnly"]?.GetValue<bool>());
|
||||
Assert.AreEqual("Hot", arm["properties"]!["accessTier"]?.GetValue<string>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Round_trip_normalize_then_denormalize()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadJson(AliasesJson);
|
||||
|
||||
var resource = @"{
|
||||
""name"": ""acct1"",
|
||||
""type"": ""Microsoft.Storage/storageAccounts"",
|
||||
""properties"": { ""supportsHttpsTrafficOnly"": true, ""accessTier"": ""Hot"" }
|
||||
}";
|
||||
|
||||
// Normalize
|
||||
var envelopeJson = registry.NormalizeAndWrap(resource, "2023-01-01", "{}", "{}");
|
||||
Assert.IsNotNull(envelopeJson);
|
||||
|
||||
var envelope = JsonNode.Parse(envelopeJson!)!;
|
||||
var normalizedResource = envelope["resource"]!.ToJsonString();
|
||||
|
||||
// Denormalize
|
||||
var armJson = registry.Denormalize(normalizedResource, "2023-01-01");
|
||||
Assert.IsNotNull(armJson);
|
||||
|
||||
var arm = JsonNode.Parse(armJson!)!;
|
||||
Assert.AreEqual(true, arm["properties"]!["supportsHttpsTrafficOnly"]?.GetValue<bool>());
|
||||
Assert.AreEqual("Hot", arm["properties"]!["accessTier"]?.GetValue<string>());
|
||||
Assert.AreEqual("acct1", arm["name"]?.GetValue<string>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DataPlane_manifest_normalize()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadManifest(ManifestJson);
|
||||
|
||||
var resource = @"{
|
||||
""type"": ""Microsoft.KeyVault.Data/vaults/certificates"",
|
||||
""keySize"": 2048
|
||||
}";
|
||||
|
||||
var result = registry.NormalizeAndWrap(resource, "7.0", "{}", "{}");
|
||||
Assert.IsNotNull(result);
|
||||
|
||||
var envelope = JsonNode.Parse(result!)!;
|
||||
Assert.AreEqual(2048, envelope["resource"]!["keysize"]?.GetValue<int>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void LoadJson_invalid_throws()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadJson("not valid json");
|
||||
}
|
||||
}
|
||||
320
bindings/csharp/Regorus.Tests/MemoryGrowthTests.cs
Normal file
320
bindings/csharp/Regorus.Tests/MemoryGrowthTests.cs
Normal file
@@ -0,0 +1,320 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Regorus;
|
||||
|
||||
namespace Regorus.Tests;
|
||||
|
||||
[TestClass]
|
||||
[DoNotParallelize]
|
||||
public class MemoryGrowthTests
|
||||
{
|
||||
private static int Iterations =>
|
||||
int.TryParse(Environment.GetEnvironmentVariable("REGORUS_MEMORY_TEST_ITERS"), out var value) ? value : 50_000;
|
||||
|
||||
private static int LogEvery =>
|
||||
int.TryParse(Environment.GetEnvironmentVariable("REGORUS_MEMORY_TEST_LOG_EVERY"), out var value) ? value : 500;
|
||||
|
||||
private static int GcEvery
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!int.TryParse(Environment.GetEnvironmentVariable("REGORUS_MEMORY_TEST_GC_EVERY"), out var value))
|
||||
{
|
||||
value = LogEvery;
|
||||
}
|
||||
|
||||
return value <= 0 ? LogEvery : value;
|
||||
}
|
||||
}
|
||||
|
||||
private static long? MaxWorkingSetDeltaBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!long.TryParse(Environment.GetEnvironmentVariable("REGORUS_MEMORY_TEST_MAX_DELTA_MB"), out var mb))
|
||||
{
|
||||
mb = 32;
|
||||
}
|
||||
|
||||
if (mb <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return mb * 1024L * 1024L;
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong? GlobalRegorusMemoryLimitBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!ulong.TryParse(Environment.GetEnvironmentVariable("REGORUS_MEMORY_TEST_GLOBAL_REGORUS_LIMIT_MB"), out var mb))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mb == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return mb * 1024UL * 1024UL;
|
||||
}
|
||||
}
|
||||
|
||||
private static void WithOptionalGlobalRegorusMemoryLimit(Action action)
|
||||
{
|
||||
var priorLimit = MemoryLimits.GetGlobalMemoryLimit();
|
||||
try
|
||||
{
|
||||
if (GlobalRegorusMemoryLimitBytes is { } limit)
|
||||
{
|
||||
MemoryLimits.SetGlobalMemoryLimit(limit);
|
||||
}
|
||||
|
||||
action();
|
||||
}
|
||||
finally
|
||||
{
|
||||
MemoryLimits.SetGlobalMemoryLimit(priorLimit);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ForceFullGc()
|
||||
{
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void Engine_create_eval_dispose_does_not_grow_working_set()
|
||||
{
|
||||
WithOptionalGlobalRegorusMemoryLimit(() =>
|
||||
{
|
||||
var process = Process.GetCurrentProcess();
|
||||
process.Refresh();
|
||||
var baseline = process.WorkingSet64;
|
||||
var maxDelta = 0L;
|
||||
var baselineManaged = GC.GetTotalMemory(false);
|
||||
var maxManagedDelta = 0L;
|
||||
|
||||
for (var i = 1; i <= Iterations; i++)
|
||||
{
|
||||
using (var engine = new Engine())
|
||||
{
|
||||
engine.AddPolicy("test.rego", "package test\nx = 1\nmessage = `Hello`");
|
||||
_ = engine.EvalRule("data.test.message");
|
||||
}
|
||||
|
||||
if (i % LogEvery == 0)
|
||||
{
|
||||
process.Refresh();
|
||||
var workingSet = process.WorkingSet64;
|
||||
var managed = GC.GetTotalMemory(false);
|
||||
var delta = workingSet - baseline;
|
||||
var managedDelta = managed - baselineManaged;
|
||||
if (delta > maxDelta)
|
||||
{
|
||||
maxDelta = delta;
|
||||
}
|
||||
if (managedDelta > maxManagedDelta)
|
||||
{
|
||||
maxManagedDelta = managedDelta;
|
||||
}
|
||||
Console.WriteLine($"\n\n\u001b[1m{i} ws_mb={workingSet / 1048576.0:F1} managed_mb={managed / 1048576.0:F1} delta_mb={delta / 1048576.0:F1}\u001b[0m\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (MaxWorkingSetDeltaBytes is { } limit)
|
||||
{
|
||||
Console.WriteLine($"\n\n\u001b[1mSUMMARY: max ws delta {maxDelta / 1048576.0:F1} MB (limit {limit / 1048576.0:F1} MB); max managed delta {maxManagedDelta / 1048576.0:F1} MB.\u001b[0m\n\n");
|
||||
Assert.IsTrue(
|
||||
maxDelta <= limit,
|
||||
$"Working set grew by {maxDelta / 1048576.0:F1} MB (limit {limit / 1048576.0:F1} MB). Managed heap max delta {maxManagedDelta / 1048576.0:F1} MB.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Engine_create_eval_finalize_does_not_grow_working_set()
|
||||
{
|
||||
WithOptionalGlobalRegorusMemoryLimit(() =>
|
||||
{
|
||||
var process = Process.GetCurrentProcess();
|
||||
process.Refresh();
|
||||
var baseline = process.WorkingSet64;
|
||||
var maxDelta = 0L;
|
||||
var baselineManaged = GC.GetTotalMemory(false);
|
||||
var maxManagedDelta = 0L;
|
||||
|
||||
for (var i = 1; i <= Iterations; i++)
|
||||
{
|
||||
var engine = new Engine();
|
||||
engine.AddPolicy("test.rego", "package test\nx = 1\nmessage = `Hello`");
|
||||
_ = engine.EvalRule("data.test.message");
|
||||
|
||||
if (i % GcEvery == 0)
|
||||
{
|
||||
ForceFullGc();
|
||||
}
|
||||
|
||||
if (i % LogEvery == 0)
|
||||
{
|
||||
process.Refresh();
|
||||
var workingSet = process.WorkingSet64;
|
||||
var managed = GC.GetTotalMemory(false);
|
||||
var delta = workingSet - baseline;
|
||||
var managedDelta = managed - baselineManaged;
|
||||
if (delta > maxDelta)
|
||||
{
|
||||
maxDelta = delta;
|
||||
}
|
||||
if (managedDelta > maxManagedDelta)
|
||||
{
|
||||
maxManagedDelta = managedDelta;
|
||||
}
|
||||
Console.WriteLine($"\n\n\u001b[1m{i} ws_mb={workingSet / 1048576.0:F1} managed_mb={managed / 1048576.0:F1} delta_mb={delta / 1048576.0:F1}\u001b[0m\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (MaxWorkingSetDeltaBytes is { } limit)
|
||||
{
|
||||
Console.WriteLine($"\n\n\u001b[1mSUMMARY: max ws delta {maxDelta / 1048576.0:F1} MB (limit {limit / 1048576.0:F1} MB); max managed delta {maxManagedDelta / 1048576.0:F1} MB.\u001b[0m\n\n");
|
||||
Assert.IsTrue(
|
||||
maxDelta <= limit,
|
||||
$"Working set grew by {maxDelta / 1048576.0:F1} MB (limit {limit / 1048576.0:F1} MB). Managed heap max delta {maxManagedDelta / 1048576.0:F1} MB.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void Rvm_rehydrate_execute_dispose_does_not_grow_working_set()
|
||||
{
|
||||
WithOptionalGlobalRegorusMemoryLimit(() =>
|
||||
{
|
||||
var modules = new[]
|
||||
{
|
||||
new PolicyModule("test.rego", "package test\nallow = true"),
|
||||
};
|
||||
|
||||
using var compiled = Program.CompileFromModules("{}", modules, new[] { "data.test.allow" });
|
||||
var serialized = compiled.SerializeBinary();
|
||||
|
||||
var process = Process.GetCurrentProcess();
|
||||
process.Refresh();
|
||||
var baseline = process.WorkingSet64;
|
||||
var maxDelta = 0L;
|
||||
var baselineManaged = GC.GetTotalMemory(false);
|
||||
var maxManagedDelta = 0L;
|
||||
|
||||
for (var i = 1; i <= Iterations; i++)
|
||||
{
|
||||
using (var vm = new Rvm())
|
||||
using (var program = Program.DeserializeBinary(serialized, out _))
|
||||
{
|
||||
vm.LoadProgram(program);
|
||||
vm.SetDataJson("{}");
|
||||
vm.SetInputJson("{}");
|
||||
_ = vm.ExecuteEntryPoint(0);
|
||||
}
|
||||
|
||||
if (i % LogEvery == 0)
|
||||
{
|
||||
process.Refresh();
|
||||
var workingSet = process.WorkingSet64;
|
||||
var managed = GC.GetTotalMemory(false);
|
||||
var delta = workingSet - baseline;
|
||||
var managedDelta = managed - baselineManaged;
|
||||
if (delta > maxDelta)
|
||||
{
|
||||
maxDelta = delta;
|
||||
}
|
||||
if (managedDelta > maxManagedDelta)
|
||||
{
|
||||
maxManagedDelta = managedDelta;
|
||||
}
|
||||
Console.WriteLine($"\n\n\u001b[1m{i} ws_mb={workingSet / 1048576.0:F1} managed_mb={managed / 1048576.0:F1} delta_mb={delta / 1048576.0:F1}\u001b[0m\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (MaxWorkingSetDeltaBytes is { } limit)
|
||||
{
|
||||
Console.WriteLine($"\n\n\u001b[1mSUMMARY: max ws delta {maxDelta / 1048576.0:F1} MB (limit {limit / 1048576.0:F1} MB); max managed delta {maxManagedDelta / 1048576.0:F1} MB.\u001b[0m\n\n");
|
||||
Assert.IsTrue(
|
||||
maxDelta <= limit,
|
||||
$"Working set grew by {maxDelta / 1048576.0:F1} MB (limit {limit / 1048576.0:F1} MB). Managed heap max delta {maxManagedDelta / 1048576.0:F1} MB.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Rvm_rehydrate_execute_finalize_does_not_grow_working_set()
|
||||
{
|
||||
WithOptionalGlobalRegorusMemoryLimit(() =>
|
||||
{
|
||||
var modules = new[]
|
||||
{
|
||||
new PolicyModule("test.rego", "package test\nallow = true"),
|
||||
};
|
||||
|
||||
using var compiled = Program.CompileFromModules("{}", modules, new[] { "data.test.allow" });
|
||||
var serialized = compiled.SerializeBinary();
|
||||
|
||||
var process = Process.GetCurrentProcess();
|
||||
process.Refresh();
|
||||
var baseline = process.WorkingSet64;
|
||||
var maxDelta = 0L;
|
||||
var baselineManaged = GC.GetTotalMemory(false);
|
||||
var maxManagedDelta = 0L;
|
||||
|
||||
for (var i = 1; i <= Iterations; i++)
|
||||
{
|
||||
var vm = new Rvm();
|
||||
var program = Program.DeserializeBinary(serialized, out _);
|
||||
vm.LoadProgram(program);
|
||||
vm.SetDataJson("{}");
|
||||
vm.SetInputJson("{}");
|
||||
_ = vm.ExecuteEntryPoint(0);
|
||||
|
||||
if (i % GcEvery == 0)
|
||||
{
|
||||
ForceFullGc();
|
||||
}
|
||||
|
||||
if (i % LogEvery == 0)
|
||||
{
|
||||
process.Refresh();
|
||||
var workingSet = process.WorkingSet64;
|
||||
var managed = GC.GetTotalMemory(false);
|
||||
var delta = workingSet - baseline;
|
||||
var managedDelta = managed - baselineManaged;
|
||||
if (delta > maxDelta)
|
||||
{
|
||||
maxDelta = delta;
|
||||
}
|
||||
if (managedDelta > maxManagedDelta)
|
||||
{
|
||||
maxManagedDelta = managedDelta;
|
||||
}
|
||||
Console.WriteLine($"\n\n\u001b[1m{i} ws_mb={workingSet / 1048576.0:F1} managed_mb={managed / 1048576.0:F1} delta_mb={delta / 1048576.0:F1}\u001b[0m\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (MaxWorkingSetDeltaBytes is { } limit)
|
||||
{
|
||||
Console.WriteLine($"\n\n\u001b[1mSUMMARY: max ws delta {maxDelta / 1048576.0:F1} MB (limit {limit / 1048576.0:F1} MB); max managed delta {maxManagedDelta / 1048576.0:F1} MB.\u001b[0m\n\n");
|
||||
Assert.IsTrue(
|
||||
maxDelta <= limit,
|
||||
$"Working set grew by {maxDelta / 1048576.0:F1} MB (limit {limit / 1048576.0:F1} MB). Managed heap max delta {maxManagedDelta / 1048576.0:F1} MB.");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
374
bindings/csharp/Regorus.Tests/RbacEngineTests.cs
Normal file
374
bindings/csharp/Regorus.Tests/RbacEngineTests.cs
Normal file
@@ -0,0 +1,374 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Regorus;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
namespace Regorus.Tests;
|
||||
|
||||
[TestClass]
|
||||
public class RbacEngineTests
|
||||
{
|
||||
public TestContext? TestContext { get; set; }
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
WriteIndented = false
|
||||
};
|
||||
|
||||
private const string BaseContextJson = """
|
||||
{
|
||||
"principal": {
|
||||
"id": "user-1",
|
||||
"principal_type": "User",
|
||||
"custom_security_attributes": {
|
||||
"department": "eng",
|
||||
"levels": ["L1", "L2"]
|
||||
}
|
||||
},
|
||||
"resource": {
|
||||
"id": "/subscriptions/s1",
|
||||
"resource_type": "Microsoft.Storage/storageAccounts",
|
||||
"scope": "/subscriptions/s1",
|
||||
"attributes": {
|
||||
"owner": "alice",
|
||||
"tags": ["a", "b"],
|
||||
"count": 5,
|
||||
"enabled": false,
|
||||
"ip": "10.0.0.5",
|
||||
"guid": "a1b2c3d4-0000-0000-0000-000000000000"
|
||||
}
|
||||
},
|
||||
"request": {
|
||||
"action": "Microsoft.Storage/storageAccounts/read",
|
||||
"data_action": "Microsoft.Storage/storageAccounts/read",
|
||||
"attributes": {
|
||||
"owner": "alice",
|
||||
"text": "HelloWorld",
|
||||
"tags": ["prod", "gold"],
|
||||
"count": 10,
|
||||
"ratio": 2.5,
|
||||
"enabled": true,
|
||||
"ip": "10.0.0.8",
|
||||
"guid": "A1B2C3D4-0000-0000-0000-000000000000",
|
||||
"time": "12:30:15",
|
||||
"date": "2023-05-01T12:00:00Z",
|
||||
"numbers": [1, 2, 3],
|
||||
"letters": ["a", "b"]
|
||||
}
|
||||
},
|
||||
"environment": {
|
||||
"is_private_link": false,
|
||||
"private_endpoint": null,
|
||||
"subnet": null,
|
||||
"utc_now": "2023-05-01T12:00:00Z"
|
||||
},
|
||||
"action": "Microsoft.Storage/storageAccounts/read",
|
||||
"suboperation": "sub/read"
|
||||
}
|
||||
""";
|
||||
|
||||
[TestMethod]
|
||||
public void Rbac_engine_evaluates_all_yaml_cases()
|
||||
{
|
||||
var cases = LoadEvalTestCases().ToList();
|
||||
Assert.IsTrue(cases.Count > 0, "No RBAC test cases were loaded.");
|
||||
|
||||
foreach (var testCase in cases)
|
||||
{
|
||||
TestContext?.WriteLine($"RBAC case: {testCase.Name} -> {testCase.Condition}");
|
||||
var context = BuildBaseContext();
|
||||
if (testCase.Context != null)
|
||||
{
|
||||
ApplyOverrides(context, testCase.Context);
|
||||
}
|
||||
|
||||
var contextJson = context.ToJsonString(JsonOptions);
|
||||
var result = RbacEngine.EvaluateCondition(testCase.Condition, contextJson);
|
||||
|
||||
Assert.AreEqual(
|
||||
testCase.Expected,
|
||||
result,
|
||||
$"RBAC test '{testCase.Name}' failed for condition '{testCase.Condition}'.");
|
||||
}
|
||||
}
|
||||
|
||||
private static JsonObject BuildBaseContext()
|
||||
{
|
||||
var node = JsonNode.Parse(BaseContextJson) as JsonObject;
|
||||
if (node is null)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to parse base context JSON.");
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private static void ApplyOverrides(JsonObject context, EvalContextOverrides overrides)
|
||||
{
|
||||
var principal = (JsonObject?)context["principal"]
|
||||
?? throw new InvalidOperationException("Missing principal section.");
|
||||
var resource = (JsonObject?)context["resource"]
|
||||
?? throw new InvalidOperationException("Missing resource section.");
|
||||
var request = (JsonObject?)context["request"]
|
||||
?? throw new InvalidOperationException("Missing request section.");
|
||||
var environment = (JsonObject?)context["environment"]
|
||||
?? throw new InvalidOperationException("Missing environment section.");
|
||||
|
||||
if (!string.IsNullOrEmpty(overrides.Action))
|
||||
{
|
||||
context["action"] = overrides.Action;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(overrides.Suboperation))
|
||||
{
|
||||
context["suboperation"] = overrides.Suboperation;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(overrides.RequestAction))
|
||||
{
|
||||
request["action"] = overrides.RequestAction;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(overrides.DataAction))
|
||||
{
|
||||
request["data_action"] = overrides.DataAction;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(overrides.PrincipalId))
|
||||
{
|
||||
principal["id"] = overrides.PrincipalId;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(overrides.PrincipalType))
|
||||
{
|
||||
principal["principal_type"] = overrides.PrincipalType;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(overrides.ResourceId))
|
||||
{
|
||||
resource["id"] = overrides.ResourceId;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(overrides.ResourceType))
|
||||
{
|
||||
resource["resource_type"] = overrides.ResourceType;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(overrides.ResourceScope))
|
||||
{
|
||||
resource["scope"] = overrides.ResourceScope;
|
||||
}
|
||||
|
||||
if (overrides.RequestAttributes != null)
|
||||
{
|
||||
request["attributes"] = ConvertToJsonNode(overrides.RequestAttributes);
|
||||
}
|
||||
|
||||
if (overrides.ResourceAttributes != null)
|
||||
{
|
||||
resource["attributes"] = ConvertToJsonNode(overrides.ResourceAttributes);
|
||||
}
|
||||
|
||||
if (overrides.PrincipalCustomSecurityAttributes != null)
|
||||
{
|
||||
principal["custom_security_attributes"] = ConvertToJsonNode(overrides.PrincipalCustomSecurityAttributes);
|
||||
}
|
||||
|
||||
if (overrides.Environment != null)
|
||||
{
|
||||
if (overrides.Environment.IsPrivateLink.HasValue)
|
||||
{
|
||||
environment["is_private_link"] = overrides.Environment.IsPrivateLink.Value;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(overrides.Environment.PrivateEndpoint))
|
||||
{
|
||||
environment["private_endpoint"] = overrides.Environment.PrivateEndpoint;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(overrides.Environment.Subnet))
|
||||
{
|
||||
environment["subnet"] = overrides.Environment.Subnet;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(overrides.Environment.UtcNow))
|
||||
{
|
||||
environment["utc_now"] = overrides.Environment.UtcNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<EvalTestCase> LoadEvalTestCases()
|
||||
{
|
||||
var baseDir = Path.Combine(AppContext.BaseDirectory, "test_cases");
|
||||
if (!Directory.Exists(baseDir))
|
||||
{
|
||||
throw new DirectoryNotFoundException($"RBAC test case directory not found: {baseDir}");
|
||||
}
|
||||
|
||||
var deserializer = new DeserializerBuilder()
|
||||
.IgnoreUnmatchedProperties()
|
||||
.Build();
|
||||
|
||||
var files = Directory.EnumerateFiles(baseDir, "*.yaml")
|
||||
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
var yaml = File.ReadAllText(file);
|
||||
var suite = deserializer.Deserialize<EvalTestSuite>(yaml);
|
||||
if (suite?.TestCases is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var testCase in suite.TestCases)
|
||||
{
|
||||
yield return testCase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static JsonNode? ConvertToJsonNode(object? value)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case JsonNode node:
|
||||
return node;
|
||||
case string text:
|
||||
return JsonValue.Create(text);
|
||||
case bool boolean:
|
||||
return JsonValue.Create(boolean);
|
||||
case int intValue:
|
||||
return JsonValue.Create(intValue);
|
||||
case long longValue:
|
||||
return JsonValue.Create(longValue);
|
||||
case double doubleValue:
|
||||
return JsonValue.Create(doubleValue);
|
||||
case float floatValue:
|
||||
return JsonValue.Create(floatValue);
|
||||
case decimal decimalValue:
|
||||
return JsonValue.Create(decimalValue);
|
||||
case DateTime dateTime:
|
||||
return JsonValue.Create(dateTime.ToString("O"));
|
||||
case IDictionary dictionary:
|
||||
{
|
||||
var obj = new JsonObject();
|
||||
foreach (DictionaryEntry entry in dictionary)
|
||||
{
|
||||
var key = entry.Key?.ToString() ?? string.Empty;
|
||||
obj[key] = ConvertToJsonNode(entry.Value);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
case IEnumerable enumerable:
|
||||
{
|
||||
if (value is string)
|
||||
{
|
||||
return JsonValue.Create(value.ToString());
|
||||
}
|
||||
|
||||
var array = new JsonArray();
|
||||
foreach (var item in enumerable)
|
||||
{
|
||||
array.Add(ConvertToJsonNode(item));
|
||||
}
|
||||
return array;
|
||||
}
|
||||
default:
|
||||
return JsonValue.Create(value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class EvalTestSuite
|
||||
{
|
||||
[YamlMember(Alias = "test_cases")]
|
||||
public List<EvalTestCase> TestCases { get; set; } = new();
|
||||
}
|
||||
|
||||
private sealed class EvalTestCase
|
||||
{
|
||||
[YamlMember(Alias = "name")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[YamlMember(Alias = "condition")]
|
||||
public string Condition { get; set; } = string.Empty;
|
||||
|
||||
[YamlMember(Alias = "expected")]
|
||||
public bool Expected { get; set; }
|
||||
|
||||
[YamlMember(Alias = "context")]
|
||||
public EvalContextOverrides? Context { get; set; }
|
||||
}
|
||||
|
||||
private sealed class EvalContextOverrides
|
||||
{
|
||||
[YamlMember(Alias = "action")]
|
||||
public string? Action { get; set; }
|
||||
|
||||
[YamlMember(Alias = "suboperation")]
|
||||
public string? Suboperation { get; set; }
|
||||
|
||||
[YamlMember(Alias = "request_action")]
|
||||
public string? RequestAction { get; set; }
|
||||
|
||||
[YamlMember(Alias = "data_action")]
|
||||
public string? DataAction { get; set; }
|
||||
|
||||
[YamlMember(Alias = "principal_id")]
|
||||
public string? PrincipalId { get; set; }
|
||||
|
||||
[YamlMember(Alias = "principal_type")]
|
||||
public string? PrincipalType { get; set; }
|
||||
|
||||
[YamlMember(Alias = "resource_id")]
|
||||
public string? ResourceId { get; set; }
|
||||
|
||||
[YamlMember(Alias = "resource_type")]
|
||||
public string? ResourceType { get; set; }
|
||||
|
||||
[YamlMember(Alias = "resource_scope")]
|
||||
public string? ResourceScope { get; set; }
|
||||
|
||||
[YamlMember(Alias = "request_attributes")]
|
||||
public object? RequestAttributes { get; set; }
|
||||
|
||||
[YamlMember(Alias = "resource_attributes")]
|
||||
public object? ResourceAttributes { get; set; }
|
||||
|
||||
[YamlMember(Alias = "principal_custom_security_attributes")]
|
||||
public object? PrincipalCustomSecurityAttributes { get; set; }
|
||||
|
||||
[YamlMember(Alias = "environment")]
|
||||
public EvalEnvironmentOverrides? Environment { get; set; }
|
||||
}
|
||||
|
||||
private sealed class EvalEnvironmentOverrides
|
||||
{
|
||||
[YamlMember(Alias = "is_private_link")]
|
||||
public bool? IsPrivateLink { get; set; }
|
||||
|
||||
[YamlMember(Alias = "private_endpoint")]
|
||||
public string? PrivateEndpoint { get; set; }
|
||||
|
||||
[YamlMember(Alias = "subnet")]
|
||||
public string? Subnet { get; set; }
|
||||
|
||||
[YamlMember(Alias = "utc_now")]
|
||||
public string? UtcNow { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -20,9 +20,14 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MSTest" />
|
||||
<PackageReference Include="YamlDotNet" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Regorus" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="../../../src/languages/azure_rbac/test_cases/*.yaml" Link="test_cases/%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -193,10 +193,19 @@ public class RegorusTests
|
||||
|
||||
var result = engine.GetPolicyPackageNames();
|
||||
|
||||
var packageNames = JsonNode.Parse(result!);
|
||||
Assert.IsNotNull(result);
|
||||
|
||||
Assert.AreEqual("test", packageNames![0]["package_name"].ToString());
|
||||
Assert.AreEqual("test.nested.name", packageNames![1]["package_name"].ToString());
|
||||
var packageNames = JsonNode.Parse(result);
|
||||
Assert.IsNotNull(packageNames);
|
||||
|
||||
var packageArray = packageNames.AsArray();
|
||||
var firstPackage = packageArray[0]?.AsObject();
|
||||
var secondPackage = packageArray[1]?.AsObject();
|
||||
|
||||
Assert.IsNotNull(firstPackage);
|
||||
Assert.IsNotNull(secondPackage);
|
||||
Assert.AreEqual("test", firstPackage!["package_name"]!.GetValue<string>());
|
||||
Assert.AreEqual("test.nested.name", secondPackage!["package_name"]!.GetValue<string>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -209,71 +218,84 @@ public class RegorusTests
|
||||
|
||||
var result = engine.GetPolicyParameters();
|
||||
|
||||
var parameters = JsonNode.Parse(result!);
|
||||
Assert.IsNotNull(result);
|
||||
|
||||
Assert.AreEqual(1, parameters![0]["parameters"].AsArray().Count);
|
||||
Assert.AreEqual(1, parameters![0]["modifiers"].AsArray().Count);
|
||||
var parameters = JsonNode.Parse(result);
|
||||
Assert.IsNotNull(parameters);
|
||||
|
||||
Assert.AreEqual("a", parameters![0]["parameters"][0]["name"].ToString());
|
||||
Assert.AreEqual("b", parameters![0]["modifiers"][0]["name"].ToString());
|
||||
var parametersArray = parameters.AsArray();
|
||||
var firstEntry = parametersArray[0]?.AsObject();
|
||||
Assert.IsNotNull(firstEntry);
|
||||
|
||||
var parameterList = firstEntry!["parameters"]!.AsArray();
|
||||
var modifierList = firstEntry["modifiers"]!.AsArray();
|
||||
|
||||
Assert.AreEqual(1, parameterList.Count);
|
||||
Assert.AreEqual(1, modifierList.Count);
|
||||
|
||||
var parameterName = parameterList[0]?.AsObject()?["name"]?.GetValue<string>();
|
||||
var modifierName = modifierList[0]?.AsObject()?["name"]?.GetValue<string>();
|
||||
|
||||
Assert.AreEqual("a", parameterName);
|
||||
Assert.AreEqual("b", modifierName);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Global_memory_limit_can_be_set_and_cleared()
|
||||
{
|
||||
[TestMethod]
|
||||
public void Global_memory_limit_can_be_set_and_cleared()
|
||||
{
|
||||
lock (LimitLock)
|
||||
{
|
||||
using var guard = new MemoryLimitScope();
|
||||
using var guard = new MemoryLimitScope();
|
||||
|
||||
MemoryLimits.SetGlobalMemoryLimit(null);
|
||||
Assert.IsNull(MemoryLimits.GetGlobalMemoryLimit());
|
||||
|
||||
const ulong limit = 32 * 1024;
|
||||
MemoryLimits.SetGlobalMemoryLimit(limit);
|
||||
Assert.AreEqual(limit, MemoryLimits.GetGlobalMemoryLimit());
|
||||
|
||||
MemoryLimits.SetGlobalMemoryLimit(null);
|
||||
Assert.IsNull(MemoryLimits.GetGlobalMemoryLimit());
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Memory_limit_violations_surface_from_engine_calls()
|
||||
{
|
||||
lock (LimitLock)
|
||||
{
|
||||
using var guard = new MemoryLimitScope();
|
||||
using var engine = new Engine();
|
||||
|
||||
const ulong limit = 1;
|
||||
var payload = new string('x', 128 * 1024);
|
||||
|
||||
MemoryLimits.FlushThreadMemoryCounters();
|
||||
MemoryLimits.SetGlobalMemoryLimit(limit);
|
||||
|
||||
try
|
||||
{
|
||||
var ex = Assert.ThrowsException<InvalidOperationException>(
|
||||
() => engine.SetInputJson($"{{\"payload\":\"{payload}\"}}"));
|
||||
StringAssert.Contains(ex.Message, "execution exceeded memory limit");
|
||||
}
|
||||
finally
|
||||
{
|
||||
MemoryLimits.SetGlobalMemoryLimit(null);
|
||||
MemoryLimits.FlushThreadMemoryCounters();
|
||||
}
|
||||
}
|
||||
}
|
||||
Assert.IsNull(MemoryLimits.GetGlobalMemoryLimit());
|
||||
|
||||
[TestMethod]
|
||||
public void Evaluation_fails_when_input_pushes_policy_over_global_limit()
|
||||
{
|
||||
const ulong limit = 32 * 1024;
|
||||
MemoryLimits.SetGlobalMemoryLimit(limit);
|
||||
Assert.AreEqual(limit, MemoryLimits.GetGlobalMemoryLimit());
|
||||
|
||||
MemoryLimits.SetGlobalMemoryLimit(null);
|
||||
Assert.IsNull(MemoryLimits.GetGlobalMemoryLimit());
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Memory_limit_violations_surface_from_engine_calls()
|
||||
{
|
||||
lock (LimitLock)
|
||||
{
|
||||
using var guard = new MemoryLimitScope();
|
||||
using var engine = new Engine();
|
||||
using var guard = new MemoryLimitScope();
|
||||
using var engine = new Engine();
|
||||
|
||||
const string policy = """
|
||||
const ulong limit = 1;
|
||||
var payload = new string('x', 128 * 1024);
|
||||
|
||||
MemoryLimits.FlushThreadMemoryCounters();
|
||||
MemoryLimits.SetGlobalMemoryLimit(limit);
|
||||
|
||||
try
|
||||
{
|
||||
var ex = Assert.ThrowsException<InvalidOperationException>(
|
||||
() => engine.SetInputJson($"{{\"payload\":\"{payload}\"}}"));
|
||||
StringAssert.Contains(ex.Message, "execution exceeded memory limit");
|
||||
}
|
||||
finally
|
||||
{
|
||||
MemoryLimits.SetGlobalMemoryLimit(null);
|
||||
MemoryLimits.FlushThreadMemoryCounters();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Evaluation_fails_when_input_pushes_policy_over_global_limit()
|
||||
{
|
||||
lock (LimitLock)
|
||||
{
|
||||
using var guard = new MemoryLimitScope();
|
||||
using var engine = new Engine();
|
||||
|
||||
const string policy = """
|
||||
package memorylimit
|
||||
|
||||
import rego.v1
|
||||
@@ -281,96 +303,152 @@ import rego.v1
|
||||
stretched := concat("", [input.block | numbers.range(0, input.repeat - 1)[_]])
|
||||
""";
|
||||
|
||||
engine.AddPolicy("memorylimit.rego", policy);
|
||||
engine.AddPolicy("memorylimit.rego", policy);
|
||||
|
||||
MemoryLimits.FlushThreadMemoryCounters();
|
||||
const ulong limit = 4 * 1024 * 1024;
|
||||
MemoryLimits.SetGlobalMemoryLimit(limit);
|
||||
MemoryLimits.FlushThreadMemoryCounters();
|
||||
const ulong limit = 4 * 1024 * 1024;
|
||||
MemoryLimits.SetGlobalMemoryLimit(limit);
|
||||
|
||||
var block = new string('x', 16 * 1024);
|
||||
var block = new string('x', 16 * 1024);
|
||||
|
||||
var smallInput = JsonSerializer.Serialize(new { block, repeat = 16 });
|
||||
engine.SetInputJson(smallInput);
|
||||
var smallResult = engine.EvalRule("data.memorylimit.stretched");
|
||||
Assert.IsNotNull(smallResult);
|
||||
var stretched = JsonSerializer.Deserialize<string>(smallResult);
|
||||
Assert.IsNotNull(stretched, "Policy should return a string result.");
|
||||
Assert.AreEqual(block.Length * 16, stretched!.Length, "Policy should expand the payload under the limit.");
|
||||
var smallInput = JsonSerializer.Serialize(new { block, repeat = 16 });
|
||||
engine.SetInputJson(smallInput);
|
||||
var smallResult = engine.EvalRule("data.memorylimit.stretched");
|
||||
Assert.IsNotNull(smallResult);
|
||||
var stretched = JsonSerializer.Deserialize<string>(smallResult);
|
||||
Assert.IsNotNull(stretched, "Policy should return a string result.");
|
||||
Assert.AreEqual(block.Length * 16, stretched!.Length, "Policy should expand the payload under the limit.");
|
||||
|
||||
var largeInput = JsonSerializer.Serialize(new { block, repeat = 4096 });
|
||||
engine.SetInputJson(largeInput);
|
||||
var largeInput = JsonSerializer.Serialize(new { block, repeat = 4096 });
|
||||
engine.SetInputJson(largeInput);
|
||||
|
||||
var ex = Assert.ThrowsException<InvalidOperationException>(
|
||||
() => engine.EvalRule("data.memorylimit.stretched"));
|
||||
StringAssert.Contains(ex.Message, "execution exceeded memory limit");
|
||||
var ex = Assert.ThrowsException<InvalidOperationException>(
|
||||
() => engine.EvalRule("data.memorylimit.stretched"));
|
||||
StringAssert.Contains(ex.Message, "execution exceeded memory limit");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Thread_flush_threshold_roundtrips()
|
||||
{
|
||||
[TestMethod]
|
||||
public void Thread_flush_threshold_roundtrips()
|
||||
{
|
||||
lock (LimitLock)
|
||||
{
|
||||
var original = MemoryLimits.GetThreadMemoryFlushThreshold();
|
||||
try
|
||||
{
|
||||
const ulong threshold = 256 * 1024;
|
||||
MemoryLimits.SetThreadFlushThresholdOverride(threshold);
|
||||
Assert.AreEqual(threshold, MemoryLimits.GetThreadMemoryFlushThreshold());
|
||||
|
||||
MemoryLimits.SetThreadFlushThresholdOverride(null);
|
||||
var restored = MemoryLimits.GetThreadMemoryFlushThreshold();
|
||||
Assert.IsTrue(restored.HasValue, "Clearing override should restore allocator default.");
|
||||
if (original.HasValue)
|
||||
var original = MemoryLimits.GetThreadMemoryFlushThreshold();
|
||||
try
|
||||
{
|
||||
Assert.AreEqual(original, restored);
|
||||
const ulong threshold = 256 * 1024;
|
||||
MemoryLimits.SetThreadFlushThresholdOverride(threshold);
|
||||
Assert.AreEqual(threshold, MemoryLimits.GetThreadMemoryFlushThreshold());
|
||||
|
||||
MemoryLimits.SetThreadFlushThresholdOverride(null);
|
||||
var restored = MemoryLimits.GetThreadMemoryFlushThreshold();
|
||||
Assert.IsTrue(restored.HasValue, "Clearing override should restore allocator default.");
|
||||
if (original.HasValue)
|
||||
{
|
||||
Assert.AreEqual(original, restored);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
MemoryLimits.SetThreadFlushThresholdOverride(original);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
MemoryLimits.SetThreadFlushThresholdOverride(original);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SetInputJson_has_negligible_allocations_after_warmup()
|
||||
{
|
||||
using var engine = new Engine();
|
||||
const string payload = "{}";
|
||||
|
||||
// Warm up the engine and JIT to ensure subsequent measurements are representative.
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
engine.SetInputJson(payload);
|
||||
}
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
|
||||
const int iterations = 256;
|
||||
var before = GC.GetAllocatedBytesForCurrentThread();
|
||||
|
||||
for (int i = 0; i < iterations; i++)
|
||||
[TestMethod]
|
||||
public void SetInputJson_has_negligible_allocations_after_warmup()
|
||||
{
|
||||
engine.SetInputJson(payload);
|
||||
using var engine = new Engine();
|
||||
const string payload = "{}";
|
||||
|
||||
// Warm up the engine and JIT to ensure subsequent measurements are representative.
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
engine.SetInputJson(payload);
|
||||
}
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
|
||||
const int iterations = 256;
|
||||
var before = GC.GetAllocatedBytesForCurrentThread();
|
||||
|
||||
for (int i = 0; i < iterations; i++)
|
||||
{
|
||||
engine.SetInputJson(payload);
|
||||
}
|
||||
|
||||
var after = GC.GetAllocatedBytesForCurrentThread();
|
||||
var allocated = Math.Max(0, after - before);
|
||||
var bytesPerOp = allocated / (double)iterations;
|
||||
|
||||
// Runtime bookkeeping (delegate caches, GC write barriers) differs across platforms, so
|
||||
// we measure bytes per call rather than absolute totals and allow a small budget.
|
||||
// CI will flag regressions where marshalling starts allocating per invocation.
|
||||
|
||||
// Allow a small budget for delegates and runtime bookkeeping while still flagging regressions.
|
||||
Assert.IsTrue(
|
||||
bytesPerOp <= 512,
|
||||
$"Expected ≤512 B/op after warmup, but observed {bytesPerOp:F2} B/op (total {allocated} bytes)."
|
||||
);
|
||||
}
|
||||
|
||||
var after = GC.GetAllocatedBytesForCurrentThread();
|
||||
var allocated = Math.Max(0, after - before);
|
||||
var bytesPerOp = allocated / (double)iterations;
|
||||
[TestMethod]
|
||||
public void Disposed_objects_throw_object_disposed_exception()
|
||||
{
|
||||
var engine = new Engine();
|
||||
engine.Dispose();
|
||||
Assert.ThrowsException<ObjectDisposedException>(() => engine.EvalRule("data.test.message"));
|
||||
|
||||
// Runtime bookkeeping (delegate caches, GC write barriers) differs across platforms, so
|
||||
// we measure bytes per call rather than absolute totals and allow a small budget.
|
||||
// CI will flag regressions where marshalling starts allocating per invocation.
|
||||
var program = Program.CreateEmpty();
|
||||
program.Dispose();
|
||||
Assert.ThrowsException<ObjectDisposedException>(() => program.SerializeBinary());
|
||||
|
||||
// Allow a small budget for delegates and runtime bookkeeping while still flagging regressions.
|
||||
Assert.IsTrue(
|
||||
bytesPerOp <= 512,
|
||||
$"Expected ≤512 B/op after warmup, but observed {bytesPerOp:F2} B/op (total {allocated} bytes)."
|
||||
);
|
||||
}
|
||||
var rvm = new Rvm();
|
||||
rvm.Dispose();
|
||||
Assert.ThrowsException<ObjectDisposedException>(() => rvm.Execute());
|
||||
|
||||
var modules = new[] { new PolicyModule("test.rego", "package test\nallow = true") };
|
||||
var compiled = Compiler.CompilePolicyWithEntrypoint("{}", modules, "data.test.allow");
|
||||
compiled.Dispose();
|
||||
Assert.ThrowsException<ObjectDisposedException>(() => compiled.EvalWithInput("{}"));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Registry_helpers_return_empty_after_clear()
|
||||
{
|
||||
TargetRegistry.Clear();
|
||||
Assert.IsTrue(TargetRegistry.IsEmpty);
|
||||
Assert.AreEqual(0, TargetRegistry.GetNames().Count);
|
||||
|
||||
SchemaRegistry.ClearResources();
|
||||
SchemaRegistry.ClearEffects();
|
||||
Assert.IsTrue(SchemaRegistry.IsResourceRegistryEmpty);
|
||||
Assert.IsTrue(SchemaRegistry.IsEffectRegistryEmpty);
|
||||
Assert.AreEqual(0, SchemaRegistry.GetResourceNames().Count);
|
||||
Assert.AreEqual(0, SchemaRegistry.GetEffectNames().Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Utf8_marshalling_handles_large_unicode_payloads()
|
||||
{
|
||||
var payload = string.Concat(new string('ß', 2048), "-✓-", new string('漢', 1024));
|
||||
|
||||
using var engine = new Engine();
|
||||
engine.AddPolicy("test.rego", "package test\nmessage = input.msg");
|
||||
engine.SetInputJson(JsonSerializer.Serialize(new { msg = payload }));
|
||||
|
||||
var result = engine.EvalRule("data.test.message");
|
||||
|
||||
Assert.IsNotNull(result);
|
||||
|
||||
// Compare by parsing the JSON string to avoid encoder differences across platforms.
|
||||
var parsed = JsonSerializer.Deserialize<string>(result);
|
||||
Assert.IsNotNull(parsed);
|
||||
|
||||
Assert.AreEqual(payload, parsed);
|
||||
}
|
||||
|
||||
private sealed class MemoryLimitScope : IDisposable
|
||||
{
|
||||
|
||||
@@ -96,9 +96,9 @@ allow if {
|
||||
Assert.AreEqual("true", result, "expected allow=true");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Program_host_await_suspend_and_resume_succeeds()
|
||||
{
|
||||
[TestMethod]
|
||||
public void Program_host_await_suspend_and_resume_succeeds()
|
||||
{
|
||||
var modules = new[] { new PolicyModule("host_await.rego", HostAwaitPolicy) };
|
||||
var entryPoints = new[] { "data.demo.allow" };
|
||||
|
||||
@@ -115,5 +115,5 @@ allow if {
|
||||
|
||||
var resumed = vm.Resume("{\"tier\":\"gold\"}");
|
||||
Assert.AreEqual("true", resumed, "expected allow=true after resume");
|
||||
}
|
||||
}
|
||||
}
|
||||
153
bindings/csharp/Regorus/AliasRegistry.cs
Normal file
153
bindings/csharp/Regorus/AliasRegistry.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using Regorus.Internal;
|
||||
|
||||
#nullable enable
|
||||
namespace Regorus
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages Azure Policy alias definitions used for resource normalization
|
||||
/// and policy compilation.
|
||||
/// </summary>
|
||||
public unsafe sealed class AliasRegistry : SafeHandleWrapper
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an empty alias registry.
|
||||
/// </summary>
|
||||
public AliasRegistry()
|
||||
: base(RegorusAliasRegistryHandle.Create(), nameof(AliasRegistry))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load control-plane alias data (array of ProviderAliases) from a JSON string.
|
||||
/// </summary>
|
||||
/// <param name="json">JSON array of ProviderAliases (e.g. from Get-AzPolicyAlias or ResourceTypesAndAliases.json)</param>
|
||||
public void LoadJson(string json)
|
||||
{
|
||||
Utf8Marshaller.WithUtf8(json, jsonPtr =>
|
||||
{
|
||||
UseHandle(regPtr =>
|
||||
{
|
||||
CheckAndDropResult(API.regorus_alias_registry_load_json(
|
||||
(RegorusAliasRegistry*)regPtr, (byte*)jsonPtr));
|
||||
return 0;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load a data-plane policy manifest from a JSON string.
|
||||
/// </summary>
|
||||
/// <param name="json">JSON object containing a DataPolicyManifest</param>
|
||||
public void LoadManifest(string json)
|
||||
{
|
||||
Utf8Marshaller.WithUtf8(json, jsonPtr =>
|
||||
{
|
||||
UseHandle(regPtr =>
|
||||
{
|
||||
CheckAndDropResult(API.regorus_alias_registry_load_manifest(
|
||||
(RegorusAliasRegistry*)regPtr, (byte*)jsonPtr));
|
||||
return 0;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of resource types loaded in the registry.
|
||||
/// </summary>
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return UseHandle(regPtr =>
|
||||
{
|
||||
return ResultHelpers.GetIntResult(
|
||||
API.regorus_alias_registry_len((RegorusAliasRegistry*)regPtr));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalize an ARM resource JSON and wrap it into the standard input envelope
|
||||
/// expected by a compiled Azure Policy program.
|
||||
/// </summary>
|
||||
/// <param name="resourceJson">Raw ARM resource JSON</param>
|
||||
/// <param name="apiVersion">API version string (e.g. "2023-01-01"), or null to use default alias paths</param>
|
||||
/// <param name="contextJson">Additional context JSON object (pass "{}" if none)</param>
|
||||
/// <param name="parametersJson">Policy parameter values JSON (pass "{}" if none)</param>
|
||||
/// <returns>JSON string: { "resource": <normalized>, "context": <context>, "parameters": <params> }</returns>
|
||||
public string? NormalizeAndWrap(string resourceJson, string? apiVersion = null, string contextJson = "{}", string parametersJson = "{}")
|
||||
{
|
||||
return Utf8Marshaller.WithUtf8(resourceJson, resPtr =>
|
||||
Utf8Marshaller.WithUtf8(contextJson, ctxPtr =>
|
||||
Utf8Marshaller.WithUtf8(parametersJson, paramsPtr =>
|
||||
{
|
||||
if (apiVersion is null)
|
||||
{
|
||||
return UseHandle(regPtr =>
|
||||
{
|
||||
return ResultHelpers.GetStringResult(
|
||||
API.regorus_alias_registry_normalize_and_wrap(
|
||||
(RegorusAliasRegistry*)regPtr,
|
||||
(byte*)resPtr, null,
|
||||
(byte*)ctxPtr, (byte*)paramsPtr));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
return Utf8Marshaller.WithUtf8(apiVersion, apiPtr =>
|
||||
UseHandle(regPtr =>
|
||||
{
|
||||
return ResultHelpers.GetStringResult(
|
||||
API.regorus_alias_registry_normalize_and_wrap(
|
||||
(RegorusAliasRegistry*)regPtr,
|
||||
(byte*)resPtr, (byte*)apiPtr,
|
||||
(byte*)ctxPtr, (byte*)paramsPtr));
|
||||
}));
|
||||
}
|
||||
})));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Denormalize a previously-normalized resource JSON back to ARM format.
|
||||
/// </summary>
|
||||
/// <param name="normalizedJson">The normalized resource JSON</param>
|
||||
/// <param name="apiVersion">API version string, or null to use default alias paths</param>
|
||||
/// <returns>Denormalized ARM JSON string</returns>
|
||||
public string? Denormalize(string normalizedJson, string? apiVersion = null)
|
||||
{
|
||||
return Utf8Marshaller.WithUtf8(normalizedJson, normPtr =>
|
||||
{
|
||||
if (apiVersion is null)
|
||||
{
|
||||
return UseHandle(regPtr =>
|
||||
{
|
||||
return ResultHelpers.GetStringResult(
|
||||
API.regorus_alias_registry_denormalize(
|
||||
(RegorusAliasRegistry*)regPtr,
|
||||
(byte*)normPtr, null));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
return Utf8Marshaller.WithUtf8(apiVersion, apiPtr =>
|
||||
UseHandle(regPtr =>
|
||||
{
|
||||
return ResultHelpers.GetStringResult(
|
||||
API.regorus_alias_registry_denormalize(
|
||||
(RegorusAliasRegistry*)regPtr,
|
||||
(byte*)normPtr, (byte*)apiPtr));
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static string? CheckAndDropResult(RegorusResult result)
|
||||
{
|
||||
return ResultHelpers.GetStringResult(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
39
bindings/csharp/Regorus/CacheConfig.cs
Normal file
39
bindings/csharp/Regorus/CacheConfig.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Regorus
|
||||
{
|
||||
/// <summary>
|
||||
/// Global configuration for compiled pattern caches used by regex and glob builtins.
|
||||
/// </summary>
|
||||
public readonly struct CacheConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CacheConfig"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="regex">Maximum cached compiled regex patterns (default 256, 0 = disabled).</param>
|
||||
/// <param name="glob">Maximum cached compiled glob matchers (default 128, 0 = disabled).</param>
|
||||
public CacheConfig(nuint regex, nuint glob)
|
||||
{
|
||||
Regex = regex;
|
||||
Glob = glob;
|
||||
}
|
||||
|
||||
/// <summary>Maximum cached compiled regex patterns (default 256).</summary>
|
||||
public nuint Regex { get; }
|
||||
|
||||
/// <summary>Maximum cached compiled glob matchers (default 128).</summary>
|
||||
public nuint Glob { get; }
|
||||
|
||||
internal Regorus.Internal.RegorusCacheConfig ToNative()
|
||||
{
|
||||
return new Regorus.Internal.RegorusCacheConfig
|
||||
{
|
||||
regex = Regex,
|
||||
glob = Glob,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using Regorus.Internal;
|
||||
|
||||
#nullable enable
|
||||
@@ -18,20 +17,15 @@ namespace Regorus
|
||||
/// Each instance represents a unique native policy object.
|
||||
///
|
||||
/// Thread Safety: This class is thread-safe for all operations. Multiple threads
|
||||
/// can safely call EvalWithInput() concurrently, and Dispose() will safely wait
|
||||
/// for all active evaluations to complete before freeing resources. No external
|
||||
/// synchronization is required.
|
||||
/// can safely call EvalWithInput() concurrently. Dispose() blocks new calls, waits
|
||||
/// briefly, and defers the native release to the last in-flight caller if needed.
|
||||
/// No external synchronization is required.
|
||||
/// </summary>
|
||||
public unsafe sealed class CompiledPolicy : IDisposable
|
||||
public unsafe sealed class CompiledPolicy : SafeHandleWrapper
|
||||
{
|
||||
private RegorusCompiledPolicyHandle? _handle;
|
||||
private readonly ManualResetEventSlim _idleEvent = new(initialState: true);
|
||||
private int _isDisposed;
|
||||
private int _activeEvaluations;
|
||||
|
||||
internal CompiledPolicy(RegorusCompiledPolicyHandle handle)
|
||||
: base(handle, nameof(CompiledPolicy))
|
||||
{
|
||||
_handle = handle ?? throw new ArgumentNullException(nameof(handle));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -45,36 +39,16 @@ namespace Regorus
|
||||
/// <exception cref="ObjectDisposedException">Thrown when the policy has been disposed</exception>
|
||||
public string? EvalWithInput(string inputJson)
|
||||
{
|
||||
// Increment active evaluations count
|
||||
var active = System.Threading.Interlocked.Increment(ref _activeEvaluations);
|
||||
if (active == 1)
|
||||
return Internal.Utf8Marshaller.WithUtf8(inputJson, inputPtr =>
|
||||
{
|
||||
_idleEvent.Reset();
|
||||
}
|
||||
try
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
return Internal.Utf8Marshaller.WithUtf8(inputJson, inputPtr =>
|
||||
return UseHandle(policyPtr =>
|
||||
{
|
||||
return UseHandle(policyPtr =>
|
||||
unsafe
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return CheckAndDropResult(Internal.API.regorus_compiled_policy_eval_with_input((Internal.RegorusCompiledPolicy*)policyPtr, (byte*)inputPtr));
|
||||
}
|
||||
});
|
||||
return CheckAndDropResult(Internal.API.regorus_compiled_policy_eval_with_input((Internal.RegorusCompiledPolicy*)policyPtr, (byte*)inputPtr));
|
||||
}
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Decrement active evaluations count
|
||||
var remaining = System.Threading.Interlocked.Decrement(ref _activeEvaluations);
|
||||
if (remaining == 0)
|
||||
{
|
||||
_idleEvent.Set();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -86,7 +60,6 @@ namespace Regorus
|
||||
/// <exception cref="ObjectDisposedException">Thrown when the policy has been disposed</exception>
|
||||
public PolicyInfo GetPolicyInfo()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
var jsonResult = UseHandle(policyPtr =>
|
||||
{
|
||||
unsafe
|
||||
@@ -94,7 +67,7 @@ namespace Regorus
|
||||
return CheckAndDropResult(Internal.API.regorus_compiled_policy_get_policy_info((Internal.RegorusCompiledPolicy*)policyPtr));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (string.IsNullOrEmpty(jsonResult))
|
||||
{
|
||||
throw new Exception("Failed to get policy info: empty response");
|
||||
@@ -106,8 +79,8 @@ namespace Regorus
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
|
||||
return JsonSerializer.Deserialize<PolicyInfo>(jsonResult!, options)
|
||||
|
||||
return JsonSerializer.Deserialize<PolicyInfo>(jsonResult!, options)
|
||||
?? throw new Exception("Failed to deserialize policy info");
|
||||
}
|
||||
catch (JsonException ex)
|
||||
@@ -116,106 +89,9 @@ namespace Regorus
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (System.Threading.Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
|
||||
{
|
||||
var handle = _handle;
|
||||
if (handle != null)
|
||||
{
|
||||
_idleEvent.Wait();
|
||||
|
||||
handle.Dispose();
|
||||
_handle = null;
|
||||
}
|
||||
|
||||
_idleEvent.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
if (_isDisposed != 0 || _handle is null || _handle.IsClosed)
|
||||
throw new ObjectDisposedException(nameof(CompiledPolicy));
|
||||
}
|
||||
|
||||
private string? CheckAndDropResult(Internal.RegorusResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (result.status != Internal.RegorusStatus.Ok)
|
||||
{
|
||||
var message = Internal.Utf8Marshaller.FromUtf8(result.error_message);
|
||||
throw result.status.CreateException(message);
|
||||
}
|
||||
|
||||
return result.data_type switch
|
||||
{
|
||||
Internal.RegorusDataType.String => Internal.Utf8Marshaller.FromUtf8(result.output),
|
||||
Internal.RegorusDataType.Boolean => result.bool_value.ToString().ToLowerInvariant(),
|
||||
Internal.RegorusDataType.Integer => result.int_value.ToString(),
|
||||
Internal.RegorusDataType.None => null,
|
||||
_ => Internal.Utf8Marshaller.FromUtf8(result.output)
|
||||
};
|
||||
}
|
||||
finally
|
||||
{
|
||||
Internal.API.regorus_result_drop(result);
|
||||
}
|
||||
}
|
||||
|
||||
private RegorusCompiledPolicyHandle GetHandleForUse()
|
||||
{
|
||||
var handle = _handle;
|
||||
if (handle is null || handle.IsClosed || handle.IsInvalid)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(CompiledPolicy));
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
internal T UseHandle<T>(Func<IntPtr, T> func)
|
||||
{
|
||||
var handle = GetHandleForUse();
|
||||
bool addedRef = false;
|
||||
try
|
||||
{
|
||||
handle.DangerousAddRef(ref addedRef);
|
||||
var pointer = handle.DangerousGetHandle();
|
||||
if (pointer == IntPtr.Zero)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(CompiledPolicy));
|
||||
}
|
||||
|
||||
return func(pointer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (addedRef)
|
||||
{
|
||||
handle.DangerousRelease();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal T UseHandleForInterop<T>(Func<IntPtr, T> func)
|
||||
{
|
||||
return UseHandle(func);
|
||||
}
|
||||
|
||||
private void UseHandle(Action<IntPtr> action)
|
||||
{
|
||||
UseHandle<object?>(handlePtr =>
|
||||
{
|
||||
action(handlePtr);
|
||||
return null;
|
||||
});
|
||||
return Internal.ResultHelpers.GetStringResult(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,17 +12,17 @@ namespace Regorus
|
||||
/// <summary>
|
||||
/// Represents a policy module with an ID and content.
|
||||
/// </summary>
|
||||
public struct PolicyModule
|
||||
public readonly struct PolicyModule
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier for this policy module.
|
||||
/// Gets the unique identifier for this policy module.
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
public string Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Rego policy content.
|
||||
/// Gets the Rego policy content.
|
||||
/// </summary>
|
||||
public string Content { get; set; }
|
||||
public string Content { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the PolicyModule struct.
|
||||
@@ -53,50 +53,40 @@ namespace Regorus
|
||||
/// <exception cref="Exception">Thrown when compilation fails</exception>
|
||||
public static CompiledPolicy CompilePolicyWithEntrypoint(string dataJson, IEnumerable<PolicyModule> modules, string entryPointRule)
|
||||
{
|
||||
var modulesArray = modules.ToArray();
|
||||
|
||||
var nativeModules = new Internal.RegorusPolicyModule[modulesArray.Length];
|
||||
var pinnedStrings = new List<Utf8Marshaller.PinnedUtf8>(modulesArray.Length * 2);
|
||||
|
||||
try
|
||||
if (modules is null)
|
||||
{
|
||||
for (int i = 0; i < modulesArray.Length; i++)
|
||||
throw new ArgumentNullException(nameof(modules));
|
||||
}
|
||||
|
||||
return CompilePolicyWithEntrypoint(dataJson, modules.ToArray(), entryPointRule);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compiles a policy from data and modules with a specific entry point rule.
|
||||
/// </summary>
|
||||
public static CompiledPolicy CompilePolicyWithEntrypoint(string dataJson, IReadOnlyList<PolicyModule> modules, string entryPointRule)
|
||||
{
|
||||
if (modules is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modules));
|
||||
}
|
||||
|
||||
using var pinnedModules = Internal.ModuleMarshalling.PinPolicyModules(modules);
|
||||
|
||||
return Utf8Marshaller.WithUtf8(dataJson, dataPtr =>
|
||||
Utf8Marshaller.WithUtf8(entryPointRule, entryPointPtr =>
|
||||
{
|
||||
var idPinned = Utf8Marshaller.Pin(modulesArray[i].Id);
|
||||
var contentPinned = Utf8Marshaller.Pin(modulesArray[i].Content);
|
||||
pinnedStrings.Add(idPinned);
|
||||
pinnedStrings.Add(contentPinned);
|
||||
|
||||
nativeModules[i] = new Internal.RegorusPolicyModule
|
||||
unsafe
|
||||
{
|
||||
id = idPinned.Pointer,
|
||||
content = contentPinned.Pointer
|
||||
};
|
||||
}
|
||||
|
||||
return Utf8Marshaller.WithUtf8(dataJson, dataPtr =>
|
||||
Utf8Marshaller.WithUtf8(entryPointRule, entryPointPtr =>
|
||||
{
|
||||
unsafe
|
||||
fixed (Internal.RegorusPolicyModule* modulesPtr = pinnedModules.Buffer)
|
||||
{
|
||||
fixed (Internal.RegorusPolicyModule* modulesPtr = nativeModules)
|
||||
{
|
||||
var result = Internal.API.regorus_compile_policy_with_entrypoint(
|
||||
(byte*)dataPtr, modulesPtr, (UIntPtr)modulesArray.Length, (byte*)entryPointPtr);
|
||||
var result = Internal.API.regorus_compile_policy_with_entrypoint(
|
||||
(byte*)dataPtr, modulesPtr, (UIntPtr)pinnedModules.Length, (byte*)entryPointPtr);
|
||||
|
||||
var policy = GetCompiledPolicyResult(result);
|
||||
return policy;
|
||||
}
|
||||
return GetCompiledPolicyResult(result);
|
||||
}
|
||||
}));
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var pinned in pinnedStrings)
|
||||
{
|
||||
pinned.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -110,49 +100,39 @@ namespace Regorus
|
||||
/// <exception cref="Exception">Thrown when compilation fails</exception>
|
||||
public static CompiledPolicy CompilePolicyForTarget(string dataJson, IEnumerable<PolicyModule> modules)
|
||||
{
|
||||
var modulesArray = modules.ToArray();
|
||||
|
||||
var nativeModules = new Internal.RegorusPolicyModule[modulesArray.Length];
|
||||
var pinnedStrings = new List<Utf8Marshaller.PinnedUtf8>(modulesArray.Length * 2);
|
||||
|
||||
try
|
||||
if (modules is null)
|
||||
{
|
||||
for (int i = 0; i < modulesArray.Length; i++)
|
||||
throw new ArgumentNullException(nameof(modules));
|
||||
}
|
||||
|
||||
return CompilePolicyForTarget(dataJson, modules.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compiles a target-aware policy from data and modules.
|
||||
/// </summary>
|
||||
public static CompiledPolicy CompilePolicyForTarget(string dataJson, IReadOnlyList<PolicyModule> modules)
|
||||
{
|
||||
if (modules is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modules));
|
||||
}
|
||||
|
||||
using var pinnedModules = Internal.ModuleMarshalling.PinPolicyModules(modules);
|
||||
|
||||
return Utf8Marshaller.WithUtf8(dataJson, dataPtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var idPinned = Utf8Marshaller.Pin(modulesArray[i].Id);
|
||||
var contentPinned = Utf8Marshaller.Pin(modulesArray[i].Content);
|
||||
pinnedStrings.Add(idPinned);
|
||||
pinnedStrings.Add(contentPinned);
|
||||
|
||||
nativeModules[i] = new Internal.RegorusPolicyModule
|
||||
fixed (Internal.RegorusPolicyModule* modulesPtr = pinnedModules.Buffer)
|
||||
{
|
||||
id = idPinned.Pointer,
|
||||
content = contentPinned.Pointer
|
||||
};
|
||||
}
|
||||
var result = Internal.API.regorus_compile_policy_for_target(
|
||||
(byte*)dataPtr, modulesPtr, (UIntPtr)pinnedModules.Length);
|
||||
|
||||
return Utf8Marshaller.WithUtf8(dataJson, dataPtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (Internal.RegorusPolicyModule* modulesPtr = nativeModules)
|
||||
{
|
||||
var result = Internal.API.regorus_compile_policy_for_target(
|
||||
(byte*)dataPtr, modulesPtr, (UIntPtr)modulesArray.Length);
|
||||
|
||||
var policy = GetCompiledPolicyResult(result);
|
||||
return policy;
|
||||
}
|
||||
return GetCompiledPolicyResult(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var pinned in pinnedStrings)
|
||||
{
|
||||
pinned.Dispose();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static CompiledPolicy GetCompiledPolicyResult(Internal.RegorusResult result)
|
||||
|
||||
@@ -16,14 +16,11 @@ namespace Regorus
|
||||
/// Cloning is cheap and involves only incrementing reference counts for shared immutable objects like parsed policies,
|
||||
/// data etc. Mutable state is deep copied as needed.
|
||||
/// </summary>
|
||||
public unsafe sealed class Engine : IDisposable
|
||||
public unsafe sealed class Engine : SafeHandleWrapper
|
||||
{
|
||||
private RegorusEngineHandle? _handle;
|
||||
private int _isDisposed;
|
||||
|
||||
public Engine()
|
||||
: base(RegorusEngineHandle.Create(), nameof(Engine))
|
||||
{
|
||||
_handle = RegorusEngineHandle.Create();
|
||||
}
|
||||
|
||||
public static void SetFallbackExecutionTimerConfig(ExecutionTimerConfig config)
|
||||
@@ -37,42 +34,24 @@ namespace Regorus
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_clear_fallback_execution_timer_config());
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public static void SetCacheConfig(CacheConfig config)
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
|
||||
// This object will be cleaned up by the Dispose method.
|
||||
// Therefore, call GC.SuppressFinalize to
|
||||
// take this object off the finalization queue
|
||||
// and prevent finalization code for this object
|
||||
// from executing a second time.
|
||||
GC.SuppressFinalize(this);
|
||||
var nativeConfig = config.ToNative();
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_set_cache_config(nativeConfig));
|
||||
}
|
||||
|
||||
// Dispose(bool disposing) executes in two distinct scenarios.
|
||||
// If disposing equals true, the method has been called directly
|
||||
// or indirectly by a user's code. Managed and unmanaged resources
|
||||
// can be disposed.
|
||||
// If disposing equals false, the method has been called by the
|
||||
// runtime from inside the finalizer and you should not reference
|
||||
// other objects. Only unmanaged resources can be disposed.
|
||||
void Dispose(bool disposing)
|
||||
public static void ClearCache()
|
||||
{
|
||||
if (System.Threading.Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
|
||||
{
|
||||
_handle?.Dispose();
|
||||
_handle = null;
|
||||
}
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_clear_cache());
|
||||
}
|
||||
|
||||
private Engine(RegorusEngineHandle handle)
|
||||
: base(handle, nameof(Engine))
|
||||
{
|
||||
_handle = handle ?? throw new ArgumentNullException(nameof(handle));
|
||||
}
|
||||
|
||||
public Engine Clone()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
@@ -91,402 +70,216 @@ namespace Regorus
|
||||
|
||||
public void SetStrictBuiltinErrors(bool strict)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_strict_builtin_errors((Regorus.Internal.RegorusEngine*)enginePtr, strict));
|
||||
}
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_strict_builtin_errors((Regorus.Internal.RegorusEngine*)enginePtr, strict));
|
||||
});
|
||||
}
|
||||
|
||||
public void SetExecutionTimerConfig(ExecutionTimerConfig config)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
var nativeConfig = config.ToNative();
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var localConfig = nativeConfig;
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_execution_timer_config((Regorus.Internal.RegorusEngine*)enginePtr, &localConfig));
|
||||
}
|
||||
var localConfig = nativeConfig;
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_execution_timer_config((Regorus.Internal.RegorusEngine*)enginePtr, &localConfig));
|
||||
});
|
||||
}
|
||||
|
||||
public void ClearExecutionTimerConfig()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_clear_execution_timer_config((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
}
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_clear_execution_timer_config((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
});
|
||||
}
|
||||
|
||||
public void SetPolicyLengthConfig(PolicyLengthConfig config)
|
||||
{
|
||||
var nativeConfig = config.ToNative();
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_policy_length_config((Regorus.Internal.RegorusEngine*)enginePtr, nativeConfig));
|
||||
});
|
||||
}
|
||||
|
||||
public void ClearPolicyLengthConfig()
|
||||
{
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_clear_policy_length_config((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
});
|
||||
}
|
||||
|
||||
public string? AddPolicy(string path, string rego)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return Utf8Marshaller.WithUtf8(path, pathPtr =>
|
||||
Utf8Marshaller.WithUtf8(rego, regoPtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_add_policy((Regorus.Internal.RegorusEngine*)enginePtr, (byte*)pathPtr, (byte*)regoPtr));
|
||||
}
|
||||
});
|
||||
}
|
||||
}));
|
||||
UseHandle(enginePtr =>
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_add_policy((Regorus.Internal.RegorusEngine*)enginePtr, (byte*)pathPtr, (byte*)regoPtr))
|
||||
)));
|
||||
}
|
||||
|
||||
public void SetRegoV0(bool enable)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_rego_v0((Regorus.Internal.RegorusEngine*)enginePtr, enable));
|
||||
}
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_rego_v0((Regorus.Internal.RegorusEngine*)enginePtr, enable));
|
||||
});
|
||||
}
|
||||
|
||||
public string? AddPolicyFromFile(string path)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return Utf8Marshaller.WithUtf8(path, pathPtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_add_policy_from_file((Regorus.Internal.RegorusEngine*)enginePtr, (byte*)pathPtr));
|
||||
}
|
||||
});
|
||||
}
|
||||
return UseHandle(enginePtr =>
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_add_policy_from_file((Regorus.Internal.RegorusEngine*)enginePtr, (byte*)pathPtr))
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void AddDataJson(string data)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
Utf8Marshaller.WithUtf8(data, dataPtr =>
|
||||
{
|
||||
unsafe
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_add_data_json((Regorus.Internal.RegorusEngine*)enginePtr, (byte*)dataPtr));
|
||||
}
|
||||
});
|
||||
}
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_add_data_json((Regorus.Internal.RegorusEngine*)enginePtr, (byte*)dataPtr));
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void AddDataFromJsonFile(string path)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
Utf8Marshaller.WithUtf8(path, pathPtr =>
|
||||
{
|
||||
unsafe
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_add_data_from_json_file((Regorus.Internal.RegorusEngine*)enginePtr, (byte*)pathPtr));
|
||||
}
|
||||
});
|
||||
}
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_add_data_from_json_file((Regorus.Internal.RegorusEngine*)enginePtr, (byte*)pathPtr));
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void SetInputJson(string input)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
Utf8Marshaller.WithUtf8(input, inputPtr =>
|
||||
{
|
||||
unsafe
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_input_json((Regorus.Internal.RegorusEngine*)enginePtr, (byte*)inputPtr));
|
||||
}
|
||||
});
|
||||
}
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_input_json((Regorus.Internal.RegorusEngine*)enginePtr, (byte*)inputPtr));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void SetInputFromJsonFile(string path)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
Utf8Marshaller.WithUtf8(path, pathPtr =>
|
||||
{
|
||||
unsafe
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_input_from_json_file((Regorus.Internal.RegorusEngine*)enginePtr, (byte*)pathPtr));
|
||||
}
|
||||
});
|
||||
}
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_input_from_json_file((Regorus.Internal.RegorusEngine*)enginePtr, (byte*)pathPtr));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public string? EvalQuery(string query)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return Utf8Marshaller.WithUtf8(query, queryPtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_eval_query((Regorus.Internal.RegorusEngine*)enginePtr, (byte*)queryPtr));
|
||||
}
|
||||
});
|
||||
}
|
||||
return UseHandle(enginePtr =>
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_eval_query((Regorus.Internal.RegorusEngine*)enginePtr, (byte*)queryPtr))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public string? EvalRule(string rule)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return Utf8Marshaller.WithUtf8(rule, rulePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_eval_rule((Regorus.Internal.RegorusEngine*)enginePtr, (byte*)rulePtr));
|
||||
}
|
||||
});
|
||||
}
|
||||
return UseHandle(enginePtr =>
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_eval_rule((Regorus.Internal.RegorusEngine*)enginePtr, (byte*)rulePtr))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public void SetEnableCoverage(bool enable)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_enable_coverage((Regorus.Internal.RegorusEngine*)enginePtr, enable));
|
||||
}
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_enable_coverage((Regorus.Internal.RegorusEngine*)enginePtr, enable));
|
||||
});
|
||||
}
|
||||
|
||||
public void ClearCoverageData()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_clear_coverage_data((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
}
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_clear_coverage_data((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
});
|
||||
}
|
||||
|
||||
public string? GetCoverageReport()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_get_coverage_report((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
}
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_get_coverage_report((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
});
|
||||
}
|
||||
|
||||
public string? GetCoverageReportPretty()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_get_coverage_report_pretty((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
}
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_get_coverage_report_pretty((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
});
|
||||
}
|
||||
|
||||
public void SetGatherPrints(bool enable)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_gather_prints((Regorus.Internal.RegorusEngine*)enginePtr, enable));
|
||||
}
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_gather_prints((Regorus.Internal.RegorusEngine*)enginePtr, enable));
|
||||
});
|
||||
}
|
||||
|
||||
public string? TakePrints()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_take_prints((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
}
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_take_prints((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
});
|
||||
}
|
||||
|
||||
public string? GetAstAsJson()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_get_ast_as_json((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
}
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_get_ast_as_json((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
});
|
||||
}
|
||||
|
||||
public string? GetPolicyPackageNames()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_get_policy_package_names((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
}
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_get_policy_package_names((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
});
|
||||
}
|
||||
|
||||
public string? GetPolicyParameters()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_get_policy_parameters((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
}
|
||||
return CheckAndDropResult(Regorus.Internal.API.regorus_engine_get_policy_parameters((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
});
|
||||
}
|
||||
|
||||
private static string? StringFromUtf8(IntPtr ptr)
|
||||
private static string? CheckAndDropResult(Regorus.Internal.RegorusResult result)
|
||||
{
|
||||
|
||||
#if NETSTANDARD2_1
|
||||
return Marshal.PtrToStringUTF8(ptr);
|
||||
#else
|
||||
int len = 0;
|
||||
while (Marshal.ReadByte(ptr, len) != 0) { ++len; }
|
||||
byte[] buffer = new byte[len];
|
||||
Marshal.Copy(ptr, buffer, 0, buffer.Length);
|
||||
return Encoding.UTF8.GetString(buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static string? CheckAndDropResult(Regorus.Internal.RegorusResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (result.status != Regorus.Internal.RegorusStatus.Ok)
|
||||
{
|
||||
var message = Utf8Marshaller.FromUtf8(result.error_message);
|
||||
throw result.status.CreateException(message);
|
||||
}
|
||||
|
||||
return result.data_type switch
|
||||
{
|
||||
Regorus.Internal.RegorusDataType.String => Utf8Marshaller.FromUtf8(result.output),
|
||||
Regorus.Internal.RegorusDataType.Boolean => result.bool_value.ToString().ToLowerInvariant(),
|
||||
Regorus.Internal.RegorusDataType.Integer => result.int_value.ToString(),
|
||||
Regorus.Internal.RegorusDataType.None => null,
|
||||
_ => Utf8Marshaller.FromUtf8(result.output)
|
||||
};
|
||||
}
|
||||
finally
|
||||
{
|
||||
Regorus.Internal.API.regorus_result_drop(result);
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
if (_isDisposed != 0 || _handle is null || _handle.IsClosed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Engine));
|
||||
}
|
||||
}
|
||||
|
||||
internal RegorusEngineHandle GetHandleForUse()
|
||||
{
|
||||
var handle = _handle;
|
||||
if (handle is null || handle.IsClosed || handle.IsInvalid)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Engine));
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
internal void UseHandle(Action<IntPtr> action)
|
||||
{
|
||||
UseHandle<object?>(handlePtr =>
|
||||
{
|
||||
action(handlePtr);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
internal T UseHandle<T>(Func<IntPtr, T> func)
|
||||
{
|
||||
var handle = GetHandleForUse();
|
||||
bool addedRef = false;
|
||||
try
|
||||
{
|
||||
handle.DangerousAddRef(ref addedRef);
|
||||
var pointer = handle.DangerousGetHandle();
|
||||
if (pointer == IntPtr.Zero)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Engine));
|
||||
}
|
||||
|
||||
return func(pointer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (addedRef)
|
||||
{
|
||||
handle.DangerousRelease();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal T UseHandleForInterop<T>(Func<IntPtr, T> func)
|
||||
{
|
||||
return UseHandle(func);
|
||||
return ResultHelpers.GetStringResult(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -89,12 +89,14 @@ namespace Regorus
|
||||
);
|
||||
}
|
||||
|
||||
if (result.int_value < 0)
|
||||
try
|
||||
{
|
||||
throw new OverflowException($"{errorContext}: native value was negative ({result.int_value})");
|
||||
return checked((ulong)result.int_value);
|
||||
}
|
||||
catch (OverflowException ex)
|
||||
{
|
||||
throw new OverflowException($"{errorContext}: native value was out of range ({result.int_value})", ex);
|
||||
}
|
||||
|
||||
return (ulong)result.int_value;
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
156
bindings/csharp/Regorus/ModuleMarshalling.cs
Normal file
156
bindings/csharp/Regorus/ModuleMarshalling.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using Regorus;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Regorus.Internal
|
||||
{
|
||||
internal static unsafe class ModuleMarshalling
|
||||
{
|
||||
internal sealed class PinnedPolicyModules : IDisposable
|
||||
{
|
||||
private readonly List<Utf8Marshaller.PinnedUtf8> _pins;
|
||||
private bool _disposed;
|
||||
|
||||
internal PinnedPolicyModules(RegorusPolicyModule[] buffer, int length, List<Utf8Marshaller.PinnedUtf8> pins)
|
||||
{
|
||||
Buffer = buffer;
|
||||
Length = length;
|
||||
_pins = pins;
|
||||
}
|
||||
|
||||
internal RegorusPolicyModule[] Buffer { get; }
|
||||
|
||||
internal int Length { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var pin in _pins)
|
||||
{
|
||||
pin.Dispose();
|
||||
}
|
||||
|
||||
ArrayPool<RegorusPolicyModule>.Shared.Return(Buffer, clearArray: true);
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PinnedEntryPoints : IDisposable
|
||||
{
|
||||
private readonly List<Utf8Marshaller.PinnedUtf8> _pins;
|
||||
private bool _disposed;
|
||||
|
||||
internal PinnedEntryPoints(IntPtr[] buffer, int length, List<Utf8Marshaller.PinnedUtf8> pins)
|
||||
{
|
||||
Buffer = buffer;
|
||||
Length = length;
|
||||
_pins = pins;
|
||||
}
|
||||
|
||||
internal IntPtr[] Buffer { get; }
|
||||
|
||||
internal int Length { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var pin in _pins)
|
||||
{
|
||||
pin.Dispose();
|
||||
}
|
||||
|
||||
ArrayPool<IntPtr>.Shared.Return(Buffer, clearArray: true);
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
internal static PinnedPolicyModules PinPolicyModules(IReadOnlyList<PolicyModule> modules)
|
||||
{
|
||||
if (modules is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modules));
|
||||
}
|
||||
|
||||
var count = modules.Count;
|
||||
var buffer = ArrayPool<RegorusPolicyModule>.Shared.Rent(count);
|
||||
var pins = new List<Utf8Marshaller.PinnedUtf8>(count * 2);
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var idPinned = Utf8Marshaller.Pin(modules[i].Id);
|
||||
var contentPinned = Utf8Marshaller.Pin(modules[i].Content);
|
||||
pins.Add(idPinned);
|
||||
pins.Add(contentPinned);
|
||||
|
||||
buffer[i] = new RegorusPolicyModule
|
||||
{
|
||||
id = idPinned.Pointer,
|
||||
content = contentPinned.Pointer
|
||||
};
|
||||
}
|
||||
|
||||
return new PinnedPolicyModules(buffer, count, pins);
|
||||
}
|
||||
catch
|
||||
{
|
||||
foreach (var pin in pins)
|
||||
{
|
||||
pin.Dispose();
|
||||
}
|
||||
|
||||
ArrayPool<RegorusPolicyModule>.Shared.Return(buffer, clearArray: true);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
internal static PinnedEntryPoints PinEntryPoints(IReadOnlyList<string> entryPoints)
|
||||
{
|
||||
if (entryPoints is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(entryPoints));
|
||||
}
|
||||
|
||||
var count = entryPoints.Count;
|
||||
var buffer = ArrayPool<IntPtr>.Shared.Rent(count);
|
||||
var pins = new List<Utf8Marshaller.PinnedUtf8>(count);
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var entryPinned = Utf8Marshaller.Pin(entryPoints[i]);
|
||||
pins.Add(entryPinned);
|
||||
buffer[i] = (IntPtr)entryPinned.Pointer;
|
||||
}
|
||||
|
||||
return new PinnedEntryPoints(buffer, count, pins);
|
||||
}
|
||||
catch
|
||||
{
|
||||
foreach (var pin in pins)
|
||||
{
|
||||
pin.Dispose();
|
||||
}
|
||||
|
||||
ArrayPool<IntPtr>.Shared.Return(buffer, clearArray: true);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -428,6 +428,18 @@ namespace Regorus.Internal
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_engine_clear_execution_timer_config", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_engine_clear_execution_timer_config(RegorusEngine* engine);
|
||||
|
||||
/// <summary>
|
||||
/// Set the policy length limits for a specific engine instance.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_engine_set_policy_length_config", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_engine_set_policy_length_config(RegorusEngine* engine, RegorusPolicyLengthConfig config);
|
||||
|
||||
/// <summary>
|
||||
/// Clear the policy length configuration for a specific engine instance.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_engine_clear_policy_length_config", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_engine_clear_policy_length_config(RegorusEngine* engine);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Execution Timer Global Methods
|
||||
@@ -446,6 +458,22 @@ namespace Regorus.Internal
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cache Configuration Global Methods
|
||||
|
||||
/// <summary>
|
||||
/// Configure the global pattern caches used by regex and glob builtins.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_set_cache_config", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_set_cache_config(RegorusCacheConfig config);
|
||||
|
||||
/// <summary>
|
||||
/// Clear all entries from every pattern cache.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_clear_cache", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_clear_cache();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Compilation Methods
|
||||
|
||||
/// <summary>
|
||||
@@ -492,6 +520,16 @@ namespace Regorus.Internal
|
||||
|
||||
#endregion
|
||||
|
||||
#region RBAC Methods
|
||||
|
||||
/// <summary>
|
||||
/// Evaluate an Azure RBAC condition expression against a JSON evaluation context.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_rbac_engine_eval_condition", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_rbac_engine_eval_condition(byte* condition, byte* context_json);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Target Registry Methods
|
||||
|
||||
/// <summary>
|
||||
@@ -631,6 +669,55 @@ namespace Regorus.Internal
|
||||
internal static extern RegorusResult regorus_effect_schema_clear();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Alias Registry Methods
|
||||
|
||||
/// <summary>
|
||||
/// Create a new, empty AliasRegistry.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_alias_registry_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusAliasRegistry* regorus_alias_registry_new();
|
||||
|
||||
/// <summary>
|
||||
/// Drop an AliasRegistry.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_alias_registry_drop", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern void regorus_alias_registry_drop(RegorusAliasRegistry* registry);
|
||||
|
||||
/// <summary>
|
||||
/// Load control-plane alias data (array of ProviderAliases) into the registry.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_alias_registry_load_json", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_alias_registry_load_json(RegorusAliasRegistry* registry, byte* json);
|
||||
|
||||
/// <summary>
|
||||
/// Load a data-plane policy manifest into the registry.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_alias_registry_load_manifest", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_alias_registry_load_manifest(RegorusAliasRegistry* registry, byte* json);
|
||||
|
||||
/// <summary>
|
||||
/// Return the number of resource types loaded in the alias registry.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_alias_registry_len", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_alias_registry_len(RegorusAliasRegistry* registry);
|
||||
|
||||
/// <summary>
|
||||
/// Normalize an ARM resource JSON and wrap it into the standard input envelope.
|
||||
/// Returns a JSON string.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_alias_registry_normalize_and_wrap", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_alias_registry_normalize_and_wrap(
|
||||
RegorusAliasRegistry* registry, byte* resource_json, byte* api_version, byte* context_json, byte* parameters_json);
|
||||
|
||||
/// <summary>
|
||||
/// Denormalize a previously-normalized resource JSON back to ARM format.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_alias_registry_denormalize", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_alias_registry_denormalize(
|
||||
RegorusAliasRegistry* registry, byte* normalized_json, byte* api_version);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region Native Structures
|
||||
@@ -762,6 +849,27 @@ namespace Regorus.Internal
|
||||
public uint check_interval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FFI representation of the policy length configuration.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct RegorusPolicyLengthConfig
|
||||
{
|
||||
public uint max_col;
|
||||
public UIntPtr max_file_bytes;
|
||||
public UIntPtr max_lines;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FFI representation of the cache configuration.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct RegorusCacheConfig
|
||||
{
|
||||
public UIntPtr regex;
|
||||
public UIntPtr glob;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Byte buffer returned from FFI.
|
||||
/// </summary>
|
||||
@@ -815,5 +923,13 @@ namespace Regorus.Internal
|
||||
public byte* content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper for AliasRegistry.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe partial struct RegorusAliasRegistry
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
53
bindings/csharp/Regorus/PolicyLengthConfig.cs
Normal file
53
bindings/csharp/Regorus/PolicyLengthConfig.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Regorus
|
||||
{
|
||||
/// <summary>
|
||||
/// Policy source length limits enforced when loading policy files.
|
||||
/// </summary>
|
||||
public readonly struct PolicyLengthConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PolicyLengthConfig"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="maxCol">Maximum column width per line. Must be non-zero.</param>
|
||||
/// <param name="maxFileBytes">Maximum policy file size in bytes. Must be non-zero.</param>
|
||||
/// <param name="maxLines">Maximum number of lines per policy file. Must be non-zero.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when any parameter is zero.</exception>
|
||||
public PolicyLengthConfig(uint maxCol, nuint maxFileBytes, nuint maxLines)
|
||||
{
|
||||
if (maxCol == 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(maxCol), "Must be non-zero.");
|
||||
if (maxFileBytes == 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(maxFileBytes), "Must be non-zero.");
|
||||
if (maxLines == 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(maxLines), "Must be non-zero.");
|
||||
|
||||
MaxCol = maxCol;
|
||||
MaxFileBytes = maxFileBytes;
|
||||
MaxLines = maxLines;
|
||||
}
|
||||
|
||||
/// <summary>Maximum column width per line (default: 1024).</summary>
|
||||
public uint MaxCol { get; }
|
||||
|
||||
/// <summary>Maximum policy file size in bytes (default: 1 MiB).</summary>
|
||||
public nuint MaxFileBytes { get; }
|
||||
|
||||
/// <summary>Maximum number of lines per policy file (default: 20000).</summary>
|
||||
public nuint MaxLines { get; }
|
||||
|
||||
internal Regorus.Internal.RegorusPolicyLengthConfig ToNative()
|
||||
{
|
||||
return new Regorus.Internal.RegorusPolicyLengthConfig
|
||||
{
|
||||
max_col = MaxCol,
|
||||
max_file_bytes = MaxFileBytes,
|
||||
max_lines = MaxLines,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,14 +13,11 @@ namespace Regorus
|
||||
/// <summary>
|
||||
/// Represents a compiled RVM program.
|
||||
/// </summary>
|
||||
public unsafe sealed class Program : IDisposable
|
||||
public unsafe sealed class Program : SafeHandleWrapper
|
||||
{
|
||||
private RegorusProgramHandle? _handle;
|
||||
private int _isDisposed;
|
||||
|
||||
private Program(RegorusProgramHandle handle)
|
||||
: base(handle, nameof(Program))
|
||||
{
|
||||
_handle = handle ?? throw new ArgumentNullException(nameof(handle));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -36,63 +33,57 @@ namespace Regorus
|
||||
/// </summary>
|
||||
public static Program CompileFromModules(string dataJson, IEnumerable<PolicyModule> modules, IEnumerable<string> entryPoints)
|
||||
{
|
||||
var modulesArray = modules.ToArray();
|
||||
var entryPointsArray = entryPoints.ToArray();
|
||||
if (entryPointsArray.Length == 0)
|
||||
if (modules is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modules));
|
||||
}
|
||||
|
||||
if (entryPoints is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(entryPoints));
|
||||
}
|
||||
|
||||
return CompileFromModules(dataJson, modules.ToArray(), entryPoints.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compile an RVM program from modules and entry points.
|
||||
/// </summary>
|
||||
public static Program CompileFromModules(string dataJson, IReadOnlyList<PolicyModule> modules, IReadOnlyList<string> entryPoints)
|
||||
{
|
||||
if (modules is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modules));
|
||||
}
|
||||
|
||||
if (entryPoints is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(entryPoints));
|
||||
}
|
||||
|
||||
if (entryPoints.Count == 0)
|
||||
{
|
||||
throw new ArgumentException("At least one entry point is required.", nameof(entryPoints));
|
||||
}
|
||||
|
||||
var nativeModules = new RegorusPolicyModule[modulesArray.Length];
|
||||
var pinnedStrings = new List<Utf8Marshaller.PinnedUtf8>(modulesArray.Length * 2 + entryPointsArray.Length);
|
||||
var entryPointers = new IntPtr[entryPointsArray.Length];
|
||||
using var pinnedModules = ModuleMarshalling.PinPolicyModules(modules);
|
||||
using var pinnedEntryPoints = ModuleMarshalling.PinEntryPoints(entryPoints);
|
||||
|
||||
try
|
||||
return Utf8Marshaller.WithUtf8(dataJson, dataPtr =>
|
||||
{
|
||||
for (int i = 0; i < modulesArray.Length; i++)
|
||||
fixed (RegorusPolicyModule* modulesPtr = pinnedModules.Buffer)
|
||||
fixed (IntPtr* entryPtr = pinnedEntryPoints.Buffer)
|
||||
{
|
||||
var idPinned = Utf8Marshaller.Pin(modulesArray[i].Id);
|
||||
var contentPinned = Utf8Marshaller.Pin(modulesArray[i].Content);
|
||||
pinnedStrings.Add(idPinned);
|
||||
pinnedStrings.Add(contentPinned);
|
||||
var result = API.regorus_program_compile_from_modules(
|
||||
(byte*)dataPtr,
|
||||
modulesPtr,
|
||||
(UIntPtr)pinnedModules.Length,
|
||||
(byte**)entryPtr,
|
||||
(UIntPtr)pinnedEntryPoints.Length);
|
||||
|
||||
nativeModules[i] = new RegorusPolicyModule
|
||||
{
|
||||
id = idPinned.Pointer,
|
||||
content = contentPinned.Pointer
|
||||
};
|
||||
return GetProgramResult(result);
|
||||
}
|
||||
|
||||
for (int i = 0; i < entryPointsArray.Length; i++)
|
||||
{
|
||||
var entryPinned = Utf8Marshaller.Pin(entryPointsArray[i]);
|
||||
pinnedStrings.Add(entryPinned);
|
||||
entryPointers[i] = (IntPtr)entryPinned.Pointer;
|
||||
}
|
||||
|
||||
return Utf8Marshaller.WithUtf8(dataJson, dataPtr =>
|
||||
{
|
||||
fixed (RegorusPolicyModule* modulesPtr = nativeModules)
|
||||
fixed (IntPtr* entryPtr = entryPointers)
|
||||
{
|
||||
var result = API.regorus_program_compile_from_modules(
|
||||
(byte*)dataPtr,
|
||||
modulesPtr,
|
||||
(UIntPtr)modulesArray.Length,
|
||||
(byte**)entryPtr,
|
||||
(UIntPtr)entryPointsArray.Length);
|
||||
|
||||
return GetProgramResult(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var pinned in pinnedStrings)
|
||||
{
|
||||
pinned.Dispose();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -104,44 +95,48 @@ namespace Regorus
|
||||
{
|
||||
throw new ArgumentNullException(nameof(engine));
|
||||
}
|
||||
if (entryPoints is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(entryPoints));
|
||||
}
|
||||
|
||||
var entryPointsArray = entryPoints.ToArray();
|
||||
if (entryPointsArray.Length == 0)
|
||||
return CompileFromEngine(engine, entryPoints.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compile an RVM program from an engine instance and entry points.
|
||||
/// </summary>
|
||||
public static Program CompileFromEngine(Engine engine, IReadOnlyList<string> entryPoints)
|
||||
{
|
||||
if (engine is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(engine));
|
||||
}
|
||||
|
||||
if (entryPoints is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(entryPoints));
|
||||
}
|
||||
|
||||
if (entryPoints.Count == 0)
|
||||
{
|
||||
throw new ArgumentException("At least one entry point is required.", nameof(entryPoints));
|
||||
}
|
||||
|
||||
var pinnedStrings = new List<Utf8Marshaller.PinnedUtf8>(entryPointsArray.Length);
|
||||
var entryPointers = new IntPtr[entryPointsArray.Length];
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < entryPointsArray.Length; i++)
|
||||
{
|
||||
var entryPinned = Utf8Marshaller.Pin(entryPointsArray[i]);
|
||||
pinnedStrings.Add(entryPinned);
|
||||
entryPointers[i] = (IntPtr)entryPinned.Pointer;
|
||||
}
|
||||
using var pinnedEntryPoints = ModuleMarshalling.PinEntryPoints(entryPoints);
|
||||
|
||||
return engine.UseHandleForInterop(enginePtr =>
|
||||
{
|
||||
fixed (IntPtr* entryPtr = entryPointers)
|
||||
{
|
||||
var result = API.regorus_engine_compile_program_with_entrypoints(
|
||||
(RegorusEngine*)enginePtr,
|
||||
(byte**)entryPtr,
|
||||
(UIntPtr)entryPointsArray.Length);
|
||||
|
||||
return GetProgramResult(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
finally
|
||||
return engine.UseHandleForInterop(enginePtr =>
|
||||
{
|
||||
foreach (var pinned in pinnedStrings)
|
||||
fixed (IntPtr* entryPtr = pinnedEntryPoints.Buffer)
|
||||
{
|
||||
pinned.Dispose();
|
||||
var result = API.regorus_engine_compile_program_with_entrypoints(
|
||||
(RegorusEngine*)enginePtr,
|
||||
(byte**)entryPtr,
|
||||
(UIntPtr)pinnedEntryPoints.Length);
|
||||
|
||||
return GetProgramResult(result);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -169,7 +164,6 @@ namespace Regorus
|
||||
/// </summary>
|
||||
public byte[] SerializeBinary()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return UseHandle(programPtr =>
|
||||
{
|
||||
var result = API.regorus_program_serialize_binary((RegorusProgram*)programPtr);
|
||||
@@ -182,70 +176,12 @@ namespace Regorus
|
||||
/// </summary>
|
||||
public string? GenerateListing()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return UseHandle(programPtr =>
|
||||
{
|
||||
return CheckAndDropResult(API.regorus_program_generate_listing((RegorusProgram*)programPtr));
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (System.Threading.Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
|
||||
{
|
||||
_handle?.Dispose();
|
||||
_handle = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
if (_isDisposed != 0 || _handle is null || _handle.IsClosed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Program));
|
||||
}
|
||||
}
|
||||
|
||||
internal RegorusProgramHandle GetHandleForUse()
|
||||
{
|
||||
var handle = _handle;
|
||||
if (handle is null || handle.IsClosed || handle.IsInvalid)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Program));
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
internal T UseHandle<T>(Func<IntPtr, T> func)
|
||||
{
|
||||
var handle = GetHandleForUse();
|
||||
bool addedRef = false;
|
||||
try
|
||||
{
|
||||
handle.DangerousAddRef(ref addedRef);
|
||||
var pointer = handle.DangerousGetHandle();
|
||||
if (pointer == IntPtr.Zero)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Program));
|
||||
}
|
||||
|
||||
return func(pointer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (addedRef)
|
||||
{
|
||||
handle.DangerousRelease();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Program GetProgramResult(RegorusResult result)
|
||||
{
|
||||
try
|
||||
@@ -272,27 +208,7 @@ namespace Regorus
|
||||
|
||||
private static string? CheckAndDropResult(RegorusResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (result.status != RegorusStatus.Ok)
|
||||
{
|
||||
var message = Utf8Marshaller.FromUtf8(result.error_message);
|
||||
throw result.status.CreateException(message);
|
||||
}
|
||||
|
||||
return result.data_type switch
|
||||
{
|
||||
RegorusDataType.String => Utf8Marshaller.FromUtf8(result.output),
|
||||
RegorusDataType.Boolean => result.bool_value.ToString().ToLowerInvariant(),
|
||||
RegorusDataType.Integer => result.int_value.ToString(),
|
||||
RegorusDataType.None => null,
|
||||
_ => Utf8Marshaller.FromUtf8(result.output)
|
||||
};
|
||||
}
|
||||
finally
|
||||
{
|
||||
API.regorus_result_drop(result);
|
||||
}
|
||||
return ResultHelpers.GetStringResult(result);
|
||||
}
|
||||
|
||||
private static byte[] ExtractBuffer(RegorusResult result)
|
||||
|
||||
45
bindings/csharp/Regorus/RbacEngine.cs
Normal file
45
bindings/csharp/Regorus/RbacEngine.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using Regorus.Internal;
|
||||
|
||||
#nullable enable
|
||||
namespace Regorus
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides helpers for evaluating Azure RBAC condition expressions.
|
||||
/// </summary>
|
||||
public static unsafe class RbacEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// Evaluate an Azure RBAC condition expression against a JSON evaluation context.
|
||||
/// </summary>
|
||||
/// <param name="condition">Azure RBAC condition expression.</param>
|
||||
/// <param name="contextJson">JSON encoded EvaluationContext.</param>
|
||||
/// <returns>True if the condition evaluates to true; otherwise false.</returns>
|
||||
/// <exception cref="Exception">Thrown when evaluation fails.</exception>
|
||||
public static bool EvaluateCondition(string condition, string contextJson)
|
||||
{
|
||||
if (condition is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(condition));
|
||||
}
|
||||
|
||||
if (contextJson is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(contextJson));
|
||||
}
|
||||
|
||||
return Utf8Marshaller.WithUtf8(condition, conditionPtr =>
|
||||
Utf8Marshaller.WithUtf8(contextJson, contextPtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var result = Internal.API.regorus_rbac_engine_eval_condition((byte*)conditionPtr, (byte*)contextPtr);
|
||||
return ResultHelpers.GetBoolResult(result);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,18 @@
|
||||
<LangVersion>10.0</LangVersion>
|
||||
|
||||
<!-- See https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-pack -->
|
||||
<VersionPrefix>0.9.0</VersionPrefix>
|
||||
<VersionPrefix>0.9.1</VersionPrefix>
|
||||
<VersionSuffix>$(VersionSuffix)</VersionSuffix>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<PackageLicenseExpression>MIT AND Apache-2.0 AND BSD-3-Clause</PackageLicenseExpression>
|
||||
<PackageProjectUrl>https://github.com/microsoft/regorus</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/microsoft/regorus</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<Authors>Microsoft</Authors>
|
||||
<Company>Microsoft</Company>
|
||||
<PackageTags>rego;policy;engine;authorization;opa;rust</PackageTags>
|
||||
<Description>Fast, lightweight Rego interpreter and policy engine for .NET, powered by Rust.</Description>
|
||||
<Copyright>Copyright (c) Microsoft Corporation.</Copyright>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@@ -46,10 +55,14 @@
|
||||
|
||||
<Error Text="$(RegorusFFIArtifactsDir)/x86_64-unknown-linux-gnu/$(RegorusFFIArtifactsProfile)/libregorus_ffi.so missing."
|
||||
Condition="!Exists('$(RegorusFFIArtifactsDir)/x86_64-unknown-linux-gnu/$(RegorusFFIArtifactsProfile)/libregorus_ffi.so')" />
|
||||
|
||||
<Error Text="$(RegorusFFIArtifactsDir)/aarch64-apple-darwin/$(RegorusFFIArtifactsProfile)/libregorus_ffi.dylib missing."
|
||||
Condition="!Exists('$(RegorusFFIArtifactsDir)/aarch64-apple-darwin/$(RegorusFFIArtifactsProfile)/libregorus_ffi.dylib')" />
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="docs/README.md" Pack="true" PackagePath="/" />
|
||||
<None Include="../../../LICENSE" Pack="true" PackagePath="/" />
|
||||
|
||||
<!-- Copy each binary to expected location within the package -->
|
||||
<None Include="$(RegorusFFIArtifactsDir)/x86_64-pc-windows-msvc/$(RegorusFFIArtifactsProfile)/*.dll" Pack="true" PackagePath="runtimes/win-x64/native/" />
|
||||
|
||||
73
bindings/csharp/Regorus/ResultHelpers.cs
Normal file
73
bindings/csharp/Regorus/ResultHelpers.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Regorus.Internal
|
||||
{
|
||||
internal static unsafe class ResultHelpers
|
||||
{
|
||||
internal static string? GetStringResult(RegorusResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (result.status != RegorusStatus.Ok)
|
||||
{
|
||||
var message = Utf8Marshaller.FromUtf8(result.error_message);
|
||||
throw result.status.CreateException(message);
|
||||
}
|
||||
|
||||
return result.data_type switch
|
||||
{
|
||||
RegorusDataType.String => Utf8Marshaller.FromUtf8(result.output),
|
||||
RegorusDataType.Boolean => result.bool_value.ToString().ToLowerInvariant(),
|
||||
RegorusDataType.Integer => result.int_value.ToString(),
|
||||
RegorusDataType.None => null,
|
||||
_ => Utf8Marshaller.FromUtf8(result.output)
|
||||
};
|
||||
}
|
||||
finally
|
||||
{
|
||||
API.regorus_result_drop(result);
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool GetBoolResult(RegorusResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (result.status != RegorusStatus.Ok)
|
||||
{
|
||||
var message = Utf8Marshaller.FromUtf8(result.error_message);
|
||||
throw result.status.CreateException(message);
|
||||
}
|
||||
|
||||
return result.data_type == RegorusDataType.Boolean && result.bool_value;
|
||||
}
|
||||
finally
|
||||
{
|
||||
API.regorus_result_drop(result);
|
||||
}
|
||||
}
|
||||
|
||||
internal static long GetIntResult(RegorusResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (result.status != RegorusStatus.Ok)
|
||||
{
|
||||
var message = Utf8Marshaller.FromUtf8(result.error_message);
|
||||
throw result.status.CreateException(message);
|
||||
}
|
||||
|
||||
return result.data_type == RegorusDataType.Integer ? result.int_value : 0;
|
||||
}
|
||||
finally
|
||||
{
|
||||
API.regorus_result_drop(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,22 +7,35 @@ using Regorus.Internal;
|
||||
#nullable enable
|
||||
namespace Regorus
|
||||
{
|
||||
/// <summary>
|
||||
/// Execution mode for the RVM runtime.
|
||||
/// </summary>
|
||||
public enum ExecutionMode : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Run to completion without yielding.
|
||||
/// </summary>
|
||||
RunToCompletion = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Suspendable execution mode.
|
||||
/// </summary>
|
||||
Suspendable = 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper for the Regorus RVM runtime.
|
||||
/// </summary>
|
||||
public unsafe sealed class Rvm : IDisposable
|
||||
public unsafe sealed class Rvm : SafeHandleWrapper
|
||||
{
|
||||
private RegorusRvmHandle? _handle;
|
||||
private int _isDisposed;
|
||||
|
||||
public Rvm()
|
||||
: base(RegorusRvmHandle.Create(), nameof(Rvm))
|
||||
{
|
||||
_handle = RegorusRvmHandle.Create();
|
||||
}
|
||||
|
||||
private Rvm(RegorusRvmHandle handle)
|
||||
: base(handle, nameof(Rvm))
|
||||
{
|
||||
_handle = handle ?? throw new ArgumentNullException(nameof(handle));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -47,13 +60,12 @@ namespace Regorus
|
||||
/// </summary>
|
||||
public void LoadProgram(Program program)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (program is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(program));
|
||||
}
|
||||
|
||||
program.UseHandle(programPtr =>
|
||||
program.UseHandleForInterop(programPtr =>
|
||||
{
|
||||
UseHandle(vmPtr =>
|
||||
{
|
||||
@@ -69,7 +81,6 @@ namespace Regorus
|
||||
/// </summary>
|
||||
public void SetDataJson(string dataJson)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
Utf8Marshaller.WithUtf8(dataJson, dataPtr =>
|
||||
{
|
||||
UseHandle(vmPtr =>
|
||||
@@ -85,7 +96,6 @@ namespace Regorus
|
||||
/// </summary>
|
||||
public void SetInputJson(string inputJson)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
Utf8Marshaller.WithUtf8(inputJson, inputPtr =>
|
||||
{
|
||||
UseHandle(vmPtr =>
|
||||
@@ -101,7 +111,6 @@ namespace Regorus
|
||||
/// </summary>
|
||||
public void SetExecutionMode(byte mode)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
UseHandle(vmPtr =>
|
||||
{
|
||||
CheckAndDropResult(API.regorus_rvm_set_execution_mode((RegorusRvm*)vmPtr, mode));
|
||||
@@ -109,12 +118,19 @@ namespace Regorus
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the execution mode.
|
||||
/// </summary>
|
||||
public void SetExecutionMode(ExecutionMode mode)
|
||||
{
|
||||
SetExecutionMode((byte)mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute the program and return the JSON result.
|
||||
/// </summary>
|
||||
public string? Execute()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return UseHandle(vmPtr =>
|
||||
{
|
||||
return CheckAndDropResult(API.regorus_rvm_execute((RegorusRvm*)vmPtr));
|
||||
@@ -126,7 +142,6 @@ namespace Regorus
|
||||
/// </summary>
|
||||
public string? ExecuteEntryPoint(string entryPoint)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return Utf8Marshaller.WithUtf8(entryPoint, entryPtr =>
|
||||
{
|
||||
return UseHandle(vmPtr =>
|
||||
@@ -141,7 +156,6 @@ namespace Regorus
|
||||
/// </summary>
|
||||
public string? ExecuteEntryPoint(ulong index)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return UseHandle(vmPtr =>
|
||||
{
|
||||
return CheckAndDropResult(API.regorus_rvm_execute_entry_point_by_index((RegorusRvm*)vmPtr, (UIntPtr)index));
|
||||
@@ -153,7 +167,6 @@ namespace Regorus
|
||||
/// </summary>
|
||||
public string? Resume(string? resumeValueJson)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (resumeValueJson is null)
|
||||
{
|
||||
return UseHandle(vmPtr =>
|
||||
@@ -176,70 +189,12 @@ namespace Regorus
|
||||
/// </summary>
|
||||
public string? GetExecutionState()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return UseHandle(vmPtr =>
|
||||
{
|
||||
return CheckAndDropResult(API.regorus_rvm_get_execution_state((RegorusRvm*)vmPtr));
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (System.Threading.Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
|
||||
{
|
||||
_handle?.Dispose();
|
||||
_handle = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
if (_isDisposed != 0 || _handle is null || _handle.IsClosed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Rvm));
|
||||
}
|
||||
}
|
||||
|
||||
internal RegorusRvmHandle GetHandleForUse()
|
||||
{
|
||||
var handle = _handle;
|
||||
if (handle is null || handle.IsClosed || handle.IsInvalid)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Rvm));
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
internal T UseHandle<T>(Func<IntPtr, T> func)
|
||||
{
|
||||
var handle = GetHandleForUse();
|
||||
bool addedRef = false;
|
||||
try
|
||||
{
|
||||
handle.DangerousAddRef(ref addedRef);
|
||||
var pointer = handle.DangerousGetHandle();
|
||||
if (pointer == IntPtr.Zero)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Rvm));
|
||||
}
|
||||
|
||||
return func(pointer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (addedRef)
|
||||
{
|
||||
handle.DangerousRelease();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Rvm GetRvmResult(RegorusResult result)
|
||||
{
|
||||
try
|
||||
@@ -266,27 +221,7 @@ namespace Regorus
|
||||
|
||||
private static string? CheckAndDropResult(RegorusResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (result.status != RegorusStatus.Ok)
|
||||
{
|
||||
var message = Utf8Marshaller.FromUtf8(result.error_message);
|
||||
throw result.status.CreateException(message);
|
||||
}
|
||||
|
||||
return result.data_type switch
|
||||
{
|
||||
RegorusDataType.String => Utf8Marshaller.FromUtf8(result.output),
|
||||
RegorusDataType.Boolean => result.bool_value.ToString().ToLowerInvariant(),
|
||||
RegorusDataType.Integer => result.int_value.ToString(),
|
||||
RegorusDataType.None => null,
|
||||
_ => Utf8Marshaller.FromUtf8(result.output)
|
||||
};
|
||||
}
|
||||
finally
|
||||
{
|
||||
API.regorus_result_drop(result);
|
||||
}
|
||||
return ResultHelpers.GetStringResult(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
272
bindings/csharp/Regorus/SafeHandleWrapper.cs
Normal file
272
bindings/csharp/Regorus/SafeHandleWrapper.cs
Normal file
@@ -0,0 +1,272 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#nullable enable
|
||||
namespace Regorus
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for native handle wrappers that coordinates handle usage and disposal.
|
||||
///
|
||||
/// Behavior summary:
|
||||
/// - UseHandle: blocks Dispose while running; throws ObjectDisposedException if disposal has started or the handle is invalid.
|
||||
/// - Dispose: marks disposing and blocks new calls; waits briefly for in-flight calls to finish, then defers native release to the last exiting call if needed.
|
||||
/// - Handles are never exposed directly; derived classes can only work through UseHandle helpers.
|
||||
///
|
||||
/// Concurrency model:
|
||||
/// - _state tracks lifecycle transitions (Active -> DisposeRequested -> Released).
|
||||
/// - HandleGate tracks in-flight operations and enforces the "no new calls after Dispose" rule.
|
||||
/// - SafeHandle is pinned per call via DangerousAddRef to prevent use-after-free while native work runs.
|
||||
/// - If Dispose times out, the last in-flight caller performs the release to avoid leaks.
|
||||
/// </summary>
|
||||
public abstract class SafeHandleWrapper : IDisposable
|
||||
{
|
||||
private static readonly TimeSpan DefaultDisposeTimeout = TimeSpan.FromMilliseconds(50);
|
||||
private const int StateActive = 0;
|
||||
private const int StateDisposeRequested = 1;
|
||||
private const int StateReleased = 2;
|
||||
private readonly HandleGate _gate;
|
||||
private readonly string _ownerName;
|
||||
private int _state;
|
||||
private SafeHandle? _handle;
|
||||
|
||||
protected SafeHandleWrapper(SafeHandle handle, string ownerName)
|
||||
{
|
||||
// Cache ownership info and initialize the gate before any use to avoid racing disposal.
|
||||
_handle = handle ?? throw new ArgumentNullException(nameof(handle));
|
||||
_ownerName = ownerName ?? throw new ArgumentNullException(nameof(ownerName));
|
||||
_gate = new HandleGate(ownerName);
|
||||
// Default to a very short wait when in-flight calls exist; release is deferred to the last caller if needed.
|
||||
}
|
||||
|
||||
protected void UseHandle(Action<IntPtr> action)
|
||||
{
|
||||
// Reuse the generic path to keep add/ref/release in one place.
|
||||
UseHandle<object?>(ptr =>
|
||||
{
|
||||
action(ptr);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
protected T UseHandle<T>(Func<IntPtr, T> func)
|
||||
{
|
||||
// Fast reject if dispose was requested.
|
||||
if (System.Threading.Volatile.Read(ref _state) != StateActive)
|
||||
{
|
||||
throw new ObjectDisposedException(_ownerName);
|
||||
}
|
||||
|
||||
// Enter gate so Dispose waits for in-flight native calls.
|
||||
_gate.Enter();
|
||||
bool addedRef = false;
|
||||
SafeHandle? handle = null;
|
||||
try
|
||||
{
|
||||
// Race: Dispose could begin after Enter; GetHandleForUse validates the handle again.
|
||||
handle = GetHandleForUse();
|
||||
// DangerousAddRef pins the SafeHandle so Dispose cannot close it mid-call.
|
||||
handle.DangerousAddRef(ref addedRef);
|
||||
var pointer = handle.DangerousGetHandle();
|
||||
// Validate pointer after AddRef in case handle became invalid between checks.
|
||||
if (pointer == IntPtr.Zero)
|
||||
{
|
||||
throw new ObjectDisposedException(_ownerName);
|
||||
}
|
||||
|
||||
return func(pointer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Always release the DangerousAddRef to avoid leaking the native handle.
|
||||
if (addedRef)
|
||||
{
|
||||
handle?.DangerousRelease();
|
||||
}
|
||||
|
||||
// Leave gate so Dispose can proceed when the last caller exits.
|
||||
var idle = _gate.Exit();
|
||||
// Race: Dispose may have timed out while we were in-flight.
|
||||
// The last exiting caller performs the native release to avoid leaks.
|
||||
if (idle && System.Threading.Volatile.Read(ref _state) == StateDisposeRequested)
|
||||
{
|
||||
TryReleaseHandle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal T UseHandleForInterop<T>(Func<IntPtr, T> func)
|
||||
{
|
||||
// Explicit alias for interop-specific call sites.
|
||||
return UseHandle(func);
|
||||
}
|
||||
|
||||
internal void UseHandleForInterop(Action<IntPtr> action)
|
||||
{
|
||||
// Explicit alias for interop-specific call sites.
|
||||
UseHandle(action);
|
||||
}
|
||||
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
// Fast check for dispose state so callers fail deterministically.
|
||||
if (System.Threading.Volatile.Read(ref _state) != StateActive)
|
||||
{
|
||||
throw new ObjectDisposedException(_ownerName);
|
||||
}
|
||||
|
||||
// Validate the underlying SafeHandle is still usable; avoids races with release.
|
||||
var handle = _handle;
|
||||
if (handle is null || handle.IsClosed || handle.IsInvalid)
|
||||
{
|
||||
throw new ObjectDisposedException(_ownerName);
|
||||
}
|
||||
}
|
||||
|
||||
private SafeHandle GetHandleForUse()
|
||||
{
|
||||
// Centralized gate for derived classes to grab the handle safely.
|
||||
// This is a second line of defense in case disposal began after the initial state check.
|
||||
var handle = _handle;
|
||||
if (handle is null || handle.IsClosed || handle.IsInvalid)
|
||||
{
|
||||
throw new ObjectDisposedException(_ownerName);
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Only the first caller runs disposal; others become no-ops.
|
||||
if (System.Threading.Interlocked.CompareExchange(ref _state, StateDisposeRequested, StateActive) == StateActive)
|
||||
{
|
||||
// Block new calls and wait briefly if there are in-flight operations.
|
||||
var completed = _gate.TryBeginDispose(DefaultDisposeTimeout, out var hadActive);
|
||||
if (completed)
|
||||
{
|
||||
// Either no active calls or they drained within the short timeout.
|
||||
TryReleaseHandle();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Defer release to the last in-flight caller to avoid leaks without blocking indefinitely.
|
||||
// Race: if the last in-flight caller already exited, there will be no Exit() to trigger release.
|
||||
// Re-check active state and release immediately in that case.
|
||||
if (!hadActive || _gate.IsIdle)
|
||||
{
|
||||
TryReleaseHandle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void TryReleaseHandle()
|
||||
{
|
||||
if (System.Threading.Interlocked.CompareExchange(ref _state, StateReleased, StateDisposeRequested) != StateDisposeRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Once released, no caller should be able to observe a valid handle.
|
||||
// SafeHandle.Dispose closes the native resource; null to prevent reuse after dispose.
|
||||
_handle?.Dispose();
|
||||
_handle = null;
|
||||
// Release the wait handle resources after disposal completes.
|
||||
_gate.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tracks in-flight operations and coordinates disposal.
|
||||
/// </summary>
|
||||
private sealed class HandleGate : IDisposable
|
||||
{
|
||||
private readonly string _ownerName;
|
||||
private readonly System.Threading.ManualResetEventSlim _idle = new(initialState: true);
|
||||
private int _active;
|
||||
private int _disposing;
|
||||
|
||||
internal HandleGate(string ownerName)
|
||||
{
|
||||
_ownerName = ownerName;
|
||||
}
|
||||
|
||||
internal void Enter()
|
||||
{
|
||||
// If disposal already started, reject new work immediately.
|
||||
if (System.Threading.Volatile.Read(ref _disposing) != 0)
|
||||
{
|
||||
ThrowDisposed();
|
||||
}
|
||||
|
||||
// Track active callers; first one resets idle event.
|
||||
var active = System.Threading.Interlocked.Increment(ref _active);
|
||||
if (active == 1)
|
||||
{
|
||||
_idle.Reset();
|
||||
}
|
||||
|
||||
// Re-check disposing to handle races where Dispose began after increment.
|
||||
if (System.Threading.Volatile.Read(ref _disposing) != 0)
|
||||
{
|
||||
Exit();
|
||||
ThrowDisposed();
|
||||
}
|
||||
}
|
||||
|
||||
internal bool Exit()
|
||||
{
|
||||
// Last caller signals idle so Dispose can continue.
|
||||
if (System.Threading.Interlocked.Decrement(ref _active) == 0)
|
||||
{
|
||||
_idle.Set();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal bool IsIdle => System.Threading.Volatile.Read(ref _active) == 0;
|
||||
|
||||
internal bool TryBeginDispose(TimeSpan timeout, out bool hadActive)
|
||||
{
|
||||
// Set disposing flag once; subsequent calls treat as already disposing.
|
||||
if (System.Threading.Interlocked.Exchange(ref _disposing, 1) != 0)
|
||||
{
|
||||
hadActive = System.Threading.Volatile.Read(ref _active) != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
hadActive = System.Threading.Volatile.Read(ref _active) != 0;
|
||||
if (!hadActive)
|
||||
{
|
||||
// No in-flight callers; disposal can proceed without waiting.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Wait for active callers to drain; optional timeout avoids blocking forever.
|
||||
if (timeout == System.Threading.Timeout.InfiniteTimeSpan)
|
||||
{
|
||||
_idle.Wait();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Race note: callers may finish between the timeout decision and Wait call; Wait handles that safely.
|
||||
return _idle.Wait(timeout);
|
||||
}
|
||||
|
||||
private void ThrowDisposed()
|
||||
{
|
||||
throw new ObjectDisposedException(_ownerName);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_idle.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ namespace Regorus
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
if (!IsInvalid && !IsClosed)
|
||||
if (!IsInvalid)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
@@ -76,7 +76,7 @@ namespace Regorus
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
if (!IsInvalid && !IsClosed)
|
||||
if (!IsInvalid)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
@@ -124,7 +124,7 @@ namespace Regorus
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
if (!IsInvalid && !IsClosed)
|
||||
if (!IsInvalid)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
@@ -172,7 +172,7 @@ namespace Regorus
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
if (!IsInvalid && !IsClosed)
|
||||
if (!IsInvalid)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
@@ -183,4 +183,52 @@ namespace Regorus
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class RegorusAliasRegistryHandle : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
private RegorusAliasRegistryHandle() : base(ownsHandle: true)
|
||||
{
|
||||
}
|
||||
|
||||
internal static RegorusAliasRegistryHandle Create()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var raw = Internal.API.regorus_alias_registry_new();
|
||||
if (raw is null)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to create Regorus alias registry.");
|
||||
}
|
||||
|
||||
var handle = new RegorusAliasRegistryHandle();
|
||||
handle.SetHandle((IntPtr)raw);
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
internal static RegorusAliasRegistryHandle FromPointer(IntPtr pointer)
|
||||
{
|
||||
if (pointer == IntPtr.Zero)
|
||||
{
|
||||
throw new ArgumentException("Pointer cannot be zero.", nameof(pointer));
|
||||
}
|
||||
|
||||
var handle = new RegorusAliasRegistryHandle();
|
||||
handle.SetHandle(pointer);
|
||||
return handle;
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
if (!IsInvalid)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
Internal.API.regorus_alias_registry_drop((Internal.RegorusAliasRegistry*)handle);
|
||||
}
|
||||
SetHandle(IntPtr.Zero);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using Regorus.Internal;
|
||||
|
||||
#nullable enable
|
||||
@@ -27,7 +29,7 @@ namespace Regorus
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
CheckAndDropResult(Internal.API.regorus_resource_schema_register((byte*)namePtr, (byte*)schemaPtr));
|
||||
ResultHelpers.GetStringResult(Internal.API.regorus_resource_schema_register((byte*)namePtr, (byte*)schemaPtr));
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -46,7 +48,7 @@ namespace Regorus
|
||||
unsafe
|
||||
{
|
||||
var result = Internal.API.regorus_resource_schema_contains((byte*)namePtr);
|
||||
return GetBoolResult(result);
|
||||
return ResultHelpers.GetBoolResult(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -61,7 +63,7 @@ namespace Regorus
|
||||
get
|
||||
{
|
||||
var result = Internal.API.regorus_resource_schema_len();
|
||||
return GetIntResult(result);
|
||||
return ResultHelpers.GetIntResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +77,7 @@ namespace Regorus
|
||||
get
|
||||
{
|
||||
var result = Internal.API.regorus_resource_schema_is_empty();
|
||||
return GetBoolResult(result);
|
||||
return ResultHelpers.GetBoolResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +88,16 @@ namespace Regorus
|
||||
/// <exception cref="Exception">Thrown when the operation fails</exception>
|
||||
public static string ListResourceNames()
|
||||
{
|
||||
return CheckAndDropResult(Internal.API.regorus_resource_schema_list_names()) ?? "[]";
|
||||
return ResultHelpers.GetStringResult(Internal.API.regorus_resource_schema_list_names()) ?? "[]";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all registered resource schema names as managed strings.
|
||||
/// </summary>
|
||||
public static IReadOnlyList<string> GetResourceNames()
|
||||
{
|
||||
var json = ListResourceNames();
|
||||
return JsonSerializer.Deserialize<string[]>(json) ?? Array.Empty<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -102,7 +113,7 @@ namespace Regorus
|
||||
unsafe
|
||||
{
|
||||
var result = Internal.API.regorus_resource_schema_remove((byte*)namePtr);
|
||||
return GetBoolResult(result);
|
||||
return ResultHelpers.GetBoolResult(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -113,7 +124,7 @@ namespace Regorus
|
||||
/// <exception cref="Exception">Thrown when the operation fails</exception>
|
||||
public static void ClearResources()
|
||||
{
|
||||
CheckAndDropResult(Internal.API.regorus_resource_schema_clear());
|
||||
ResultHelpers.GetStringResult(Internal.API.regorus_resource_schema_clear());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -130,7 +141,7 @@ namespace Regorus
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
CheckAndDropResult(Internal.API.regorus_effect_schema_register((byte*)namePtr, (byte*)schemaPtr));
|
||||
ResultHelpers.GetStringResult(Internal.API.regorus_effect_schema_register((byte*)namePtr, (byte*)schemaPtr));
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -149,7 +160,7 @@ namespace Regorus
|
||||
unsafe
|
||||
{
|
||||
var result = Internal.API.regorus_effect_schema_contains((byte*)namePtr);
|
||||
return GetBoolResult(result);
|
||||
return ResultHelpers.GetBoolResult(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -164,7 +175,7 @@ namespace Regorus
|
||||
get
|
||||
{
|
||||
var result = Internal.API.regorus_effect_schema_len();
|
||||
return GetIntResult(result);
|
||||
return ResultHelpers.GetIntResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +189,7 @@ namespace Regorus
|
||||
get
|
||||
{
|
||||
var result = Internal.API.regorus_effect_schema_is_empty();
|
||||
return GetBoolResult(result);
|
||||
return ResultHelpers.GetBoolResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +200,16 @@ namespace Regorus
|
||||
/// <exception cref="Exception">Thrown when the operation fails</exception>
|
||||
public static string ListEffectNames()
|
||||
{
|
||||
return CheckAndDropResult(Internal.API.regorus_effect_schema_list_names()) ?? "[]";
|
||||
return ResultHelpers.GetStringResult(Internal.API.regorus_effect_schema_list_names()) ?? "[]";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all registered effect schema names as managed strings.
|
||||
/// </summary>
|
||||
public static IReadOnlyList<string> GetEffectNames()
|
||||
{
|
||||
var json = ListEffectNames();
|
||||
return JsonSerializer.Deserialize<string[]>(json) ?? Array.Empty<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -205,7 +225,7 @@ namespace Regorus
|
||||
unsafe
|
||||
{
|
||||
var result = Internal.API.regorus_effect_schema_remove((byte*)namePtr);
|
||||
return GetBoolResult(result);
|
||||
return ResultHelpers.GetBoolResult(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -216,68 +236,7 @@ namespace Regorus
|
||||
/// <exception cref="Exception">Thrown when the operation fails</exception>
|
||||
public static void ClearEffects()
|
||||
{
|
||||
CheckAndDropResult(Internal.API.regorus_effect_schema_clear());
|
||||
}
|
||||
|
||||
private static string? CheckAndDropResult(Internal.RegorusResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (result.status != Internal.RegorusStatus.Ok)
|
||||
{
|
||||
var message = Utf8Marshaller.FromUtf8(result.error_message);
|
||||
throw result.status.CreateException(message);
|
||||
}
|
||||
|
||||
return result.data_type switch
|
||||
{
|
||||
Internal.RegorusDataType.String => Utf8Marshaller.FromUtf8(result.output),
|
||||
Internal.RegorusDataType.Boolean => result.bool_value.ToString().ToLowerInvariant(),
|
||||
Internal.RegorusDataType.Integer => result.int_value.ToString(),
|
||||
Internal.RegorusDataType.None => null,
|
||||
_ => Utf8Marshaller.FromUtf8(result.output)
|
||||
};
|
||||
}
|
||||
finally
|
||||
{
|
||||
Internal.API.regorus_result_drop(result);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool GetBoolResult(Internal.RegorusResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (result.status != Internal.RegorusStatus.Ok)
|
||||
{
|
||||
var message = Utf8Marshaller.FromUtf8(result.error_message);
|
||||
throw result.status.CreateException(message);
|
||||
}
|
||||
|
||||
return result.data_type == Internal.RegorusDataType.Boolean ? result.bool_value : false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Internal.API.regorus_result_drop(result);
|
||||
}
|
||||
}
|
||||
|
||||
private static long GetIntResult(Internal.RegorusResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (result.status != Internal.RegorusStatus.Ok)
|
||||
{
|
||||
var message = Utf8Marshaller.FromUtf8(result.error_message);
|
||||
throw result.status.CreateException(message);
|
||||
}
|
||||
|
||||
return result.data_type == Internal.RegorusDataType.Integer ? result.int_value : 0;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Internal.API.regorus_result_drop(result);
|
||||
}
|
||||
ResultHelpers.GetStringResult(Internal.API.regorus_effect_schema_clear());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using Regorus.Internal;
|
||||
|
||||
#nullable enable
|
||||
@@ -26,7 +28,7 @@ namespace Regorus
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
CheckAndDropResult(Internal.API.regorus_register_target_from_json((byte*)targetPtr));
|
||||
ResultHelpers.GetStringResult(Internal.API.regorus_register_target_from_json((byte*)targetPtr));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -44,7 +46,7 @@ namespace Regorus
|
||||
unsafe
|
||||
{
|
||||
var result = Internal.API.regorus_target_registry_contains((byte*)namePtr);
|
||||
return GetBoolResult(result);
|
||||
return ResultHelpers.GetBoolResult(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -56,7 +58,16 @@ namespace Regorus
|
||||
/// <exception cref="Exception">Thrown when the operation fails</exception>
|
||||
public static string ListNames()
|
||||
{
|
||||
return CheckAndDropResult(Internal.API.regorus_target_registry_list_names()) ?? "[]";
|
||||
return ResultHelpers.GetStringResult(Internal.API.regorus_target_registry_list_names()) ?? "[]";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a list of all registered target names as managed strings.
|
||||
/// </summary>
|
||||
public static IReadOnlyList<string> GetNames()
|
||||
{
|
||||
var json = ListNames();
|
||||
return JsonSerializer.Deserialize<string[]>(json) ?? Array.Empty<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -72,7 +83,7 @@ namespace Regorus
|
||||
unsafe
|
||||
{
|
||||
var result = Internal.API.regorus_target_registry_remove((byte*)namePtr);
|
||||
return GetBoolResult(result);
|
||||
return ResultHelpers.GetBoolResult(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -83,7 +94,7 @@ namespace Regorus
|
||||
/// <exception cref="Exception">Thrown when the operation fails</exception>
|
||||
public static void Clear()
|
||||
{
|
||||
CheckAndDropResult(Internal.API.regorus_target_registry_clear());
|
||||
ResultHelpers.GetStringResult(Internal.API.regorus_target_registry_clear());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -96,10 +107,9 @@ namespace Regorus
|
||||
get
|
||||
{
|
||||
var result = Internal.API.regorus_target_registry_len();
|
||||
return GetIntResult(result);
|
||||
return ResultHelpers.GetIntResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the target registry is empty.
|
||||
/// </summary>
|
||||
@@ -110,68 +120,7 @@ namespace Regorus
|
||||
get
|
||||
{
|
||||
var result = Internal.API.regorus_target_registry_is_empty();
|
||||
return GetBoolResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
private static string? CheckAndDropResult(Internal.RegorusResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (result.status != Internal.RegorusStatus.Ok)
|
||||
{
|
||||
var message = Utf8Marshaller.FromUtf8(result.error_message);
|
||||
throw result.status.CreateException(message);
|
||||
}
|
||||
|
||||
return result.data_type switch
|
||||
{
|
||||
Internal.RegorusDataType.String => Utf8Marshaller.FromUtf8(result.output),
|
||||
Internal.RegorusDataType.Boolean => result.bool_value.ToString().ToLowerInvariant(),
|
||||
Internal.RegorusDataType.Integer => result.int_value.ToString(),
|
||||
Internal.RegorusDataType.None => null,
|
||||
_ => Utf8Marshaller.FromUtf8(result.output)
|
||||
};
|
||||
}
|
||||
finally
|
||||
{
|
||||
Internal.API.regorus_result_drop(result);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool GetBoolResult(Internal.RegorusResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (result.status != Internal.RegorusStatus.Ok)
|
||||
{
|
||||
var message = Utf8Marshaller.FromUtf8(result.error_message);
|
||||
throw result.status.CreateException(message);
|
||||
}
|
||||
|
||||
return result.data_type == Internal.RegorusDataType.Boolean ? result.bool_value : false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Internal.API.regorus_result_drop(result);
|
||||
}
|
||||
}
|
||||
|
||||
private static long GetIntResult(Internal.RegorusResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (result.status != Internal.RegorusStatus.Ok)
|
||||
{
|
||||
var message = Utf8Marshaller.FromUtf8(result.error_message);
|
||||
throw result.status.CreateException(message);
|
||||
}
|
||||
|
||||
return result.data_type == Internal.RegorusDataType.Integer ? result.int_value : 0;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Internal.API.regorus_result_drop(result);
|
||||
return ResultHelpers.GetBoolResult(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@ namespace Regorus.Internal
|
||||
/// </summary>
|
||||
internal static class Utf8Marshaller
|
||||
{
|
||||
// Mirrors BCL patterns (e.g., System.Text.Json encoding helpers) by stackalloc'ing
|
||||
// up to 512 bytes to cover common short strings while keeping the stack usage well
|
||||
// below typical per-frame limits; larger payloads fall back to pooled buffers.
|
||||
private const int StackAllocThreshold = 512;
|
||||
// Mirrors BCL patterns (e.g., System.Text.Json encoding helpers) by stackalloc'ing
|
||||
// up to 512 bytes to cover common short strings while keeping the stack usage well
|
||||
// below typical per-frame limits; larger payloads fall back to pooled buffers.
|
||||
private const int StackAllocThreshold = 512;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a pooled and pinned UTF-8 buffer suitable for scenarios where
|
||||
|
||||
@@ -65,7 +65,7 @@ triplet_count := count([1 |
|
||||
private const string EXECUTION_TIMER_QUERY = "data.limits.timer.triplet_count";
|
||||
private const int EXECUTION_TIMER_VALUE_COUNT = 40;
|
||||
|
||||
private const string RVM_POLICY = """
|
||||
private const string RVM_POLICY = """
|
||||
package demo
|
||||
import rego.v1
|
||||
|
||||
@@ -78,7 +78,7 @@ allow if {
|
||||
}
|
||||
""";
|
||||
|
||||
private const string RVM_DATA = """
|
||||
private const string RVM_DATA = """
|
||||
{
|
||||
"roles": {
|
||||
"alice": ["admin", "reader"]
|
||||
@@ -86,13 +86,13 @@ allow if {
|
||||
}
|
||||
""";
|
||||
|
||||
private const string RVM_INPUT = """
|
||||
private const string RVM_INPUT = """
|
||||
{
|
||||
"user": "alice"
|
||||
}
|
||||
""";
|
||||
|
||||
private const string HOST_AWAIT_POLICY = """
|
||||
private const string HOST_AWAIT_POLICY = """
|
||||
package demo
|
||||
import rego.v1
|
||||
|
||||
@@ -105,7 +105,7 @@ allow if {
|
||||
}
|
||||
""";
|
||||
|
||||
private const string HOST_AWAIT_INPUT = """
|
||||
private const string HOST_AWAIT_INPUT = """
|
||||
{
|
||||
"account": {
|
||||
"id": "acct-1",
|
||||
@@ -216,7 +216,7 @@ allow if {
|
||||
|
||||
var nonCompliantResult = compiledPolicy.EvalWithInput(NON_COMPLIANT_STORAGE_ACCOUNT);
|
||||
Console.WriteLine($"Result: {nonCompliantResult}");
|
||||
|
||||
|
||||
// 4. Demonstrate thread-safe concurrent evaluation
|
||||
Console.WriteLine("\n4. Testing concurrent evaluation from multiple threads:");
|
||||
DemonstrateConcurrentEvaluation(compiledPolicy);
|
||||
@@ -246,42 +246,44 @@ allow if {
|
||||
};
|
||||
|
||||
Console.WriteLine($"Starting {testInputs.Length} concurrent evaluations...");
|
||||
|
||||
var tasks = testInputs.Select(input =>
|
||||
Task.Run(() => {
|
||||
|
||||
var tasks = testInputs.Select(input =>
|
||||
Task.Run(() =>
|
||||
{
|
||||
var (threadName, json) = input;
|
||||
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
|
||||
|
||||
// Multiple evaluations per thread to stress test
|
||||
var results = new List<string>();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
var result = compiledPolicy.EvalWithInput(json);
|
||||
var result = compiledPolicy.EvalWithInput(json)
|
||||
?? throw new System.InvalidOperationException("Expected EvalWithInput to return a JSON value.");
|
||||
results.Add(result);
|
||||
}
|
||||
|
||||
|
||||
stopwatch.Stop();
|
||||
var microseconds = stopwatch.ElapsedTicks * 1000000 / System.Diagnostics.Stopwatch.Frequency;
|
||||
|
||||
|
||||
// Verify all results are identical (thread safety)
|
||||
var firstResult = results[0];
|
||||
var allIdentical = results.All(r => r == firstResult);
|
||||
|
||||
|
||||
Console.WriteLine($"✓ {threadName}: {results.Count} evaluations in {microseconds}μs, " +
|
||||
$"Results consistent: {allIdentical}");
|
||||
|
||||
|
||||
return (threadName, results.Count, microseconds, allIdentical);
|
||||
})
|
||||
).ToArray();
|
||||
|
||||
// Wait for all threads to complete
|
||||
var results = Task.WhenAll(tasks).Result;
|
||||
|
||||
|
||||
Console.WriteLine("\nConcurrency test results:");
|
||||
var totalEvaluations = results.Sum(r => r.Item2);
|
||||
var maxTime = results.Max(r => r.Item3);
|
||||
var allConsistent = results.All(r => r.allIdentical);
|
||||
|
||||
|
||||
Console.WriteLine($"✓ Total evaluations: {totalEvaluations}");
|
||||
Console.WriteLine($"✓ Max thread time: {maxTime}μs");
|
||||
Console.WriteLine($"✓ All threads consistent: {allConsistent}");
|
||||
@@ -292,28 +294,28 @@ allow if {
|
||||
static void DemonstratePolicyInfo(Regorus.CompiledPolicy compiledPolicy)
|
||||
{
|
||||
Console.WriteLine("Getting policy metadata using GetPolicyInfo()...");
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
var policyInfo = compiledPolicy.GetPolicyInfo();
|
||||
|
||||
|
||||
Console.WriteLine($"✓ Policy Information Retrieved:");
|
||||
Console.WriteLine($" Target Name: {policyInfo.TargetName ?? "None"}");
|
||||
Console.WriteLine($" Effect Rule: {policyInfo.EffectRule ?? "None"}");
|
||||
Console.WriteLine($" Entrypoint Rule: {policyInfo.EntrypointRule}");
|
||||
|
||||
|
||||
Console.WriteLine($" Module IDs ({policyInfo.ModuleIds.Count}):");
|
||||
foreach (var moduleId in policyInfo.ModuleIds)
|
||||
{
|
||||
Console.WriteLine($" - {moduleId}");
|
||||
}
|
||||
|
||||
|
||||
Console.WriteLine($" Applicable Resource Types ({policyInfo.ApplicableResourceTypes.Count}):");
|
||||
foreach (var resourceType in policyInfo.ApplicableResourceTypes)
|
||||
{
|
||||
Console.WriteLine($" - {resourceType}");
|
||||
}
|
||||
|
||||
|
||||
if (policyInfo.Parameters != null && policyInfo.Parameters.Count > 0)
|
||||
{
|
||||
Console.WriteLine($" Policy Parameters:");
|
||||
@@ -333,7 +335,7 @@ allow if {
|
||||
Console.WriteLine($" Description: {param.Description}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (parameterSet.Modifiers.Count > 0)
|
||||
{
|
||||
Console.WriteLine($" Modifiers ({parameterSet.Modifiers.Count}):");
|
||||
@@ -348,11 +350,11 @@ allow if {
|
||||
{
|
||||
Console.WriteLine(" No parameter information available");
|
||||
}
|
||||
|
||||
|
||||
// Demonstrate JSON serialization of policy info
|
||||
Console.WriteLine("\n✓ Policy Info as JSON:");
|
||||
var jsonOptions = new JsonSerializerOptions
|
||||
{
|
||||
var jsonOptions = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
|
||||
@@ -18,8 +18,13 @@ var w = new Stopwatch();
|
||||
|
||||
w.Restart();
|
||||
|
||||
// Configure the global pattern caches.
|
||||
Regorus.Engine.SetCacheConfig(new Regorus.CacheConfig(regex: 256, glob: 128));
|
||||
|
||||
var engine = new Regorus.Engine();
|
||||
engine.SetRegoV0(true);
|
||||
// Raise the default col limit to 2000
|
||||
engine.SetPolicyLengthConfig(new Regorus.PolicyLengthConfig(maxCol: 2000, maxFileBytes: 1048576, maxLines: 20000));
|
||||
|
||||
w.Stop();
|
||||
var newEngineTicks = w.ElapsedTicks;
|
||||
@@ -42,7 +47,8 @@ w.Restart();
|
||||
|
||||
// Set input and eval rule.
|
||||
engine.SetInputFromJsonFile("../../../tests/aci/input.json");
|
||||
var value = engine.EvalRule("data.framework.mount_overlay");
|
||||
var value = engine.EvalRule("data.framework.mount_overlay")
|
||||
?? throw new System.InvalidOperationException("Expected EvalRule to return a JSON value.");
|
||||
|
||||
#if NET8_0_OR_GREATER
|
||||
var valueDoc = System.Text.Json.JsonDocument.Parse(value);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"msbuild-sdks": {
|
||||
"Microsoft.Build.NoTargets": "3.7.56"
|
||||
},
|
||||
"sdk": {
|
||||
"allowPrerelease": false,
|
||||
"version": "8.0.412",
|
||||
"rollForward": "latestFeature"
|
||||
}
|
||||
"msbuild-sdks": {
|
||||
"Microsoft.Build.NoTargets": "3.7.134"
|
||||
},
|
||||
"sdk": {
|
||||
"allowPrerelease": false,
|
||||
"version": "8.0.412",
|
||||
"rollForward": "latestFeature"
|
||||
}
|
||||
}
|
||||
574
bindings/ffi/Cargo.lock
generated
574
bindings/ffi/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -2,8 +2,9 @@
|
||||
|
||||
[package]
|
||||
name = "regorus-ffi"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
edition = "2021"
|
||||
license = "MIT AND Apache-2.0 AND BSD-3-Clause"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[lib]
|
||||
@@ -31,8 +32,10 @@ default = [
|
||||
"coverage",
|
||||
"allocator-memory-limits",
|
||||
"rvm",
|
||||
"rbac",
|
||||
"regorus/arc",
|
||||
"regorus/full-opa",
|
||||
"cache",
|
||||
"contention_checks",
|
||||
]
|
||||
ast = ["regorus/ast"]
|
||||
@@ -42,6 +45,8 @@ coverage = ["regorus/coverage"]
|
||||
allocator-memory-limits = ["regorus/allocator-memory-limits"]
|
||||
contention_checks = ["parking_lot"]
|
||||
rvm = ["regorus/rvm"]
|
||||
rbac = ["regorus/azure-rbac"]
|
||||
cache = ["regorus/cache"]
|
||||
custom_allocator = []
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
479
bindings/ffi/src/alias_registry.rs
Normal file
479
bindings/ffi/src/alias_registry.rs
Normal file
@@ -0,0 +1,479 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
//! FFI bindings for `AliasRegistry` – Azure Policy alias catalog management.
|
||||
|
||||
#![cfg(feature = "azure_policy")]
|
||||
|
||||
use crate::common::{from_c_str, to_ref, RegorusResult, RegorusStatus};
|
||||
use crate::panic_guard::with_unwind_guard;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
use anyhow::Result;
|
||||
use core::ffi::c_char;
|
||||
use core::ptr;
|
||||
|
||||
use regorus::languages::azure_policy::aliases::AliasRegistry;
|
||||
|
||||
/// Opaque wrapper for `AliasRegistry`.
|
||||
pub struct RegorusAliasRegistry {
|
||||
registry: AliasRegistry,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Lifecycle
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Create a new, empty `AliasRegistry`.
|
||||
///
|
||||
/// The caller must eventually call `regorus_alias_registry_drop` to free the handle.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_alias_registry_new() -> *mut RegorusAliasRegistry {
|
||||
let wrapper = RegorusAliasRegistry {
|
||||
registry: AliasRegistry::new(),
|
||||
};
|
||||
Box::into_raw(Box::new(wrapper))
|
||||
}
|
||||
|
||||
/// Drop a `RegorusAliasRegistry`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_alias_registry_drop(registry: *mut RegorusAliasRegistry) {
|
||||
if let Ok(r) = to_ref(registry) {
|
||||
unsafe {
|
||||
let _ = Box::from_raw(ptr::from_mut(r));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Loading
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Load control-plane alias data (array of `ProviderAliases`) into the registry.
|
||||
///
|
||||
/// `json` must be a valid null-terminated UTF-8 string containing the JSON
|
||||
/// array returned by `Get-AzPolicyAlias` or the static
|
||||
/// `ResourceTypesAndAliases.json` file.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_alias_registry_load_json(
|
||||
registry: *mut RegorusAliasRegistry,
|
||||
json: *const c_char,
|
||||
) -> RegorusResult {
|
||||
with_unwind_guard(|| {
|
||||
let output = || -> Result<()> {
|
||||
let json_str = from_c_str(json)?;
|
||||
to_ref(registry)?.registry.load_from_json(&json_str)?;
|
||||
Ok(())
|
||||
}();
|
||||
|
||||
match output {
|
||||
Ok(()) => RegorusResult::ok_void(),
|
||||
Err(e) => RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidDataFormat,
|
||||
format!("Failed to load alias catalog: {e}"),
|
||||
),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Load a data-plane policy manifest into the registry.
|
||||
///
|
||||
/// `json` must be a valid null-terminated UTF-8 string containing a single
|
||||
/// `DataPolicyManifest` JSON object.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_alias_registry_load_manifest(
|
||||
registry: *mut RegorusAliasRegistry,
|
||||
json: *const c_char,
|
||||
) -> RegorusResult {
|
||||
with_unwind_guard(|| {
|
||||
let output = || -> Result<()> {
|
||||
let json_str = from_c_str(json)?;
|
||||
to_ref(registry)?
|
||||
.registry
|
||||
.load_data_policy_manifest_json(&json_str)?;
|
||||
Ok(())
|
||||
}();
|
||||
|
||||
match output {
|
||||
Ok(()) => RegorusResult::ok_void(),
|
||||
Err(e) => RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidDataFormat,
|
||||
format!("Failed to load data-plane manifest: {e}"),
|
||||
),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Queries
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Return the number of resource types loaded in the alias registry.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_alias_registry_len(registry: *mut RegorusAliasRegistry) -> RegorusResult {
|
||||
with_unwind_guard(|| {
|
||||
let output = || -> Result<i64> {
|
||||
let len = to_ref(registry)?.registry.len();
|
||||
Ok(len as i64)
|
||||
}();
|
||||
|
||||
match output {
|
||||
Ok(n) => RegorusResult::ok_int(n),
|
||||
Err(e) => RegorusResult::err_with_message(RegorusStatus::Error, format!("{e}")),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Normalize / Denormalize
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Normalize an ARM resource JSON and wrap it into the standard input envelope.
|
||||
///
|
||||
/// Returns a JSON string:
|
||||
/// `{ "resource": <normalized>, "context": <context>, "parameters": <params> }`.
|
||||
///
|
||||
/// * `resource_json` – raw ARM resource JSON
|
||||
/// * `api_version` – API version string (e.g. `"2023-01-01"`), or null to use
|
||||
/// the default alias paths
|
||||
/// * `context_json` – JSON object for additional context (pass `"{}"` if none)
|
||||
/// * `parameters_json` – JSON object of policy parameter values (pass `"{}"` if none)
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_alias_registry_normalize_and_wrap(
|
||||
registry: *mut RegorusAliasRegistry,
|
||||
resource_json: *const c_char,
|
||||
api_version: *const c_char,
|
||||
context_json: *const c_char,
|
||||
parameters_json: *const c_char,
|
||||
) -> RegorusResult {
|
||||
with_unwind_guard(|| {
|
||||
let output = || -> Result<String> {
|
||||
let resource_str = from_c_str(resource_json)?;
|
||||
let api_ver = if api_version.is_null() {
|
||||
None
|
||||
} else {
|
||||
let s = from_c_str(api_version)?;
|
||||
if s.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(s)
|
||||
}
|
||||
};
|
||||
let context_str = from_c_str(context_json)?;
|
||||
let params_str = from_c_str(parameters_json)?;
|
||||
|
||||
let resource = regorus::Value::from_json_str(&resource_str)?;
|
||||
let context = regorus::Value::from_json_str(&context_str)?;
|
||||
let params = regorus::Value::from_json_str(¶ms_str)?;
|
||||
|
||||
let wrapped = to_ref(registry)?.registry.normalize_and_wrap(
|
||||
&resource,
|
||||
api_ver.as_deref(),
|
||||
Some(context),
|
||||
Some(params),
|
||||
);
|
||||
wrapped.to_json_str()
|
||||
}();
|
||||
|
||||
match output {
|
||||
Ok(s) => RegorusResult::ok_string(s),
|
||||
Err(e) => RegorusResult::err_with_message(RegorusStatus::Error, format!("{e}")),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Denormalize a previously-normalized resource JSON back to ARM format.
|
||||
///
|
||||
/// * `normalized_json` – the normalized resource JSON
|
||||
/// * `api_version` – API version string, or null to use the default alias paths
|
||||
///
|
||||
/// Returns the denormalized ARM JSON string.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_alias_registry_denormalize(
|
||||
registry: *mut RegorusAliasRegistry,
|
||||
normalized_json: *const c_char,
|
||||
api_version: *const c_char,
|
||||
) -> RegorusResult {
|
||||
with_unwind_guard(|| {
|
||||
let output = || -> Result<String> {
|
||||
let normalized_str = from_c_str(normalized_json)?;
|
||||
let api_ver = if api_version.is_null() {
|
||||
None
|
||||
} else {
|
||||
let s = from_c_str(api_version)?;
|
||||
if s.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(s)
|
||||
}
|
||||
};
|
||||
|
||||
let normalized = regorus::Value::from_json_str(&normalized_str)?;
|
||||
|
||||
let result = to_ref(registry)?
|
||||
.registry
|
||||
.denormalize(&normalized, api_ver.as_deref());
|
||||
result.to_json_str()
|
||||
}();
|
||||
|
||||
match output {
|
||||
Ok(s) => RegorusResult::ok_string(s),
|
||||
Err(e) => RegorusResult::err_with_message(RegorusStatus::Error, format!("{e}")),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::common::regorus_result_drop;
|
||||
use core::ffi::CStr;
|
||||
use std::ffi::CString;
|
||||
|
||||
/// Helper: create a C string from a Rust &str.
|
||||
fn c(s: &str) -> CString {
|
||||
CString::new(s).expect("CString::new failed")
|
||||
}
|
||||
|
||||
/// Helper: assert a RegorusResult has Ok status and extract string output.
|
||||
fn assert_ok_string(r: &RegorusResult) -> String {
|
||||
assert_eq!(r.status, RegorusStatus::Ok, "expected Ok status");
|
||||
assert!(!r.output.is_null(), "expected non-null output");
|
||||
let s = unsafe { CStr::from_ptr(r.output) }
|
||||
.to_str()
|
||||
.expect("invalid UTF-8 in output")
|
||||
.to_string();
|
||||
s
|
||||
}
|
||||
|
||||
/// Helper: assert a RegorusResult has Ok status with integer output.
|
||||
fn assert_ok_int(r: &RegorusResult) -> i64 {
|
||||
assert_eq!(r.status, RegorusStatus::Ok, "expected Ok status");
|
||||
r.int_value
|
||||
}
|
||||
|
||||
const ALIASES: &str = r#"[{
|
||||
"namespace": "Microsoft.Storage",
|
||||
"resourceTypes": [{
|
||||
"resourceType": "storageAccounts",
|
||||
"aliases": [{
|
||||
"name": "Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly",
|
||||
"defaultPath": "properties.supportsHttpsTrafficOnly",
|
||||
"paths": []
|
||||
}]
|
||||
}]
|
||||
}]"#;
|
||||
|
||||
const MANIFEST: &str = r#"{
|
||||
"dataNamespace": "Microsoft.KeyVault.Data",
|
||||
"aliases": [],
|
||||
"resourceTypeAliases": [{
|
||||
"resourceType": "vaults/certificates",
|
||||
"aliases": [{
|
||||
"name": "Microsoft.KeyVault.Data/vaults/certificates/keySize",
|
||||
"paths": [{ "path": "keySize", "apiVersions": ["7.0"] }]
|
||||
}]
|
||||
}]
|
||||
}"#;
|
||||
|
||||
#[test]
|
||||
fn lifecycle_new_and_drop() {
|
||||
let reg = regorus_alias_registry_new();
|
||||
assert!(!reg.is_null());
|
||||
regorus_alias_registry_drop(reg);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_json_and_check_len() {
|
||||
let reg = regorus_alias_registry_new();
|
||||
let json = c(ALIASES);
|
||||
|
||||
let r = regorus_alias_registry_load_json(reg, json.as_ptr());
|
||||
assert_eq!(r.status, RegorusStatus::Ok);
|
||||
regorus_result_drop(r);
|
||||
|
||||
let r = regorus_alias_registry_len(reg);
|
||||
assert_eq!(assert_ok_int(&r), 1);
|
||||
regorus_result_drop(r);
|
||||
|
||||
regorus_alias_registry_drop(reg);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_manifest_and_check_len() {
|
||||
let reg = regorus_alias_registry_new();
|
||||
let json = c(MANIFEST);
|
||||
|
||||
let r = regorus_alias_registry_load_manifest(reg, json.as_ptr());
|
||||
assert_eq!(r.status, RegorusStatus::Ok);
|
||||
regorus_result_drop(r);
|
||||
|
||||
let r = regorus_alias_registry_len(reg);
|
||||
assert_eq!(assert_ok_int(&r), 1);
|
||||
regorus_result_drop(r);
|
||||
|
||||
regorus_alias_registry_drop(reg);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_invalid_json_returns_error() {
|
||||
let reg = regorus_alias_registry_new();
|
||||
let bad = c("not valid json");
|
||||
|
||||
let r = regorus_alias_registry_load_json(reg, bad.as_ptr());
|
||||
assert_ne!(r.status, RegorusStatus::Ok);
|
||||
regorus_result_drop(r);
|
||||
|
||||
regorus_alias_registry_drop(reg);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_and_wrap_round_trip() {
|
||||
let reg = regorus_alias_registry_new();
|
||||
let aliases = c(ALIASES);
|
||||
let r = regorus_alias_registry_load_json(reg, aliases.as_ptr());
|
||||
assert_eq!(r.status, RegorusStatus::Ok);
|
||||
regorus_result_drop(r);
|
||||
|
||||
let resource = c(r#"{
|
||||
"name": "acct1",
|
||||
"type": "Microsoft.Storage/storageAccounts",
|
||||
"properties": { "supportsHttpsTrafficOnly": true }
|
||||
}"#);
|
||||
let api = c("2023-01-01");
|
||||
let ctx = c(r#"{"resourceGroup": {"name": "rg1"}}"#);
|
||||
let params = c(r#"{"env": "prod"}"#);
|
||||
|
||||
// Normalize
|
||||
let r = regorus_alias_registry_normalize_and_wrap(
|
||||
reg,
|
||||
resource.as_ptr(),
|
||||
api.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
params.as_ptr(),
|
||||
);
|
||||
let envelope_json = assert_ok_string(&r);
|
||||
regorus_result_drop(r);
|
||||
|
||||
// Parse and verify structure
|
||||
let envelope: serde_json::Value =
|
||||
serde_json::from_str(&envelope_json).expect("invalid JSON output");
|
||||
assert!(
|
||||
envelope.get("resource").is_some(),
|
||||
"envelope missing 'resource'"
|
||||
);
|
||||
assert!(
|
||||
envelope.get("parameters").is_some(),
|
||||
"envelope missing 'parameters'"
|
||||
);
|
||||
assert!(
|
||||
envelope.get("context").is_some(),
|
||||
"envelope missing 'context'"
|
||||
);
|
||||
|
||||
// The normalized resource should have lowercased alias fields
|
||||
let res = &envelope["resource"];
|
||||
assert_eq!(res["supportshttpstrafficonly"], true);
|
||||
assert_eq!(res["name"], "acct1");
|
||||
|
||||
// Context and parameters should be passed through
|
||||
assert_eq!(envelope["context"]["resourceGroup"]["name"], "rg1");
|
||||
assert_eq!(envelope["parameters"]["env"], "prod");
|
||||
|
||||
// Denormalize the resource portion
|
||||
let resource_json = serde_json::to_string(&res).expect("serialize resource");
|
||||
let norm_cstr = c(&resource_json);
|
||||
|
||||
let r = regorus_alias_registry_denormalize(reg, norm_cstr.as_ptr(), api.as_ptr());
|
||||
let denorm_json = assert_ok_string(&r);
|
||||
regorus_result_drop(r);
|
||||
|
||||
let denorm: serde_json::Value =
|
||||
serde_json::from_str(&denorm_json).expect("invalid denorm JSON");
|
||||
// Should be back under properties with restored casing
|
||||
assert_eq!(
|
||||
denorm["properties"]["supportsHttpsTrafficOnly"], true,
|
||||
"expected restored casing under properties"
|
||||
);
|
||||
|
||||
regorus_alias_registry_drop(reg);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn denormalize_invalid_json_returns_error() {
|
||||
let reg = regorus_alias_registry_new();
|
||||
let aliases = c(ALIASES);
|
||||
let r = regorus_alias_registry_load_json(reg, aliases.as_ptr());
|
||||
assert_eq!(r.status, RegorusStatus::Ok);
|
||||
regorus_result_drop(r);
|
||||
|
||||
let bad = c("not json");
|
||||
let api = c("2023-01-01");
|
||||
let r = regorus_alias_registry_denormalize(reg, bad.as_ptr(), api.as_ptr());
|
||||
assert_ne!(r.status, RegorusStatus::Ok);
|
||||
regorus_result_drop(r);
|
||||
|
||||
regorus_alias_registry_drop(reg);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_data_plane_manifest() {
|
||||
let reg = regorus_alias_registry_new();
|
||||
let manifest = c(MANIFEST);
|
||||
let r = regorus_alias_registry_load_manifest(reg, manifest.as_ptr());
|
||||
assert_eq!(r.status, RegorusStatus::Ok);
|
||||
regorus_result_drop(r);
|
||||
|
||||
let resource = c(r#"{
|
||||
"type": "Microsoft.KeyVault.Data/vaults/certificates",
|
||||
"keySize": 2048
|
||||
}"#);
|
||||
let api = c("7.0");
|
||||
let ctx = c("{}");
|
||||
let params = c("{}");
|
||||
|
||||
let r = regorus_alias_registry_normalize_and_wrap(
|
||||
reg,
|
||||
resource.as_ptr(),
|
||||
api.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
params.as_ptr(),
|
||||
);
|
||||
let envelope_json = assert_ok_string(&r);
|
||||
regorus_result_drop(r);
|
||||
|
||||
let envelope: serde_json::Value =
|
||||
serde_json::from_str(&envelope_json).expect("invalid JSON output");
|
||||
assert_eq!(envelope["resource"]["keysize"], 2048);
|
||||
|
||||
regorus_alias_registry_drop(reg);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_registry_normalize() {
|
||||
let reg = regorus_alias_registry_new();
|
||||
let resource = c(r#"{"name": "test", "type": "Unknown/type", "properties": {"foo": 1}}"#);
|
||||
let api = c("");
|
||||
let ctx = c("{}");
|
||||
let params = c("{}");
|
||||
|
||||
let r = regorus_alias_registry_normalize_and_wrap(
|
||||
reg,
|
||||
resource.as_ptr(),
|
||||
api.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
params.as_ptr(),
|
||||
);
|
||||
let json = assert_ok_string(&r);
|
||||
regorus_result_drop(r);
|
||||
|
||||
let envelope: serde_json::Value = serde_json::from_str(&json).expect("invalid JSON");
|
||||
// Without aliases, properties should still be flattened
|
||||
assert_eq!(envelope["resource"]["foo"], 1);
|
||||
assert_eq!(envelope["resource"]["name"], "test");
|
||||
|
||||
regorus_alias_registry_drop(reg);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ use core::ffi::{c_char, c_longlong, c_void, CStr};
|
||||
use core::{mem, ptr};
|
||||
|
||||
/// Status of a call on `RegorusEngine`.
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub enum RegorusStatus {
|
||||
/// The operation was successful.
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::common::{
|
||||
};
|
||||
use crate::compiled_policy::RegorusCompiledPolicy;
|
||||
use crate::limits::RegorusExecutionTimerConfig;
|
||||
use crate::limits::RegorusPolicyLengthConfig;
|
||||
use crate::lock::{new_handle, read, try_read, try_write, Handle, ReadGuard, WriteGuard};
|
||||
use crate::panic_guard::with_unwind_guard;
|
||||
use alloc::boxed::Box;
|
||||
@@ -491,6 +492,37 @@ pub extern "C" fn regorus_engine_clear_execution_timer_config(
|
||||
}())
|
||||
}
|
||||
|
||||
/// Set the policy length limits used when loading policies.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_set_policy_length_config(
|
||||
engine: *mut RegorusEngine,
|
||||
config: RegorusPolicyLengthConfig,
|
||||
) -> RegorusResult {
|
||||
with_unwind_guard(|| {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.set_policy_length_config(config.to_policy_length_config()?);
|
||||
Ok(())
|
||||
}())
|
||||
})
|
||||
}
|
||||
|
||||
/// Clear the policy length configuration, reverting to defaults.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_clear_policy_length_config(
|
||||
engine: *mut RegorusEngine,
|
||||
) -> RegorusResult {
|
||||
with_unwind_guard(|| {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.clear_policy_length_config();
|
||||
Ok(())
|
||||
}())
|
||||
})
|
||||
}
|
||||
|
||||
/// Get pretty printed coverage report.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Report.html#method.to_string_pretty
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod alias_registry;
|
||||
mod allocator;
|
||||
mod common;
|
||||
mod compile;
|
||||
@@ -14,6 +15,8 @@ mod engine;
|
||||
mod limits;
|
||||
mod lock;
|
||||
mod panic_guard;
|
||||
#[cfg(feature = "rbac")]
|
||||
mod rbac;
|
||||
#[cfg(feature = "rvm")]
|
||||
pub(crate) mod rvm;
|
||||
mod schema_registry;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use crate::common::{to_regorus_result, RegorusResult, RegorusStatus};
|
||||
use alloc::format;
|
||||
use anyhow::{anyhow, Result};
|
||||
use core::num::NonZeroU32;
|
||||
use core::num::{NonZeroU32, NonZeroUsize};
|
||||
use core::time::Duration;
|
||||
use regorus::utils::limits::{self, ExecutionTimerConfig};
|
||||
|
||||
@@ -158,6 +158,31 @@ impl RegorusExecutionTimerConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// FFI representation of [`regorus::PolicyLengthConfig`].
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RegorusPolicyLengthConfig {
|
||||
/// Maximum column width per line (must be non-zero).
|
||||
pub max_col: u32,
|
||||
/// Maximum policy file size in bytes (must be non-zero).
|
||||
pub max_file_bytes: usize,
|
||||
/// Maximum number of lines per policy file (must be non-zero).
|
||||
pub max_lines: usize,
|
||||
}
|
||||
|
||||
impl RegorusPolicyLengthConfig {
|
||||
pub fn to_policy_length_config(self) -> Result<regorus::PolicyLengthConfig> {
|
||||
Ok(regorus::PolicyLengthConfig {
|
||||
max_col: NonZeroU32::new(self.max_col)
|
||||
.ok_or_else(|| anyhow!("max_col must be non-zero"))?,
|
||||
max_file_bytes: NonZeroUsize::new(self.max_file_bytes)
|
||||
.ok_or_else(|| anyhow!("max_file_bytes must be non-zero"))?,
|
||||
max_lines: NonZeroUsize::new(self.max_lines)
|
||||
.ok_or_else(|| anyhow!("max_lines must be non-zero"))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_set_fallback_execution_timer_config(
|
||||
config: RegorusExecutionTimerConfig,
|
||||
@@ -174,6 +199,40 @@ pub extern "C" fn regorus_clear_fallback_execution_timer_config() -> RegorusResu
|
||||
RegorusResult::ok_void()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Cache configuration (global)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// FFI representation of [`regorus::cache::Config`].
|
||||
#[cfg(feature = "cache")]
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RegorusCacheConfig {
|
||||
/// Maximum compiled regex patterns (default 256, 0 = disabled).
|
||||
pub regex: usize,
|
||||
/// Maximum compiled glob matchers (default 128, 0 = disabled).
|
||||
pub glob: usize,
|
||||
}
|
||||
|
||||
/// Configure the global pattern caches used by `regex.*` and `glob.*` builtins.
|
||||
#[cfg(feature = "cache")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_set_cache_config(config: RegorusCacheConfig) -> RegorusResult {
|
||||
regorus::cache::configure(regorus::cache::Config {
|
||||
regex: config.regex,
|
||||
glob: config.glob,
|
||||
});
|
||||
RegorusResult::ok_void()
|
||||
}
|
||||
|
||||
/// Clear all entries from every pattern cache.
|
||||
#[cfg(feature = "cache")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_clear_cache() -> RegorusResult {
|
||||
regorus::cache::clear();
|
||||
RegorusResult::ok_void()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
|
||||
59
bindings/ffi/src/rbac.rs
Normal file
59
bindings/ffi/src/rbac.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use crate::common::{from_c_str, RegorusResult, RegorusStatus};
|
||||
use crate::panic_guard::with_unwind_guard;
|
||||
use alloc::format;
|
||||
use core::ffi::c_char;
|
||||
|
||||
use regorus::languages::azure_rbac::ast::EvaluationContext;
|
||||
use regorus::languages::azure_rbac::interpreter::ConditionInterpreter;
|
||||
|
||||
#[no_mangle]
|
||||
/// Evaluate an Azure RBAC condition expression against a JSON evaluation context.
|
||||
///
|
||||
/// * `condition`: RBAC condition string.
|
||||
/// * `context_json`: JSON representation of EvaluationContext.
|
||||
pub extern "C" fn regorus_rbac_engine_eval_condition(
|
||||
condition: *const c_char,
|
||||
context_json: *const c_char,
|
||||
) -> RegorusResult {
|
||||
with_unwind_guard(|| {
|
||||
let condition = match from_c_str(condition) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidArgument,
|
||||
format!("{err}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
let context_json = match from_c_str(context_json) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidArgument,
|
||||
format!("{err}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
let context: EvaluationContext = match serde_json::from_str(&context_json) {
|
||||
Ok(context) => context,
|
||||
Err(err) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidDataFormat,
|
||||
format!("invalid context json: {err}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let interpreter = ConditionInterpreter::new(&context);
|
||||
match interpreter.evaluate_str(&condition) {
|
||||
Ok(result) => RegorusResult::ok_bool(result),
|
||||
Err(err) => RegorusResult::err_with_message(
|
||||
RegorusStatus::Error,
|
||||
format!("condition evaluation failed: {err}"),
|
||||
),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -17,7 +17,15 @@ func main() {
|
||||
engine := regorus.NewEngine()
|
||||
defer engine.Close()
|
||||
|
||||
// Configure the global pattern caches.
|
||||
if err = regorus.SetCacheConfig(regorus.CacheConfig{Regex: 256, Glob: 128}); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
engine.SetRegoV0(true)
|
||||
// Raise the default col limit to 2000
|
||||
engine.SetPolicyLengthConfig(regorus.PolicyLengthConfig{MaxCol: 2000, MaxFileBytes: 1048576, MaxLines: 20000})
|
||||
elapsed1 := time.Since(t)
|
||||
|
||||
|
||||
|
||||
@@ -214,3 +214,59 @@ func (e *Engine) TakePrints() (string, error) {
|
||||
|
||||
return C.GoString(result.output), nil
|
||||
}
|
||||
|
||||
type PolicyLengthConfig struct {
|
||||
MaxCol uint32
|
||||
MaxFileBytes uint
|
||||
MaxLines uint
|
||||
}
|
||||
|
||||
func (e *Engine) SetPolicyLengthConfig(config PolicyLengthConfig) error {
|
||||
c := C.RegorusPolicyLengthConfig{
|
||||
max_col: C.uint32_t(config.MaxCol),
|
||||
max_file_bytes: C.size_t(config.MaxFileBytes),
|
||||
max_lines: C.size_t(config.MaxLines),
|
||||
}
|
||||
result := C.regorus_engine_set_policy_length_config(e.e, c)
|
||||
defer C.regorus_result_drop(result)
|
||||
if result.status != C.Ok {
|
||||
return fmt.Errorf("%s", C.GoString(result.error_message))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Engine) ClearPolicyLengthConfig() error {
|
||||
result := C.regorus_engine_clear_policy_length_config(e.e)
|
||||
defer C.regorus_result_drop(result)
|
||||
if result.status != C.Ok {
|
||||
return fmt.Errorf("%s", C.GoString(result.error_message))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CacheConfig struct {
|
||||
Regex uint
|
||||
Glob uint
|
||||
}
|
||||
|
||||
func SetCacheConfig(config CacheConfig) error {
|
||||
c := C.RegorusCacheConfig{
|
||||
regex: C.size_t(config.Regex),
|
||||
glob: C.size_t(config.Glob),
|
||||
}
|
||||
result := C.regorus_set_cache_config(c)
|
||||
defer C.regorus_result_drop(result)
|
||||
if result.status != C.Ok {
|
||||
return fmt.Errorf("%s", C.GoString(result.error_message))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ClearCache() error {
|
||||
result := C.regorus_clear_cache()
|
||||
defer C.regorus_result_drop(result)
|
||||
if result.status != C.Ok {
|
||||
return fmt.Errorf("%s", C.GoString(result.error_message))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
648
bindings/java/Cargo.lock
generated
648
bindings/java/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -2,10 +2,11 @@
|
||||
|
||||
[package]
|
||||
name = "regorus-java"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/microsoft/regorus/bindings/java"
|
||||
description = "Java bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
|
||||
license = "MIT AND Apache-2.0 AND BSD-3-Clause"
|
||||
keywords = ["interpreter", "opa", "policy-as-code", "rego"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -13,12 +14,13 @@ keywords = ["interpreter", "opa", "policy-as-code", "rego"]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[features]
|
||||
default = ["ast", "coverage", "regorus/std", "regorus/full-opa"]
|
||||
default = ["ast", "cache", "coverage", "regorus/std", "regorus/full-opa", "regorus/allocator-memory-limits"]
|
||||
coverage = ["regorus/coverage"]
|
||||
ast = ["regorus/ast"]
|
||||
cache = ["regorus/cache"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
serde_json = "1.0.112"
|
||||
jni = "0.21.1"
|
||||
jni = "0.22.4"
|
||||
regorus = { path = "../..", default-features = false, features = ["arc", "rvm"] }
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import com.microsoft.regorus.CacheConfig;
|
||||
import com.microsoft.regorus.Engine;
|
||||
import com.microsoft.regorus.PolicyLengthConfig;
|
||||
import com.microsoft.regorus.PolicyModule;
|
||||
import com.microsoft.regorus.Program;
|
||||
import com.microsoft.regorus.Rvm;
|
||||
@@ -9,6 +11,9 @@ import com.microsoft.regorus.Rvm;
|
||||
public class Test {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Configure the global pattern caches.
|
||||
CacheConfig.configure(new CacheConfig(256, 128));
|
||||
|
||||
try (Engine engine = new Engine()) {
|
||||
String pkg = engine.addPolicy(
|
||||
"hello.rego",
|
||||
@@ -26,6 +31,9 @@ public class Test {
|
||||
// Enable coverage.
|
||||
engine.setEnableCoverage(true);
|
||||
|
||||
// Raise the default col limit to 2000
|
||||
engine.setPolicyLengthConfig(new PolicyLengthConfig(2000, 1048576, 20000));
|
||||
|
||||
// Evaluate rule.
|
||||
String valueJson = engine.evalRule("data.test.message");
|
||||
System.out.println(valueJson);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>com.microsoft.regorus</groupId>
|
||||
<artifactId>regorus-java</artifactId>
|
||||
<version>0.9.0</version>
|
||||
<version>0.9.1</version>
|
||||
|
||||
<name>Regorus Java</name>
|
||||
<description>Java bindings for Regorus - a fast, lightweight Rego interpreter written in Rust</description>
|
||||
@@ -18,7 +18,15 @@
|
||||
<licenses>
|
||||
<license>
|
||||
<name>MIT License</name>
|
||||
<url>https://opensource.org/blog/license/mit</url>
|
||||
<url>https://opensource.org/licenses/MIT</url>
|
||||
</license>
|
||||
<license>
|
||||
<name>Apache License 2.0</name>
|
||||
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
|
||||
</license>
|
||||
<license>
|
||||
<name>BSD 3-Clause License</name>
|
||||
<url>https://opensource.org/licenses/BSD-3-Clause</url>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
@@ -40,13 +48,13 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<version>4.13.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10.1</version>
|
||||
<version>2.13.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
@@ -68,7 +76,7 @@
|
||||
<plugin>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.6.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<!-- Build a debug release for tests -->
|
||||
@@ -89,7 +97,7 @@
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.2.5</version>
|
||||
<version>3.5.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>
|
||||
@@ -100,7 +108,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<version>3.12.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadoc</id>
|
||||
@@ -115,7 +123,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.4.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use anyhow::Result;
|
||||
use core::num::{NonZeroU32, NonZeroUsize};
|
||||
use jni::objects::{JBooleanArray, JByteArray, JClass, JObject, JObjectArray, JString};
|
||||
use jni::strings::JNIString;
|
||||
use jni::sys::{jboolean, jbooleanArray, jbyteArray, jlong, jobjectArray, jstring};
|
||||
use jni::JNIEnv;
|
||||
use jni::{jni_str, Env, EnvUnowned, Outcome};
|
||||
|
||||
use regorus::languages::rego::compiler::Compiler;
|
||||
use regorus::rvm::program::{
|
||||
@@ -16,7 +18,7 @@ use std::sync::Arc;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeNewEngine(
|
||||
_env: JNIEnv,
|
||||
_env: EnvUnowned,
|
||||
_class: JClass,
|
||||
) -> jlong {
|
||||
let engine = Engine::new();
|
||||
@@ -25,7 +27,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeNewEngine(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeClone(
|
||||
_env: JNIEnv,
|
||||
_env: EnvUnowned,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
) -> jlong {
|
||||
@@ -36,7 +38,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeClone(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetRegoV0(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
enable: bool,
|
||||
@@ -50,7 +52,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetRegoV0(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddPolicy(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
path: JString,
|
||||
@@ -58,9 +60,9 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddPolicy(
|
||||
) -> 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(®o)?.into();
|
||||
let pkg = env.new_string(engine.add_policy(path, rego)?)?;
|
||||
let path: String = path.try_to_string(env)?;
|
||||
let rego: String = rego.try_to_string(env)?;
|
||||
let pkg = JString::new(env, engine.add_policy(path, rego)?)?;
|
||||
Ok(pkg.into_raw())
|
||||
});
|
||||
|
||||
@@ -72,15 +74,15 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddPolicy(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddPolicyFromFile(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_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)?)?;
|
||||
let path: String = path.try_to_string(env)?;
|
||||
let pkg = JString::new(env, engine.add_policy_from_file(path)?)?;
|
||||
Ok(pkg.into_raw())
|
||||
});
|
||||
|
||||
@@ -92,14 +94,14 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddPolicyFromFile
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeGetPackages(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_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)?)?;
|
||||
let packages_json = JString::new(env, serde_json::to_string_pretty(&packages)?)?;
|
||||
Ok(packages_json.into_raw())
|
||||
});
|
||||
|
||||
@@ -111,14 +113,14 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeGetPackages(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeGetPolicies(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
) -> jstring {
|
||||
let res = throw_err(env, |env| {
|
||||
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
|
||||
let policies = engine.get_policies_as_json()?;
|
||||
let policies_json = env.new_string(&policies)?;
|
||||
let policies_json = JString::new(env, &policies)?;
|
||||
Ok(policies_json.into_raw())
|
||||
});
|
||||
|
||||
@@ -130,7 +132,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeGetPolicies(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeClearData(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
) {
|
||||
@@ -143,14 +145,14 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeClearData(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddDataJson(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_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();
|
||||
let data: String = data.try_to_string(env)?;
|
||||
engine.add_data_json(&data)?;
|
||||
Ok(())
|
||||
});
|
||||
@@ -158,14 +160,14 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddDataJson(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddDataJsonFromFile(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_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();
|
||||
let path: String = path.try_to_string(env)?;
|
||||
engine.add_data(Value::from_json_file(path)?)?;
|
||||
Ok(())
|
||||
});
|
||||
@@ -173,14 +175,14 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddDataJsonFromFi
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetInputJson(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_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();
|
||||
let input: String = input.try_to_string(env)?;
|
||||
engine.set_input_json(&input)?;
|
||||
Ok(())
|
||||
});
|
||||
@@ -188,14 +190,14 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetInputJson(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetInputJsonFromFile(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_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();
|
||||
let path: String = path.try_to_string(env)?;
|
||||
engine.set_input(Value::from_json_file(path)?);
|
||||
Ok(())
|
||||
});
|
||||
@@ -203,16 +205,16 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetInputJsonFromF
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeEvalQuery(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_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 query: String = query.try_to_string(env)?;
|
||||
let results = engine.eval_query(query, false)?;
|
||||
let output = env.new_string(serde_json::to_string(&results)?)?;
|
||||
let output = JString::new(env, serde_json::to_string(&results)?)?;
|
||||
Ok(output.into_raw())
|
||||
});
|
||||
|
||||
@@ -224,16 +226,16 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeEvalQuery(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeEvalRule(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_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 rule: String = rule.try_to_string(env)?;
|
||||
let value = engine.eval_rule(rule)?;
|
||||
let output = env.new_string(value.to_json_str()?)?;
|
||||
let output = JString::new(env, value.to_json_str()?)?;
|
||||
Ok(output.into_raw())
|
||||
});
|
||||
|
||||
@@ -246,7 +248,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeEvalRule(
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "coverage")]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetEnableCoverage(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
enable: bool,
|
||||
@@ -261,14 +263,14 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetEnableCoverage
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "coverage")]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeGetCoverageReport(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_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)?)?;
|
||||
let output = JString::new(env, serde_json::to_string_pretty(&report)?)?;
|
||||
Ok(output.into_raw())
|
||||
});
|
||||
|
||||
@@ -281,14 +283,14 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeGetCoverageReport
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "coverage")]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeGetCoverageReportPretty(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_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)?;
|
||||
let output = JString::new(env, &report)?;
|
||||
Ok(output.into_raw())
|
||||
});
|
||||
|
||||
@@ -301,7 +303,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeGetCoverageReport
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "coverage")]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeClearCoverageData(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
) {
|
||||
@@ -314,7 +316,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeClearCoverageData
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetGatherPrints(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
b: bool,
|
||||
@@ -328,14 +330,14 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetGatherPrints(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeTakePrints(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_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)?)?;
|
||||
let output = JString::new(env, serde_json::to_string_pretty(&prints)?)?;
|
||||
Ok(output.into_raw())
|
||||
});
|
||||
|
||||
@@ -348,14 +350,14 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeTakePrints(
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "ast")]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_getAstAsJson(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
) -> jstring {
|
||||
let res = throw_err(env, |env| {
|
||||
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
|
||||
let ast = engine.get_ast_as_json()?;
|
||||
let output = env.new_string(&ast)?;
|
||||
let output = JString::new(env, &ast)?;
|
||||
Ok(output.into_raw())
|
||||
});
|
||||
|
||||
@@ -365,9 +367,73 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_getAstAsJson(
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetPolicyLengthConfig(
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
max_col: u32,
|
||||
max_file_bytes: jlong,
|
||||
max_lines: jlong,
|
||||
) {
|
||||
let _ = throw_err(env, |_env| {
|
||||
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
|
||||
engine.set_policy_length_config(regorus::PolicyLengthConfig {
|
||||
max_col: NonZeroU32::new(max_col)
|
||||
.ok_or_else(|| anyhow::anyhow!("maxCol must be non-zero"))?,
|
||||
max_file_bytes: NonZeroUsize::new(max_file_bytes as usize)
|
||||
.ok_or_else(|| anyhow::anyhow!("maxFileBytes must be non-zero"))?,
|
||||
max_lines: NonZeroUsize::new(max_lines as usize)
|
||||
.ok_or_else(|| anyhow::anyhow!("maxLines must be non-zero"))?,
|
||||
});
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeClearPolicyLengthConfig(
|
||||
_env: EnvUnowned,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
) {
|
||||
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
|
||||
engine.clear_policy_length_config();
|
||||
}
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_CacheConfig_nativeSetCacheConfig(
|
||||
_env: EnvUnowned,
|
||||
_class: JClass,
|
||||
regex: jlong,
|
||||
glob: jlong,
|
||||
) {
|
||||
regorus::cache::configure(regorus::cache::Config {
|
||||
regex: if regex < 0 {
|
||||
0
|
||||
} else {
|
||||
usize::try_from(regex).unwrap_or(usize::MAX)
|
||||
},
|
||||
glob: if glob < 0 {
|
||||
0
|
||||
} else {
|
||||
usize::try_from(glob).unwrap_or(usize::MAX)
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_CacheConfig_nativeClearCache(
|
||||
_env: EnvUnowned,
|
||||
_class: JClass,
|
||||
) {
|
||||
regorus::cache::clear();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeDestroyEngine(
|
||||
_env: JNIEnv,
|
||||
_env: EnvUnowned,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
) {
|
||||
@@ -378,7 +444,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeDestroyEngine(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Program_nativeCompileFromModules(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
data_json: JString,
|
||||
module_ids: jobjectArray,
|
||||
@@ -386,7 +452,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Program_nativeCompileFromModul
|
||||
entry_points: jobjectArray,
|
||||
) -> jlong {
|
||||
let res = throw_err(env, |env| {
|
||||
let data_json: String = env.get_string(&data_json)?.into();
|
||||
let data_json: String = data_json.try_to_string(env)?;
|
||||
let data = Value::from_json_str(&data_json)?;
|
||||
|
||||
let ids = get_string_array(env, module_ids)?;
|
||||
@@ -422,7 +488,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Program_nativeCompileFromModul
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Program_nativeCompileFromEngine(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
entry_points: jobjectArray,
|
||||
@@ -447,7 +513,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Program_nativeCompileFromEngin
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Program_nativeGenerateListing(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
program_ptr: jlong,
|
||||
) -> jstring {
|
||||
@@ -455,7 +521,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Program_nativeGenerateListing(
|
||||
let program = unsafe { &*(program_ptr as *mut Arc<RvmProgram>) };
|
||||
let listing =
|
||||
generate_assembly_listing(program.as_ref(), &AssemblyListingConfig::default());
|
||||
let output = env.new_string(&listing)?;
|
||||
let output = JString::new(env, &listing)?;
|
||||
Ok(output.into_raw())
|
||||
});
|
||||
|
||||
@@ -467,7 +533,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Program_nativeGenerateListing(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Program_nativeSerializeBinary(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
program_ptr: jlong,
|
||||
) -> jbyteArray {
|
||||
@@ -491,7 +557,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Program_nativeSerializeBinary(
|
||||
/// for the duration of the call. They must come from the JVM for the current
|
||||
/// thread and not be used after this function returns.
|
||||
pub unsafe extern "system" fn Java_com_microsoft_regorus_Program_nativeDeserializeBinary(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
data: jbyteArray,
|
||||
is_partial: jbooleanArray,
|
||||
@@ -501,7 +567,7 @@ pub unsafe extern "system" fn Java_com_microsoft_regorus_Program_nativeDeseriali
|
||||
return Err(anyhow::anyhow!("data must not be null"));
|
||||
}
|
||||
|
||||
let data = unsafe { JByteArray::from_raw(data) };
|
||||
let data = unsafe { JByteArray::from_raw(env, data) };
|
||||
let bytes = env.convert_byte_array(&data)?;
|
||||
let (program, partial) =
|
||||
match RvmProgram::deserialize_binary(&bytes).map_err(|e| anyhow::anyhow!(e))? {
|
||||
@@ -510,11 +576,15 @@ pub unsafe extern "system" fn Java_com_microsoft_regorus_Program_nativeDeseriali
|
||||
};
|
||||
|
||||
if !is_partial.is_null() {
|
||||
let is_partial = unsafe { JBooleanArray::from_raw(is_partial) };
|
||||
let len = env.get_array_length(&is_partial)?;
|
||||
let is_partial = unsafe { JBooleanArray::from_raw(env, is_partial) };
|
||||
let len = is_partial.len(env)?;
|
||||
if len > 0 {
|
||||
let value: [jboolean; 1] = [if partial { 1 } else { 0 }];
|
||||
env.set_boolean_array_region(&is_partial, 0, &value)?;
|
||||
let value: [jboolean; 1] = [if partial {
|
||||
jni::sys::JNI_TRUE
|
||||
} else {
|
||||
jni::sys::JNI_FALSE
|
||||
}];
|
||||
is_partial.set_region(env, 0, &value)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,7 +596,7 @@ pub unsafe extern "system" fn Java_com_microsoft_regorus_Program_nativeDeseriali
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Program_nativeDrop(
|
||||
_env: JNIEnv,
|
||||
_env: EnvUnowned,
|
||||
_class: JClass,
|
||||
program_ptr: jlong,
|
||||
) {
|
||||
@@ -537,7 +607,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Program_nativeDrop(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeNew(
|
||||
_env: JNIEnv,
|
||||
_env: EnvUnowned,
|
||||
_class: JClass,
|
||||
) -> jlong {
|
||||
let vm = RegoVM::new();
|
||||
@@ -546,7 +616,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeNew(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeLoadProgram(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
vm_ptr: jlong,
|
||||
program_ptr: jlong,
|
||||
@@ -561,14 +631,14 @@ pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeLoadProgram(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeSetDataJson(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
vm_ptr: jlong,
|
||||
data_json: JString,
|
||||
) {
|
||||
let _ = throw_err(env, |env| {
|
||||
let vm = unsafe { &mut *(vm_ptr as *mut RegoVM) };
|
||||
let data_json: String = env.get_string(&data_json)?.into();
|
||||
let data_json: String = data_json.try_to_string(env)?;
|
||||
let data = Value::from_json_str(&data_json)?;
|
||||
vm.set_data(data)?;
|
||||
Ok(())
|
||||
@@ -577,14 +647,14 @@ pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeSetDataJson(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeSetInputJson(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
vm_ptr: jlong,
|
||||
input_json: JString,
|
||||
) {
|
||||
let _ = throw_err(env, |env| {
|
||||
let vm = unsafe { &mut *(vm_ptr as *mut RegoVM) };
|
||||
let input_json: String = env.get_string(&input_json)?.into();
|
||||
let input_json: String = input_json.try_to_string(env)?;
|
||||
let input = Value::from_json_str(&input_json)?;
|
||||
vm.set_input(input);
|
||||
Ok(())
|
||||
@@ -593,7 +663,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeSetInputJson(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeSetExecutionMode(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
vm_ptr: jlong,
|
||||
mode: u8,
|
||||
@@ -612,14 +682,14 @@ pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeSetExecutionMode(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeExecute(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
vm_ptr: jlong,
|
||||
) -> jstring {
|
||||
let res = throw_err(env, |env| {
|
||||
let vm = unsafe { &mut *(vm_ptr as *mut RegoVM) };
|
||||
let result = vm.execute()?;
|
||||
let output = env.new_string(result.to_json_str()?)?;
|
||||
let output = JString::new(env, result.to_json_str()?)?;
|
||||
Ok(output.into_raw())
|
||||
});
|
||||
|
||||
@@ -631,16 +701,16 @@ pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeExecute(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeExecuteEntryPoint(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
vm_ptr: jlong,
|
||||
entry_point: JString,
|
||||
) -> jstring {
|
||||
let res = throw_err(env, |env| {
|
||||
let vm = unsafe { &mut *(vm_ptr as *mut RegoVM) };
|
||||
let entry_point: String = env.get_string(&entry_point)?.into();
|
||||
let entry_point: String = entry_point.try_to_string(env)?;
|
||||
let result = vm.execute_entry_point_by_name(&entry_point)?;
|
||||
let output = env.new_string(result.to_json_str()?)?;
|
||||
let output = JString::new(env, result.to_json_str()?)?;
|
||||
Ok(output.into_raw())
|
||||
});
|
||||
|
||||
@@ -652,7 +722,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeExecuteEntryPoint(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeResume(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
vm_ptr: jlong,
|
||||
resume_json: JString,
|
||||
@@ -661,13 +731,13 @@ pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeResume(
|
||||
let res = throw_err(env, |env| {
|
||||
let vm = unsafe { &mut *(vm_ptr as *mut RegoVM) };
|
||||
let value = if has_value {
|
||||
let resume_json: String = env.get_string(&resume_json)?.into();
|
||||
let resume_json: String = resume_json.try_to_string(env)?;
|
||||
Some(Value::from_json_str(&resume_json)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let result = vm.resume(value)?;
|
||||
let output = env.new_string(result.to_json_str()?)?;
|
||||
let output = JString::new(env, result.to_json_str()?)?;
|
||||
Ok(output.into_raw())
|
||||
});
|
||||
|
||||
@@ -679,13 +749,13 @@ pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeResume(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeGetExecutionState(
|
||||
env: JNIEnv,
|
||||
env: EnvUnowned,
|
||||
_class: JClass,
|
||||
vm_ptr: jlong,
|
||||
) -> jstring {
|
||||
let res = throw_err(env, |env| {
|
||||
let vm = unsafe { &mut *(vm_ptr as *mut RegoVM) };
|
||||
let output = env.new_string(format!("{:?}", vm.execution_state()))?;
|
||||
let output = JString::new(env, format!("{:?}", vm.execution_state()))?;
|
||||
Ok(output.into_raw())
|
||||
});
|
||||
|
||||
@@ -697,7 +767,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeGetExecutionState(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeDrop(
|
||||
_env: JNIEnv,
|
||||
_env: EnvUnowned,
|
||||
_class: JClass,
|
||||
vm_ptr: jlong,
|
||||
) {
|
||||
@@ -706,27 +776,57 @@ pub extern "system" fn Java_com_microsoft_regorus_Rvm_nativeDrop(
|
||||
}
|
||||
}
|
||||
|
||||
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())?;
|
||||
fn throw_err<T>(mut env: EnvUnowned, f: impl FnOnce(&mut Env) -> Result<T>) -> Result<T> {
|
||||
let outcome = env.with_env(|env| -> Result<T> {
|
||||
match f(env) {
|
||||
Ok(val) => Ok(val),
|
||||
Err(err) => {
|
||||
if let Err(throw_err) = env.throw_new(
|
||||
jni_str!("java/lang/RuntimeException"),
|
||||
JNIString::new(err.to_string()),
|
||||
) {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Failed to throw Java RuntimeException for error '{err}': {throw_err}"
|
||||
));
|
||||
}
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
});
|
||||
match outcome.into_outcome() {
|
||||
Outcome::Ok(val) => Ok(val),
|
||||
Outcome::Err(err) => Err(err),
|
||||
Outcome::Panic(payload) => {
|
||||
let msg = payload
|
||||
.downcast_ref::<String>()
|
||||
.map(|s| s.as_str())
|
||||
.or_else(|| payload.downcast_ref::<&str>().copied())
|
||||
.unwrap_or("unknown panic");
|
||||
let err = anyhow::anyhow!("panic: {msg}");
|
||||
// Try to surface the panic as a Java exception.
|
||||
let _ = env.with_env(|env| -> Result<()> {
|
||||
env.throw_new(
|
||||
jni_str!("java/lang/RuntimeException"),
|
||||
JNIString::new(format!("Rust panic: {msg}")),
|
||||
)?;
|
||||
Ok(())
|
||||
});
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_string_array(env: &mut JNIEnv, array: jobjectArray) -> Result<Vec<String>> {
|
||||
fn get_string_array(env: &mut Env, array: jobjectArray) -> Result<Vec<String>> {
|
||||
if array.is_null() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
let array = unsafe { JObjectArray::from_raw(array) };
|
||||
let len = env.get_array_length(&array)?;
|
||||
let mut values = Vec::with_capacity(len as usize);
|
||||
let array = unsafe { JObjectArray::<JObject>::from_raw(env, array) };
|
||||
let len = array.len(env)?;
|
||||
let mut values = Vec::with_capacity(len);
|
||||
for i in 0..len {
|
||||
let obj = env.get_object_array_element(&array, i)?;
|
||||
let jstr = JString::from(obj);
|
||||
let value: String = env.get_string(&jstr)?.into();
|
||||
let obj = array.get_element(env, i)?;
|
||||
let jstr = unsafe { JString::from_raw(env, obj.into_raw()) };
|
||||
let value: String = jstr.try_to_string(env)?;
|
||||
values.push(value);
|
||||
}
|
||||
Ok(values)
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
* Licensed under the MIT License.
|
||||
**/
|
||||
|
||||
package com.microsoft.regorus;
|
||||
|
||||
/**
|
||||
* Global configuration for compiled pattern caches used by regex and glob builtins.
|
||||
*
|
||||
* <p>Capacity of 0 disables the corresponding cache.
|
||||
*/
|
||||
public final class CacheConfig {
|
||||
|
||||
static {
|
||||
System.loadLibrary("regorus_java");
|
||||
}
|
||||
|
||||
private static native void nativeSetCacheConfig(long regex, long glob);
|
||||
private static native void nativeClearCache();
|
||||
|
||||
/**
|
||||
* Maximum cached compiled regex patterns (default 256).
|
||||
*/
|
||||
public final long regex;
|
||||
|
||||
/**
|
||||
* Maximum cached compiled glob matchers (default 128).
|
||||
*/
|
||||
public final long glob;
|
||||
|
||||
/**
|
||||
* Create a new cache configuration.
|
||||
*
|
||||
* @param regex Maximum cached compiled regex patterns (0 = disabled).
|
||||
* @param glob Maximum cached compiled glob matchers (0 = disabled).
|
||||
*/
|
||||
public CacheConfig(long regex, long glob) {
|
||||
if (regex < 0) {
|
||||
throw new IllegalArgumentException("regex must be non-negative");
|
||||
}
|
||||
if (glob < 0) {
|
||||
throw new IllegalArgumentException("glob must be non-negative");
|
||||
}
|
||||
this.regex = regex;
|
||||
this.glob = glob;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this cache configuration globally.
|
||||
*/
|
||||
public static void configure(CacheConfig config) {
|
||||
nativeSetCacheConfig(config.regex, config.glob);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all entries from every pattern cache.
|
||||
*/
|
||||
public static void clear() {
|
||||
nativeClearCache();
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,8 @@ public class Engine implements AutoCloseable, Cloneable {
|
||||
private static native void nativeClearCoverageData(long enginePtr);
|
||||
private static native void nativeSetGatherPrints(long enginePtr, boolean b);
|
||||
private static native String nativeTakePrints(long enginePtr);
|
||||
private static native void nativeSetPolicyLengthConfig(long enginePtr, int maxCol, long maxFileBytes, long maxLines);
|
||||
private static native void nativeClearPolicyLengthConfig(long enginePtr);
|
||||
private static native void nativeDestroyEngine(long enginePtr);
|
||||
|
||||
// Pointer to Engine allocated on Rust's heap, all native methods works on
|
||||
@@ -259,6 +261,22 @@ public class Engine implements AutoCloseable, Cloneable {
|
||||
return nativeTakePrints(enginePtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the policy length limits used when loading policies.
|
||||
*
|
||||
* @param config Policy length configuration.
|
||||
*/
|
||||
public void setPolicyLengthConfig(PolicyLengthConfig config) {
|
||||
nativeSetPolicyLengthConfig(enginePtr, config.maxCol, config.maxFileBytes, config.maxLines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the policy length configuration, reverting to defaults.
|
||||
*/
|
||||
public void clearPolicyLengthConfig() {
|
||||
nativeClearPolicyLengthConfig(enginePtr);
|
||||
}
|
||||
|
||||
long getPtr() {
|
||||
return enginePtr;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
* Licensed under the MIT License.
|
||||
**/
|
||||
|
||||
package com.microsoft.regorus;
|
||||
|
||||
/**
|
||||
* Policy source length limits enforced when loading policy files.
|
||||
*
|
||||
* All values must be positive (non-zero).
|
||||
*/
|
||||
public final class PolicyLengthConfig {
|
||||
/**
|
||||
* Maximum column width per line (default: 1024).
|
||||
*/
|
||||
public final int maxCol;
|
||||
|
||||
/**
|
||||
* Maximum policy file size in bytes (default: 1 MiB).
|
||||
*/
|
||||
public final long maxFileBytes;
|
||||
|
||||
/**
|
||||
* Maximum number of lines per policy file (default: 20000).
|
||||
*/
|
||||
public final long maxLines;
|
||||
|
||||
/**
|
||||
* Create a new policy length configuration.
|
||||
*
|
||||
* @param maxCol Maximum column width per line.
|
||||
* @param maxFileBytes Maximum policy file size in bytes.
|
||||
* @param maxLines Maximum number of lines per policy file.
|
||||
*/
|
||||
public PolicyLengthConfig(int maxCol, long maxFileBytes, long maxLines) {
|
||||
if (maxCol <= 0) {
|
||||
throw new IllegalArgumentException("maxCol must be positive");
|
||||
}
|
||||
if (maxFileBytes <= 0) {
|
||||
throw new IllegalArgumentException("maxFileBytes must be positive");
|
||||
}
|
||||
if (maxLines <= 0) {
|
||||
throw new IllegalArgumentException("maxLines must be positive");
|
||||
}
|
||||
this.maxCol = maxCol;
|
||||
this.maxFileBytes = maxFileBytes;
|
||||
this.maxLines = maxLines;
|
||||
}
|
||||
}
|
||||
523
bindings/python/Cargo.lock
generated
523
bindings/python/Cargo.lock
generated
@@ -9,7 +9,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"getrandom 0.3.4",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"version_check",
|
||||
@@ -25,6 +25,12 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
@@ -36,9 +42,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.100"
|
||||
version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
@@ -46,22 +52,6 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"unty",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.8.0"
|
||||
@@ -79,9 +69,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
|
||||
[[package]]
|
||||
name = "borrow-or-share"
|
||||
@@ -101,9 +91,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.19.1"
|
||||
version = "3.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
||||
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
||||
|
||||
[[package]]
|
||||
name = "bytecount"
|
||||
@@ -113,9 +103,9 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.55"
|
||||
version = "1.2.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29"
|
||||
checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
@@ -128,10 +118,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.43"
|
||||
name = "chacha20"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
|
||||
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
@@ -150,12 +151,30 @@ dependencies = [
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cobs"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.10.0"
|
||||
@@ -182,6 +201,18 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
@@ -190,9 +221,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "fancy-regex"
|
||||
version = "0.14.0"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298"
|
||||
checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"regex-automata",
|
||||
@@ -207,15 +238,27 @@ checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "fluent-uri"
|
||||
version = "0.3.2"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1918b65d96df47d3591bed19c5cca17e3fa5d0707318e4b5ef2eae01764df7e5"
|
||||
checksum = "bc74ac4d8359ae70623506d512209619e5cf8f347124910440dbc221714b328e"
|
||||
dependencies = [
|
||||
"borrow-or-share",
|
||||
"ref-cast",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.2"
|
||||
@@ -242,9 +285,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"r-efi 5.3.0",
|
||||
"wasip2",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi 6.0.0",
|
||||
"rand_core",
|
||||
"wasip2",
|
||||
"wasip3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -259,11 +318,25 @@ dependencies = [
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash 0.1.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
@@ -376,6 +449,12 @@ dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.1.0"
|
||||
@@ -404,37 +483,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"hashbrown 0.16.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.11.0"
|
||||
version = "2.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
||||
checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.17"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.85"
|
||||
version = "0.3.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
|
||||
checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
@@ -442,27 +512,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jsonschema"
|
||||
version = "0.30.0"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1b46a0365a611fbf1d2143104dcf910aada96fafd295bab16c60b802bf6fa1d"
|
||||
checksum = "6f29616f6e19415398eb186964fb7cbbeef572c79bede3622a8277667924bbe3"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"base64",
|
||||
"bytecount",
|
||||
"data-encoding",
|
||||
"email_address",
|
||||
"fancy-regex",
|
||||
"fraction",
|
||||
"getrandom 0.3.4",
|
||||
"idna",
|
||||
"itoa",
|
||||
"num-cmp",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"referencing",
|
||||
"regex",
|
||||
"regex-syntax",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"unicode-general-category",
|
||||
"uuid-simd",
|
||||
]
|
||||
|
||||
@@ -473,10 +544,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.180"
|
||||
name = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
||||
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.183"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
@@ -500,19 +577,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
name = "lru"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.1"
|
||||
name = "memchr"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "msvc_spectre_libs"
|
||||
@@ -604,15 +678,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "5.1.0"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d"
|
||||
checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
@@ -672,9 +746,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.13.0"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950"
|
||||
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
||||
|
||||
[[package]]
|
||||
name = "postcard"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
|
||||
dependencies = [
|
||||
"cobs",
|
||||
"embedded-io 0.4.0",
|
||||
"embedded-io 0.6.1",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
@@ -686,12 +772,13 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -705,38 +792,33 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pyo3"
|
||||
version = "0.24.2"
|
||||
version = "0.28.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5203598f366b11a02b13aa20cab591229ff0a89fd121a308a5df751d5fc9219"
|
||||
checksum = "cf85e27e86080aafd5a22eae58a162e133a589551542b3e5cee4beb27e54f8e1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cfg-if",
|
||||
"indoc",
|
||||
"libc",
|
||||
"memoffset",
|
||||
"once_cell",
|
||||
"portable-atomic",
|
||||
"pyo3-build-config",
|
||||
"pyo3-ffi",
|
||||
"pyo3-macros",
|
||||
"unindent",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-build-config"
|
||||
version = "0.24.2"
|
||||
version = "0.28.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99636d423fa2ca130fa5acde3059308006d46f98caac629418e53f7ebb1e9999"
|
||||
checksum = "8bf94ee265674bf76c09fa430b0e99c26e319c945d96ca0d5a8215f31bf81cf7"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-ffi"
|
||||
version = "0.24.2"
|
||||
version = "0.28.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78f9cf92ba9c409279bc3305b5409d90db2d2c22392d443a87df3a1adad59e33"
|
||||
checksum = "491aa5fc66d8059dd44a75f4580a2962c1862a1c2945359db36f6c2818b748dc"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"pyo3-build-config",
|
||||
@@ -744,9 +826,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-macros"
|
||||
version = "0.24.2"
|
||||
version = "0.28.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b999cb1a6ce21f9a6b147dcf1be9ffedf02e0043aec74dc390f3007047cecd9"
|
||||
checksum = "f5d671734e9d7a43449f8480f8b38115df67bef8d21f76837fa75ee7aaa5e52e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"pyo3-macros-backend",
|
||||
@@ -756,9 +838,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-macros-backend"
|
||||
version = "0.24.2"
|
||||
version = "0.28.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "822ece1c7e1012745607d5cf0bcb2874769f0f7cb34c4cde03b9358eb9ef911a"
|
||||
checksum = "22faaa1ce6c430a1f71658760497291065e6450d7b5dc2bcf254d49f66ee700a"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@@ -769,9 +851,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.44"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -783,33 +865,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.2"
|
||||
name = "r-efi"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
name = "rand"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"chacha20",
|
||||
"getrandom 0.4.2",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.5"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
@@ -842,13 +918,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "referencing"
|
||||
version = "0.30.0"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8eff4fa778b5c2a57e85c5f2fe3a709c52f0e60d23146e2151cbef5893f420e"
|
||||
checksum = "b8a618c14f8ba29d8193bb55e2bf13e4fb2b1115313ecb7ae94b43100c7ac7d5"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"fluent-uri",
|
||||
"once_cell",
|
||||
"getrandom 0.3.4",
|
||||
"hashbrown 0.16.1",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"serde_json",
|
||||
@@ -856,9 +933,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.2"
|
||||
version = "1.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
||||
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -868,9 +945,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.13"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
||||
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -879,16 +956,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.8"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||
|
||||
[[package]]
|
||||
name = "regorus"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"data-encoding",
|
||||
@@ -897,9 +973,12 @@ dependencies = [
|
||||
"ipnet",
|
||||
"jsonschema",
|
||||
"lazy_static",
|
||||
"lru",
|
||||
"msvc_spectre_libs",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"parking_lot",
|
||||
"postcard",
|
||||
"rand",
|
||||
"regex",
|
||||
"regorus-mimalloc",
|
||||
@@ -929,7 +1008,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regoruspy"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ordered-float",
|
||||
@@ -946,9 +1025,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.22"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984"
|
||||
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
@@ -1038,9 +1117,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
@@ -1050,9 +1129,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.114"
|
||||
version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
|
||||
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1072,9 +1151,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.13.4"
|
||||
version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1dd07eb858a2067e2f3c7155d54e929265c264e6f37efe3ee7a8d1b5a1dd0ba"
|
||||
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
@@ -1107,16 +1186,22 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
name = "unicode-general-category"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
checksum = "0b993bddc193ae5bd0d623b49ec06ac3e9312875fdae725a975c51db1cc1677f"
|
||||
|
||||
[[package]]
|
||||
name = "unindent"
|
||||
version = "0.2.4"
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
@@ -1124,12 +1209,6 @@ version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
||||
|
||||
[[package]]
|
||||
name = "unty"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.8"
|
||||
@@ -1150,14 +1229,12 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.20.0"
|
||||
version = "1.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f"
|
||||
checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"js-sys",
|
||||
"getrandom 0.4.2",
|
||||
"rand",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1167,7 +1244,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b082222b4f6619906941c17eb2297fff4c2fb96cb60164170522942a200bd8"
|
||||
dependencies = [
|
||||
"outref",
|
||||
"uuid",
|
||||
"vsimd",
|
||||
]
|
||||
|
||||
@@ -1193,10 +1269,19 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.108"
|
||||
name = "wasip3"
|
||||
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
|
||||
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
@@ -1207,9 +1292,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.108"
|
||||
version = "0.2.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
|
||||
checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -1217,9 +1302,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.108"
|
||||
version = "0.2.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
|
||||
checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
@@ -1230,13 +1315,47 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.108"
|
||||
version = "0.2.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
|
||||
checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
@@ -1301,6 +1420,88 @@ name = "wit-bindgen"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||
dependencies = [
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-macro"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-encoder",
|
||||
"wasm-metadata",
|
||||
"wasmparser",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"unicode-xid",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
@@ -1333,18 +1534,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.36"
|
||||
version = "0.8.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dafd85c832c1b68bbb4ec0c72c7f6f4fc5179627d2bc7c26b30e4c0cc11e76cc"
|
||||
checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.36"
|
||||
version = "0.8.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cb7e4e8436d9db52fbd6625dbf2f45243ab84994a72882ec8227b99e72b439a"
|
||||
checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1407,6 +1608,6 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.17"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
[package]
|
||||
name = "regoruspy"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/microsoft/regorus/bindings/python"
|
||||
description = "Python bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
|
||||
license = "MIT AND Apache-2.0 AND BSD-3-Clause"
|
||||
keywords = ["interpreter", "opa", "policy-as-code", "rego"]
|
||||
|
||||
|
||||
@@ -14,14 +15,15 @@ keywords = ["interpreter", "opa", "policy-as-code", "rego"]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[features]
|
||||
default = ["ast", "coverage", "regorus/std", "regorus/full-opa"]
|
||||
default = ["ast", "cache", "coverage", "regorus/std", "regorus/full-opa", "regorus/allocator-memory-limits"]
|
||||
ast = ["regorus/ast"]
|
||||
cache = ["regorus/cache"]
|
||||
coverage = ["regorus/coverage"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
ordered-float = "5.0.0"
|
||||
pyo3 = { version = "0.24.1", features = ["abi3-py310", "anyhow", "extension-module"] }
|
||||
ordered-float = "5.3.0"
|
||||
pyo3 = { version = "0.28.2", features = ["abi3-py310", "anyhow", "extension-module"] }
|
||||
regorus = { path = "../..", default-features = false, features = ["arc", "rvm"] }
|
||||
serde_json = "1.0.140"
|
||||
|
||||
|
||||
@@ -5,7 +5,11 @@ build-backend = "maturin"
|
||||
[project]
|
||||
name = "regorus"
|
||||
requires-python = ">=3.8"
|
||||
license = { text = "MIT AND Apache-2.0 AND BSD-3-Clause" }
|
||||
classifiers = [
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Programming Language :: Rust",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
use anyhow::{anyhow, Result};
|
||||
use core::num::{NonZeroU32, NonZeroUsize};
|
||||
use pyo3::exceptions::PyTypeError;
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::*;
|
||||
@@ -43,7 +44,7 @@ impl Default for Engine {
|
||||
|
||||
fn from(ob: &Bound<'_, PyAny>) -> Result<Value, PyErr> {
|
||||
// dicts
|
||||
Ok(if let Ok(dict) = ob.downcast::<PyDict>() {
|
||||
Ok(if let Ok(dict) = ob.cast::<PyDict>() {
|
||||
let mut map = BTreeMap::new();
|
||||
for (k, v) in dict {
|
||||
map.insert(from(&k)?, from(&v)?);
|
||||
@@ -51,7 +52,7 @@ fn from(ob: &Bound<'_, PyAny>) -> Result<Value, PyErr> {
|
||||
map.into()
|
||||
}
|
||||
// set
|
||||
else if let Ok(pset) = ob.downcast::<PySet>() {
|
||||
else if let Ok(pset) = ob.cast::<PySet>() {
|
||||
let mut set = BTreeSet::new();
|
||||
for v in pset {
|
||||
set.insert(from(&v)?);
|
||||
@@ -59,7 +60,7 @@ fn from(ob: &Bound<'_, PyAny>) -> Result<Value, PyErr> {
|
||||
set.into()
|
||||
}
|
||||
// frozen set
|
||||
else if let Ok(pfset) = ob.downcast::<PyFrozenSet>() {
|
||||
else if let Ok(pfset) = ob.cast::<PyFrozenSet>() {
|
||||
//
|
||||
let mut set = BTreeSet::new();
|
||||
for v in pfset {
|
||||
@@ -68,13 +69,13 @@ fn from(ob: &Bound<'_, PyAny>) -> Result<Value, PyErr> {
|
||||
set.into()
|
||||
}
|
||||
// lists and tuples
|
||||
else if let Ok(plist) = ob.downcast::<PyList>() {
|
||||
else if let Ok(plist) = ob.cast::<PyList>() {
|
||||
let mut array = Vec::new();
|
||||
for v in plist {
|
||||
array.push(from(&v)?);
|
||||
}
|
||||
array.into()
|
||||
} else if let Ok(ptuple) = ob.downcast::<PyTuple>() {
|
||||
} else if let Ok(ptuple) = ob.cast::<PyTuple>() {
|
||||
let mut array = Vec::new();
|
||||
for v in ptuple {
|
||||
array.push(from(&v)?);
|
||||
@@ -85,6 +86,10 @@ fn from(ob: &Bound<'_, PyAny>) -> Result<Value, PyErr> {
|
||||
else if let Ok(s) = ob.extract::<String>() {
|
||||
s.into()
|
||||
}
|
||||
// Boolean
|
||||
else if let Ok(b) = ob.extract::<bool>() {
|
||||
b.into()
|
||||
}
|
||||
// Numeric
|
||||
else if let Ok(v) = ob.extract::<i64>() {
|
||||
v.into()
|
||||
@@ -93,16 +98,12 @@ fn from(ob: &Bound<'_, PyAny>) -> Result<Value, PyErr> {
|
||||
} 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() {
|
||||
else if ob.cast::<PyNone>().is_ok() {
|
||||
Value::Null
|
||||
}
|
||||
// Anything that is a sequence
|
||||
else if let Ok(pseq) = ob.downcast::<PySequence>() {
|
||||
else if let Ok(pseq) = ob.cast::<PySequence>() {
|
||||
let mut array = Vec::new();
|
||||
for i in 0..pseq.len()? {
|
||||
array.push(from(&pseq.get_item(i)?)?);
|
||||
@@ -110,7 +111,7 @@ fn from(ob: &Bound<'_, PyAny>) -> Result<Value, PyErr> {
|
||||
array.into()
|
||||
}
|
||||
// Anything that is a map
|
||||
else if let Ok(pmap) = ob.downcast::<PyMapping>() {
|
||||
else if let Ok(pmap) = ob.cast::<PyMapping>() {
|
||||
let mut map = BTreeMap::new();
|
||||
let keys = pmap.keys()?;
|
||||
let values = pmap.values()?;
|
||||
@@ -127,7 +128,7 @@ fn from(ob: &Bound<'_, PyAny>) -> Result<Value, PyErr> {
|
||||
})
|
||||
}
|
||||
|
||||
fn to(mut v: Value, py: Python<'_>) -> Result<PyObject> {
|
||||
fn to(mut v: Value, py: Python<'_>) -> Result<Py<PyAny>> {
|
||||
let obj = match v {
|
||||
Value::Null => None::<u64>.into_bound_py_any(py),
|
||||
|
||||
@@ -138,12 +139,17 @@ fn to(mut v: Value, py: Python<'_>) -> Result<PyObject> {
|
||||
Value::String(s) => s.into_bound_py_any(py),
|
||||
|
||||
Value::Number(_) => {
|
||||
if let Ok(f) = v.as_f64() {
|
||||
if v.as_number()?.is_integer() {
|
||||
if let Ok(u) = v.as_u64() {
|
||||
u.into_bound_py_any(py)
|
||||
} else {
|
||||
v.as_i64()?.into_bound_py_any(py)
|
||||
}
|
||||
} else if let Ok(f) = v.as_f64() {
|
||||
f.into_bound_py_any(py)
|
||||
} else if let Ok(u) = v.as_u64() {
|
||||
u.into_bound_py_any(py)
|
||||
} else {
|
||||
v.as_i64()?.into_bound_py_any(py)
|
||||
// fallback
|
||||
v.as_f64()?.into_bound_py_any(py)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,7 +297,7 @@ impl Engine {
|
||||
/// Evaluate query.
|
||||
///
|
||||
/// * `query`: Rego expression to be evaluate.
|
||||
pub fn eval_query(&mut self, query: String, py: Python<'_>) -> Result<PyObject> {
|
||||
pub fn eval_query(&mut self, query: String, py: Python<'_>) -> Result<Py<PyAny>> {
|
||||
let results = self.engine.eval_query(query, false)?;
|
||||
|
||||
let rlist = PyList::empty(py);
|
||||
@@ -332,7 +338,7 @@ impl Engine {
|
||||
/// Evaluate rule.
|
||||
///
|
||||
/// * `rule`: Full path to the rule.
|
||||
pub fn eval_rule(&mut self, rule: String, py: Python<'_>) -> Result<PyObject> {
|
||||
pub fn eval_rule(&mut self, rule: String, py: Python<'_>) -> Result<Py<PyAny>> {
|
||||
to(self.engine.eval_rule(rule)?, py)
|
||||
}
|
||||
|
||||
@@ -344,6 +350,78 @@ impl Engine {
|
||||
v.to_json_str()
|
||||
}
|
||||
|
||||
/// Registers a custom Python function as a Rego extension.
|
||||
///
|
||||
/// This allows you to define functions in Python that can be called directly
|
||||
/// from your Rego policies. The Python function will be called synchronously
|
||||
/// during policy evaluation.
|
||||
///
|
||||
/// Arguments passed from Rego are automatically converted to their corresponding
|
||||
/// Python types. The return value is converted back to a Rego value.
|
||||
///
|
||||
/// * `path`: Full path to the function as it will be used in Rego.
|
||||
/// * `nargs`: The number of arguments the function expects.
|
||||
/// * `extension`: The Python function to execute. Must accept exactly `nargs` arguments.
|
||||
///
|
||||
/// Note: When the engine is cloned, extensions share the same Python callable reference
|
||||
/// rather than being deep-copied. Stateful callables will share state across clones.
|
||||
pub fn add_extension(&mut self, path: String, nargs: u8, extension: Py<PyAny>) -> Result<()> {
|
||||
Python::attach(|py| {
|
||||
if !extension.bind(py).is_callable() {
|
||||
return Err(anyhow!("extension '{}' must be callable", path));
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let func_ref = Arc::new(extension);
|
||||
let path_clone = path.clone();
|
||||
|
||||
let extension_impl = move |args: Vec<Value>| -> Result<Value, anyhow::Error> {
|
||||
Python::attach(|py| {
|
||||
let py_args_vec: Result<Vec<Py<PyAny>>> =
|
||||
args.into_iter().map(|arg| to(arg, py)).collect();
|
||||
let py_args = PyTuple::new(py, py_args_vec?)?;
|
||||
let py_result = func_ref.call1(py, py_args).map_err(|e| {
|
||||
anyhow!("extension '{}' raises Python error: {}", path_clone, e)
|
||||
})?;
|
||||
let rego_result = from(&py_result.into_bound(py))?;
|
||||
Ok(rego_result)
|
||||
})
|
||||
};
|
||||
|
||||
self.engine
|
||||
.add_extension(path, nargs, Box::new(extension_impl))
|
||||
}
|
||||
|
||||
/// Set the policy length limits used when loading policies.
|
||||
///
|
||||
/// * `max_col`: Maximum column width per line.
|
||||
/// * `max_file_bytes`: Maximum policy file size in bytes.
|
||||
/// * `max_lines`: Maximum number of lines per policy file.
|
||||
#[pyo3(signature = (*, max_col, max_file_bytes, max_lines))]
|
||||
pub fn set_policy_length_config(
|
||||
&mut self,
|
||||
max_col: u32,
|
||||
max_file_bytes: usize,
|
||||
max_lines: usize,
|
||||
) -> Result<()> {
|
||||
self.engine
|
||||
.set_policy_length_config(::regorus::PolicyLengthConfig {
|
||||
max_col: NonZeroU32::new(max_col)
|
||||
.ok_or_else(|| anyhow!("max_col must be non-zero"))?,
|
||||
max_file_bytes: NonZeroUsize::new(max_file_bytes)
|
||||
.ok_or_else(|| anyhow!("max_file_bytes must be non-zero"))?,
|
||||
max_lines: NonZeroUsize::new(max_lines)
|
||||
.ok_or_else(|| anyhow!("max_lines must be non-zero"))?,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clear the policy length configuration, reverting to defaults.
|
||||
pub fn clear_policy_length_config(&mut self) {
|
||||
self.engine.clear_policy_length_config();
|
||||
}
|
||||
|
||||
/// Enable code coverage
|
||||
///
|
||||
/// * `enable`: Whether to enable coverage or not.
|
||||
@@ -544,10 +622,33 @@ impl Rvm {
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure the global pattern caches used by `regex.*` and `glob.*` builtins.
|
||||
///
|
||||
/// * `regex`: Maximum cached compiled regex patterns (default 256, 0 = disabled).
|
||||
/// * `glob`: Maximum cached compiled glob matchers (default 128, 0 = disabled).
|
||||
#[cfg(feature = "cache")]
|
||||
#[pyfunction]
|
||||
#[pyo3(signature = (*, regex = 256, glob = 128))]
|
||||
fn set_cache_config(regex: usize, glob: usize) {
|
||||
::regorus::cache::configure(::regorus::cache::Config { regex, glob });
|
||||
}
|
||||
|
||||
/// Clear all entries from every pattern cache.
|
||||
#[cfg(feature = "cache")]
|
||||
#[pyfunction]
|
||||
fn clear_cache() {
|
||||
::regorus::cache::clear();
|
||||
}
|
||||
|
||||
#[pymodule]
|
||||
pub fn regorus(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_class::<crate::Engine>()?;
|
||||
m.add_class::<crate::Program>()?;
|
||||
m.add_class::<crate::Rvm>()?;
|
||||
#[cfg(feature = "cache")]
|
||||
{
|
||||
m.add_function(wrap_pyfunction!(set_cache_config, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(clear_cache, m)?)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import regorus
|
||||
import sys
|
||||
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
|
||||
# Configure the global pattern caches.
|
||||
regorus.set_cache_config(regex=256, glob=128)
|
||||
|
||||
# Create engine
|
||||
engine = regorus.Engine()
|
||||
|
||||
engine.set_rego_v0(True)
|
||||
# Raise the default col limit to 2000
|
||||
engine.set_policy_length_config(max_col=2000, max_file_bytes=1048576, max_lines=20000)
|
||||
|
||||
# Load policies
|
||||
pkg = engine.add_policy_from_file('../../tests/aci/framework.rego')
|
||||
@@ -163,3 +167,254 @@ def run_host_await_example():
|
||||
print(vm.resume('{"tier":"gold"}'))
|
||||
|
||||
run_host_await_example()
|
||||
|
||||
def test_extension_execution():
|
||||
rego = regorus.Engine()
|
||||
rego.add_policy("demo",
|
||||
"""
|
||||
package demo
|
||||
|
||||
result := greeting(a, b) if {
|
||||
a := data.a
|
||||
b := data.b
|
||||
}
|
||||
""")
|
||||
|
||||
def custom_function(arg1, arg2):
|
||||
return f"{arg1}, {arg2}!"
|
||||
rego.add_extension("greeting", 2, custom_function)
|
||||
|
||||
rego.add_data({"a": "Hello", "b": "World"})
|
||||
result = rego.eval_rule("data.demo.result")
|
||||
assert result == "Hello, World!", f"Unexpected result: {result}"
|
||||
|
||||
test_extension_execution()
|
||||
|
||||
def test_extension_wrong_arity():
|
||||
rego = regorus.Engine()
|
||||
rego.add_policy("demo",
|
||||
"""
|
||||
package demo
|
||||
|
||||
result := greeting(a, b) if {
|
||||
a := data.a
|
||||
b := data.b
|
||||
}
|
||||
""")
|
||||
|
||||
def custom_function(arg1, arg2):
|
||||
return f"{arg1}, {arg2}!"
|
||||
|
||||
rego.add_extension("greeting", 3, custom_function)
|
||||
rego.add_data({"a": "Hello", "b": "World"})
|
||||
|
||||
try:
|
||||
rego.eval_rule("data.demo.result")
|
||||
except RuntimeError as ex:
|
||||
assert "error: incorrect number of parameters supplied to extension" in str(ex)
|
||||
else:
|
||||
assert False, "exception not thrown"
|
||||
|
||||
test_extension_wrong_arity()
|
||||
|
||||
def test_extension_raises_exception():
|
||||
rego = regorus.Engine()
|
||||
rego.add_policy("demo",
|
||||
"""
|
||||
package demo
|
||||
|
||||
result := greeting(a, b) if {
|
||||
a := data.a
|
||||
b := data.b
|
||||
}
|
||||
""")
|
||||
|
||||
def custom_function(arg1, arg2):
|
||||
raise RuntimeError("unknown error")
|
||||
|
||||
rego.add_extension("greeting", 2, custom_function)
|
||||
rego.add_data({"a": "Hello", "b": "World"})
|
||||
|
||||
try:
|
||||
rego.eval_rule("data.demo.result")
|
||||
except RuntimeError as ex:
|
||||
assert "error: extension 'greeting' raises Python error: RuntimeError: unknown error" in str(ex)
|
||||
else:
|
||||
assert False, "exception not thrown"
|
||||
|
||||
test_extension_raises_exception()
|
||||
|
||||
|
||||
def test_extension_zero_arg():
|
||||
rego = regorus.Engine()
|
||||
rego.add_policy("demo",
|
||||
"""
|
||||
package demo
|
||||
|
||||
result := greeting()
|
||||
""")
|
||||
|
||||
def custom_function():
|
||||
return "Hello, World!"
|
||||
|
||||
rego.add_extension("greeting", 0, custom_function)
|
||||
rego.add_data({"a": "Hello", "b": "World"})
|
||||
|
||||
result = rego.eval_rule("data.demo.result")
|
||||
assert result == "Hello, World!", f"Unexpected result: {result}"
|
||||
|
||||
test_extension_zero_arg()
|
||||
|
||||
def test_extension_non_callable():
|
||||
rego = regorus.Engine()
|
||||
rego.add_policy("demo",
|
||||
"""
|
||||
package demo
|
||||
|
||||
result := greeting()
|
||||
""")
|
||||
|
||||
try:
|
||||
rego.add_extension("greeting", 0, 123)
|
||||
except RuntimeError as ex:
|
||||
assert "extension 'greeting' must be callable" in str(ex)
|
||||
else:
|
||||
assert False, "exception not thrown"
|
||||
|
||||
test_extension_non_callable()
|
||||
|
||||
|
||||
def test_extension_duplicate():
|
||||
rego = regorus.Engine()
|
||||
rego.add_policy("demo",
|
||||
"""
|
||||
package demo
|
||||
|
||||
result := greeting()
|
||||
""")
|
||||
|
||||
def custom_function1(arg1, arg2):
|
||||
return f"{arg1}, {arg2}!"
|
||||
def custom_function2(arg1, arg2):
|
||||
return f"{arg1}, {arg2}!"
|
||||
|
||||
rego.add_extension("greeting", 0, custom_function1)
|
||||
|
||||
try:
|
||||
rego.add_extension("greeting", 0, custom_function2)
|
||||
except RuntimeError as ex:
|
||||
assert "extension already added" in str(ex)
|
||||
else:
|
||||
assert False, "exception not thrown"
|
||||
|
||||
test_extension_duplicate()
|
||||
|
||||
|
||||
def test_extension_types():
|
||||
rego = regorus.Engine()
|
||||
rego.add_policy("demo",
|
||||
"""
|
||||
package demo
|
||||
|
||||
i := custom.triple(10)
|
||||
f := custom.triple(2.5)
|
||||
b1 := custom.negate(true)
|
||||
b2 := custom.negate(false)
|
||||
|
||||
a := custom.first([true, null, 1])
|
||||
b := custom.first([null, null, 1])
|
||||
c := custom.first([null, null, null])
|
||||
|
||||
object := custom.modify_object({"a": 1, "b": 2})
|
||||
list := custom.modify_list([3, 4])
|
||||
set := custom.modify_set({5, 6})
|
||||
""")
|
||||
|
||||
def triple(n):
|
||||
return n*3
|
||||
|
||||
def negate(b):
|
||||
return not b
|
||||
|
||||
def first(lst):
|
||||
for i in lst:
|
||||
if i is not None:
|
||||
return i
|
||||
return None
|
||||
|
||||
def modify_object(object):
|
||||
assert isinstance(object, dict)
|
||||
return {k: v*2 for k, v in object.items()}
|
||||
|
||||
def modify_list(lst):
|
||||
assert isinstance(lst, list)
|
||||
return [x*2 for x in lst]
|
||||
|
||||
def modify_set(st):
|
||||
assert isinstance(st, set)
|
||||
return {x*2 for x in st}
|
||||
|
||||
rego.add_extension("custom.triple", 1, triple)
|
||||
rego.add_extension("custom.negate", 1, negate)
|
||||
rego.add_extension("custom.first", 1, first)
|
||||
rego.add_extension("custom.modify_object", 1, modify_object)
|
||||
rego.add_extension("custom.modify_list", 1, modify_list)
|
||||
rego.add_extension("custom.modify_set", 1, modify_set)
|
||||
|
||||
i = rego.eval_rule("data.demo.i")
|
||||
assert i == 30, f"Unexpected result for 'i': {i}"
|
||||
|
||||
f = rego.eval_rule("data.demo.f")
|
||||
assert f == 7.5, f"Unexpected result for 'f': {f}"
|
||||
|
||||
b1 = rego.eval_rule("data.demo.b1")
|
||||
assert b1 == False, f"Unexpected result for 'b1': {b1}"
|
||||
|
||||
b2 = rego.eval_rule("data.demo.b2")
|
||||
assert b2 == True, f"Unexpected result for 'b2': {b2}"
|
||||
|
||||
a = rego.eval_rule("data.demo.a")
|
||||
assert a == True, f"Unexpected result for 'a': {a}"
|
||||
|
||||
b = rego.eval_rule("data.demo.b")
|
||||
assert b == 1, f"Unexpected result for 'b': {b}"
|
||||
|
||||
c = rego.eval_rule("data.demo.c")
|
||||
assert c is None, f"Unexpected result for 'c': {c}"
|
||||
|
||||
obj = rego.eval_rule("data.demo.object")
|
||||
assert obj == {"a": 2, "b": 4}, f"Unexpected object: {obj}"
|
||||
|
||||
lst = rego.eval_rule("data.demo.list")
|
||||
assert lst == [6, 8], f"Unexpected list: {lst}"
|
||||
|
||||
st = rego.eval_rule("data.demo.set")
|
||||
assert st == {10, 12}, f"Unexpected set: {st}"
|
||||
|
||||
test_extension_types()
|
||||
|
||||
def test_boolean_mapping():
|
||||
rego = regorus.Engine()
|
||||
rego.add_policy("demo",
|
||||
"""
|
||||
package demo
|
||||
|
||||
result_b := data.a if {
|
||||
data.a == true
|
||||
}
|
||||
|
||||
result_i := data.b if {
|
||||
data.b == 1
|
||||
}
|
||||
""")
|
||||
rego.add_data({"a": True, "b": 1})
|
||||
|
||||
result_b = rego.eval_rule("data.demo.result_b")
|
||||
assert isinstance(result_b, bool), f"Expected bool, got {type(result_b)}"
|
||||
assert result_b, f"Unexpected result for 'result_b': {result_b}"
|
||||
|
||||
result_i = rego.eval_rule("data.demo.result_i")
|
||||
assert isinstance(result_i, int), f"Expected int, got {type(result_i)}"
|
||||
assert result_i == 1, f"Unexpected result for 'result_i': {result_i}"
|
||||
|
||||
test_boolean_mapping()
|
||||
|
||||
392
bindings/ruby/Cargo.lock
generated
392
bindings/ruby/Cargo.lock
generated
@@ -9,7 +9,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"getrandom 0.3.4",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"version_check",
|
||||
@@ -25,6 +25,12 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
@@ -36,9 +42,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.100"
|
||||
version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
@@ -46,12 +52,6 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.69.5"
|
||||
@@ -147,10 +147,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.43"
|
||||
name = "chacha20"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
|
||||
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
@@ -186,6 +197,15 @@ version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.10.0"
|
||||
@@ -226,9 +246,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "fancy-regex"
|
||||
version = "0.14.0"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298"
|
||||
checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"regex-automata",
|
||||
@@ -243,15 +263,27 @@ checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db"
|
||||
|
||||
[[package]]
|
||||
name = "fluent-uri"
|
||||
version = "0.3.2"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1918b65d96df47d3591bed19c5cca17e3fa5d0707318e4b5ef2eae01764df7e5"
|
||||
checksum = "bc74ac4d8359ae70623506d512209619e5cf8f347124910440dbc221714b328e"
|
||||
dependencies = [
|
||||
"borrow-or-share",
|
||||
"ref-cast",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.2"
|
||||
@@ -278,9 +310,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"r-efi 5.3.0",
|
||||
"wasip2",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi 6.0.0",
|
||||
"rand_core",
|
||||
"wasip2",
|
||||
"wasip3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -301,11 +349,31 @@ dependencies = [
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash 0.1.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
@@ -412,6 +480,12 @@ dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.1.0"
|
||||
@@ -440,14 +514,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"hashbrown 0.16.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.11.0"
|
||||
version = "2.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
||||
checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
@@ -476,27 +552,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jsonschema"
|
||||
version = "0.30.0"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1b46a0365a611fbf1d2143104dcf910aada96fafd295bab16c60b802bf6fa1d"
|
||||
checksum = "6f29616f6e19415398eb186964fb7cbbeef572c79bede3622a8277667924bbe3"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"base64",
|
||||
"bytecount",
|
||||
"data-encoding",
|
||||
"email_address",
|
||||
"fancy-regex",
|
||||
"fraction",
|
||||
"getrandom 0.3.4",
|
||||
"idna",
|
||||
"itoa",
|
||||
"num-cmp",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"referencing",
|
||||
"regex",
|
||||
"regex-syntax",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"unicode-general-category",
|
||||
"uuid-simd",
|
||||
]
|
||||
|
||||
@@ -512,6 +589,12 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.180"
|
||||
@@ -550,10 +633,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "magnus"
|
||||
version = "0.7.1"
|
||||
name = "lru"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d87ae53030f3a22e83879e666cb94e58a7bdf31706878a0ba48752994146dab"
|
||||
checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
|
||||
|
||||
[[package]]
|
||||
name = "magnus"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b36a5b126bbe97eb0d02d07acfeb327036c6319fd816139a49824a83b7f9012"
|
||||
dependencies = [
|
||||
"magnus-macros",
|
||||
"rb-sys",
|
||||
@@ -563,9 +652,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "magnus-macros"
|
||||
version = "0.6.0"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5968c820e2960565f647819f5928a42d6e874551cab9d88d75e3e0660d7f71e3"
|
||||
checksum = "47607461fd8e1513cb4f2076c197d8092d921a1ea75bd08af97398f593751892"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -578,20 +667,6 @@ version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "mimalloc"
|
||||
version = "2.2.6"
|
||||
dependencies = [
|
||||
"mimalloc-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mimalloc-sys"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
@@ -765,12 +840,13 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -798,33 +874,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.2"
|
||||
name = "r-efi"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
name = "rand"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"chacha20",
|
||||
"getrandom 0.4.2",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.5"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
|
||||
|
||||
[[package]]
|
||||
name = "rb-sys"
|
||||
@@ -852,9 +922,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rb-sys-env"
|
||||
version = "0.1.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a35802679f07360454b418a5d1735c89716bde01d35b1560fc953c1415a0b3bb"
|
||||
checksum = "cca7ad6a7e21e72151d56fe2495a259b5670e204c3adac41ee7ef676ea08117a"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
@@ -887,13 +957,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "referencing"
|
||||
version = "0.30.0"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8eff4fa778b5c2a57e85c5f2fe3a709c52f0e60d23146e2151cbef5893f420e"
|
||||
checksum = "b8a618c14f8ba29d8193bb55e2bf13e4fb2b1115313ecb7ae94b43100c7ac7d5"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"fluent-uri",
|
||||
"once_cell",
|
||||
"getrandom 0.3.4",
|
||||
"hashbrown 0.16.1",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"serde_json",
|
||||
@@ -901,9 +972,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.2"
|
||||
version = "1.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
||||
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -930,37 +1001,56 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||
|
||||
[[package]]
|
||||
name = "regorus"
|
||||
version = "0.5.0"
|
||||
version = "0.9.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"data-encoding",
|
||||
"globset",
|
||||
"indexmap",
|
||||
"ipnet",
|
||||
"jsonschema",
|
||||
"lazy_static",
|
||||
"mimalloc",
|
||||
"lru",
|
||||
"msvc_spectre_libs",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"parking_lot",
|
||||
"rand",
|
||||
"regex",
|
||||
"regorus-mimalloc",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"spin",
|
||||
"thiserror",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regorus-mimalloc"
|
||||
version = "2.2.6"
|
||||
dependencies = [
|
||||
"regorus-mimalloc-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regorus-mimalloc-sys"
|
||||
version = "2.2.6"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regorusrb"
|
||||
version = "0.6.0"
|
||||
version = "0.9.1"
|
||||
dependencies = [
|
||||
"magnus",
|
||||
"regorus",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_magnus",
|
||||
]
|
||||
@@ -1046,9 +1136,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_magnus"
|
||||
version = "0.9.0"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b8b945a2dadb221f1c5490cfb411cab6c3821446b8eca50ee07e5a3893ec51"
|
||||
checksum = "8ff64c88ddd26acdcad5a501f18bcc339927b77b69f4a03bfaf2a6fc5ba2ac4b"
|
||||
dependencies = [
|
||||
"magnus",
|
||||
"serde",
|
||||
@@ -1092,6 +1182,12 @@ version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
@@ -1156,12 +1252,24 @@ dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-general-category"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b993bddc193ae5bd0d623b49ec06ac3e9312875fdae725a975c51db1cc1677f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.11"
|
||||
@@ -1188,14 +1296,12 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.19.0"
|
||||
version = "1.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
|
||||
checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"js-sys",
|
||||
"getrandom 0.4.2",
|
||||
"rand",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1205,7 +1311,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b082222b4f6619906941c17eb2297fff4c2fb96cb60164170522942a200bd8"
|
||||
dependencies = [
|
||||
"outref",
|
||||
"uuid",
|
||||
"vsimd",
|
||||
]
|
||||
|
||||
@@ -1230,6 +1335,15 @@ dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasip3"
|
||||
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.108"
|
||||
@@ -1275,6 +1389,40 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
@@ -1339,6 +1487,88 @@ name = "wit-bindgen"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||
dependencies = [
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-macro"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-encoder",
|
||||
"wasm-metadata",
|
||||
"wasmparser",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"unicode-xid",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
|
||||
@@ -7,10 +7,10 @@ gemspec
|
||||
|
||||
# These gems are required for local development and testing,
|
||||
# but won't be included in the published gem
|
||||
gem "minitest", "~> 5.25"
|
||||
gem "rake", "~> 13.2"
|
||||
gem "rake-compiler", "~> 1.2"
|
||||
gem "rake-compiler-dock", "~> 1.9"
|
||||
gem "rubocop", "~> 1.73", require: false
|
||||
gem "rubocop-minitest", "~> 0.37.1", require: false
|
||||
gem "minitest", "~> 6.0"
|
||||
gem "rake", "~> 13.3"
|
||||
gem "rake-compiler", "~> 1.3"
|
||||
gem "rake-compiler-dock", "~> 1.11"
|
||||
gem "rubocop", "~> 1.86", require: false
|
||||
gem "rubocop-minitest", "~> 0.39.1", require: false
|
||||
gem "rubocop-rake", "~> 0.7.1", require: false
|
||||
|
||||
@@ -7,25 +7,30 @@ PATH
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
ast (2.4.2)
|
||||
json (2.10.2)
|
||||
language_server-protocol (3.17.0.4)
|
||||
ast (2.4.3)
|
||||
drb (2.2.3)
|
||||
json (2.19.2)
|
||||
language_server-protocol (3.17.0.5)
|
||||
lint_roller (1.1.0)
|
||||
minitest (5.25.4)
|
||||
parallel (1.26.3)
|
||||
parser (3.3.7.1)
|
||||
minitest (6.0.3)
|
||||
drb (~> 2.0)
|
||||
prism (~> 1.5)
|
||||
parallel (1.27.0)
|
||||
parser (3.3.10.2)
|
||||
ast (~> 2.4.1)
|
||||
racc
|
||||
prism (1.9.0)
|
||||
racc (1.8.1)
|
||||
rainbow (3.1.1)
|
||||
rake (13.2.1)
|
||||
rake-compiler (1.2.9)
|
||||
rake (13.3.1)
|
||||
rake-compiler (1.3.1)
|
||||
rake
|
||||
rake-compiler-dock (1.9.1)
|
||||
rb_sys (0.9.111)
|
||||
rake-compiler-dock (= 1.9.1)
|
||||
regexp_parser (2.10.0)
|
||||
rubocop (1.73.2)
|
||||
rake-compiler-dock (1.11.0)
|
||||
rb_sys (0.9.125)
|
||||
json (>= 2)
|
||||
rake-compiler-dock (= 1.11.0)
|
||||
regexp_parser (2.11.3)
|
||||
rubocop (1.86.0)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (~> 3.17.0.2)
|
||||
lint_roller (~> 1.1.0)
|
||||
@@ -33,35 +38,36 @@ GEM
|
||||
parser (>= 3.3.0.2)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 2.9.3, < 3.0)
|
||||
rubocop-ast (>= 1.38.0, < 2.0)
|
||||
rubocop-ast (>= 1.49.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 2.4.0, < 4.0)
|
||||
rubocop-ast (1.38.1)
|
||||
parser (>= 3.3.1.0)
|
||||
rubocop-minitest (0.37.1)
|
||||
rubocop-ast (1.49.1)
|
||||
parser (>= 3.3.7.2)
|
||||
prism (~> 1.7)
|
||||
rubocop-minitest (0.39.1)
|
||||
lint_roller (~> 1.1)
|
||||
rubocop (>= 1.72.1, < 2.0)
|
||||
rubocop (>= 1.75.0, < 2.0)
|
||||
rubocop-ast (>= 1.38.0, < 2.0)
|
||||
rubocop-rake (0.7.1)
|
||||
lint_roller (~> 1.1)
|
||||
rubocop (>= 1.72.1)
|
||||
ruby-progressbar (1.13.0)
|
||||
unicode-display_width (3.1.4)
|
||||
unicode-emoji (~> 4.0, >= 4.0.4)
|
||||
unicode-emoji (4.0.4)
|
||||
unicode-display_width (3.2.0)
|
||||
unicode-emoji (~> 4.1)
|
||||
unicode-emoji (4.2.0)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
minitest (~> 5.25)
|
||||
rake (~> 13.2)
|
||||
rake-compiler (~> 1.2)
|
||||
rake-compiler-dock (~> 1.9)
|
||||
minitest (~> 6.0)
|
||||
rake (~> 13.3)
|
||||
rake-compiler (~> 1.3)
|
||||
rake-compiler-dock (~> 1.11)
|
||||
regorusrb!
|
||||
rubocop (~> 1.73)
|
||||
rubocop-minitest (~> 0.37.1)
|
||||
rubocop (~> 1.86)
|
||||
rubocop-minitest (~> 0.39.1)
|
||||
rubocop-rake (~> 0.7.1)
|
||||
|
||||
BUNDLED WITH
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
[package]
|
||||
name = "regorusrb"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
edition = "2024"
|
||||
description = "Ruby bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
|
||||
license = "MIT AND Apache-2.0 AND BSD-3-Clause"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
@@ -10,12 +11,14 @@ crate-type = ["cdylib"]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[features]
|
||||
default = ["ast", "coverage", "regorus/std", "regorus/full-opa"]
|
||||
default = ["ast", "cache", "coverage", "regorus/std", "regorus/full-opa", "regorus/allocator-memory-limits"]
|
||||
ast = ["regorus/ast"]
|
||||
cache = ["regorus/cache"]
|
||||
coverage = ["regorus/coverage"]
|
||||
|
||||
[dependencies]
|
||||
magnus = { version = "0.7.1" }
|
||||
magnus = { version = "0.8.2" }
|
||||
regorus = { path = "../../../..", default-features = false, features = ["arc"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1.0.140"
|
||||
serde_magnus = "0.9.0"
|
||||
serde_magnus = "0.11.0"
|
||||
|
||||
@@ -1,10 +1,26 @@
|
||||
use core::num::{NonZeroU32, NonZeroUsize};
|
||||
use magnus::{Error, Ruby, exception::runtime_error, method, module, prelude::*};
|
||||
use regorus::Engine as RegorusEngine;
|
||||
use serde::Deserialize;
|
||||
use std::cell::RefCell;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
// `Value` exists under magnus, regorus, and serde_json, so be explicit
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct PolicyLengthSpec {
|
||||
max_col: u32,
|
||||
max_file_bytes: usize,
|
||||
max_lines: usize,
|
||||
}
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
#[derive(Deserialize)]
|
||||
struct CacheConfigSpec {
|
||||
regex: usize,
|
||||
glob: usize,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[magnus::wrap(class = "Regorus::Engine")]
|
||||
pub struct Engine {
|
||||
@@ -55,15 +71,17 @@ impl Engine {
|
||||
.map_err(|e| Error::new(runtime_error(), format!("Failed to add policy: {e}")))
|
||||
}
|
||||
|
||||
fn add_data(&self, ruby_hash: magnus::RHash) -> Result<(), Error> {
|
||||
let data_value: regorus::Value = serde_magnus::deserialize(ruby_hash).map_err(|e| {
|
||||
Error::new(
|
||||
runtime_error(),
|
||||
format!("Failed to deserialize Ruby value: {e}"),
|
||||
)
|
||||
})?;
|
||||
fn add_data(ruby: &Ruby, rb_self: &Self, ruby_hash: magnus::RHash) -> Result<(), Error> {
|
||||
let data_value: regorus::Value =
|
||||
serde_magnus::deserialize(ruby, ruby_hash).map_err(|e| {
|
||||
Error::new(
|
||||
runtime_error(),
|
||||
format!("Failed to deserialize Ruby value: {e}"),
|
||||
)
|
||||
})?;
|
||||
|
||||
self.engine
|
||||
rb_self
|
||||
.engine
|
||||
.borrow_mut()
|
||||
.add_data(data_value)
|
||||
.map_err(|e| Error::new(runtime_error(), format!("Failed to add data: {e}")))
|
||||
@@ -111,15 +129,16 @@ impl Engine {
|
||||
.map_err(|e| Error::new(runtime_error(), format!("Failed to get policies: {e}")))
|
||||
}
|
||||
|
||||
fn set_input(&self, ruby_hash: magnus::RHash) -> Result<(), Error> {
|
||||
let input_value: regorus::Value = serde_magnus::deserialize(ruby_hash).map_err(|e| {
|
||||
Error::new(
|
||||
runtime_error(),
|
||||
format!("Failed to deserialize Ruby value: {e}"),
|
||||
)
|
||||
})?;
|
||||
fn set_input(ruby: &Ruby, rb_self: &Self, ruby_hash: magnus::RHash) -> Result<(), Error> {
|
||||
let input_value: regorus::Value =
|
||||
serde_magnus::deserialize(ruby, ruby_hash).map_err(|e| {
|
||||
Error::new(
|
||||
runtime_error(),
|
||||
format!("Failed to deserialize Ruby value: {e}"),
|
||||
)
|
||||
})?;
|
||||
|
||||
self.engine.borrow_mut().set_input(input_value);
|
||||
rb_self.engine.borrow_mut().set_input(input_value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -142,14 +161,14 @@ impl Engine {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn eval_query(&self, query: String) -> Result<magnus::Value, Error> {
|
||||
let results = self
|
||||
fn eval_query(ruby: &Ruby, rb_self: &Self, query: String) -> Result<magnus::Value, Error> {
|
||||
let results = rb_self
|
||||
.engine
|
||||
.borrow_mut()
|
||||
.eval_query(query, false)
|
||||
.map_err(|e| Error::new(runtime_error(), format!("Failed to evaluate query: {e}")))?;
|
||||
|
||||
serde_magnus::serialize(&results).map_err(|e| {
|
||||
serde_magnus::serialize(ruby, &results).map_err(|e| {
|
||||
Error::new(
|
||||
runtime_error(),
|
||||
format!("Failed to serailzie query results: {e}"),
|
||||
@@ -177,15 +196,19 @@ impl Engine {
|
||||
})
|
||||
}
|
||||
|
||||
fn eval_rule(&self, query: String) -> Result<Option<magnus::Value>, Error> {
|
||||
fn eval_rule(
|
||||
ruby: &Ruby,
|
||||
rb_self: &Self,
|
||||
query: String,
|
||||
) -> Result<Option<magnus::Value>, Error> {
|
||||
let result =
|
||||
self.engine.borrow_mut().eval_rule(query).map_err(|e| {
|
||||
rb_self.engine.borrow_mut().eval_rule(query).map_err(|e| {
|
||||
Error::new(runtime_error(), format!("Failed to evaluate rule: {e}"))
|
||||
})?;
|
||||
|
||||
match result {
|
||||
regorus::Value::Undefined => Ok(None), // Convert undefined to Ruby's nil
|
||||
_ => serde_magnus::serialize(&result) // Serialize other results normally
|
||||
regorus::Value::Undefined => Ok(None),
|
||||
_ => serde_magnus::serialize(ruby, &result)
|
||||
.map(Some)
|
||||
.map_err(|e| {
|
||||
magnus::Error::new(
|
||||
@@ -280,6 +303,34 @@ impl Engine {
|
||||
})
|
||||
}
|
||||
|
||||
fn set_policy_length_config(
|
||||
ruby: &Ruby,
|
||||
rb_self: &Self,
|
||||
hash: magnus::RHash,
|
||||
) -> Result<(), Error> {
|
||||
let spec: PolicyLengthSpec = serde_magnus::deserialize(ruby, hash).map_err(|e| {
|
||||
Error::new(
|
||||
runtime_error(),
|
||||
format!("Failed to deserialize policy length config: {e}"),
|
||||
)
|
||||
})?;
|
||||
let config = regorus::PolicyLengthConfig {
|
||||
max_col: NonZeroU32::new(spec.max_col)
|
||||
.ok_or_else(|| Error::new(runtime_error(), "max_col must be non-zero"))?,
|
||||
max_file_bytes: NonZeroUsize::new(spec.max_file_bytes)
|
||||
.ok_or_else(|| Error::new(runtime_error(), "max_file_bytes must be non-zero"))?,
|
||||
max_lines: NonZeroUsize::new(spec.max_lines)
|
||||
.ok_or_else(|| Error::new(runtime_error(), "max_lines must be non-zero"))?,
|
||||
};
|
||||
rb_self.engine.borrow_mut().set_policy_length_config(config);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clear_policy_length_config(&self) -> Result<(), Error> {
|
||||
self.engine.borrow_mut().clear_policy_length_config();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ast")]
|
||||
fn get_ast_as_json(&self) -> Result<String, Error> {
|
||||
self.engine
|
||||
@@ -361,7 +412,47 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
||||
engine_class.define_method("set_gather_prints", method!(Engine::set_gather_prints, 1))?;
|
||||
engine_class.define_method("take_prints", method!(Engine::take_prints, 0))?;
|
||||
|
||||
// policy length limits
|
||||
engine_class.define_method(
|
||||
"set_policy_length_config",
|
||||
method!(Engine::set_policy_length_config, 1),
|
||||
)?;
|
||||
engine_class.define_method(
|
||||
"clear_policy_length_config",
|
||||
method!(Engine::clear_policy_length_config, 0),
|
||||
)?;
|
||||
|
||||
// ast
|
||||
engine_class.define_method("get_ast_as_json", method!(Engine::get_ast_as_json, 0))?;
|
||||
|
||||
// cache configuration (module-level)
|
||||
#[cfg(feature = "cache")]
|
||||
{
|
||||
regorus_module
|
||||
.define_module_function("set_cache_config", magnus::function!(set_cache_config, 1))?;
|
||||
regorus_module.define_module_function("clear_cache", magnus::function!(clear_cache, 0))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
fn set_cache_config(ruby: &Ruby, hash: magnus::RHash) -> Result<(), Error> {
|
||||
let spec: CacheConfigSpec = serde_magnus::deserialize(ruby, hash).map_err(|e| {
|
||||
Error::new(
|
||||
runtime_error(),
|
||||
format!("Failed to deserialize cache config: {e}"),
|
||||
)
|
||||
})?;
|
||||
regorus::cache::configure(regorus::cache::Config {
|
||||
regex: spec.regex,
|
||||
glob: spec.glob,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
fn clear_cache() -> Result<(), Error> {
|
||||
regorus::cache::clear();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Regorus
|
||||
VERSION = "0.9.0"
|
||||
VERSION = "0.9.1"
|
||||
end
|
||||
|
||||
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
||||
|
||||
spec.summary = "Ruby bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
|
||||
spec.homepage = "https://github.com/microsoft/regorus/blob/main/bindings/ruby"
|
||||
spec.license = "MIT"
|
||||
spec.licenses = ["MIT", "Apache-2.0", "BSD-3-Clause"]
|
||||
spec.required_ruby_version = ">= 3.4.2"
|
||||
spec.required_rubygems_version = ">= 3.6.5"
|
||||
|
||||
|
||||
@@ -183,6 +183,16 @@ class TestRegorus < Minitest::Test
|
||||
assert_equal ["<query.rego>:1: Hello"], @engine.take_prints
|
||||
end
|
||||
|
||||
def test_set_policy_length_config
|
||||
@engine.set_policy_length_config({ max_col: 2000, max_file_bytes: 1048576, max_lines: 20000 })
|
||||
@engine.clear_policy_length_config
|
||||
end
|
||||
|
||||
def test_set_cache_config
|
||||
::Regorus.set_cache_config({ regex: 256, glob: 128 })
|
||||
::Regorus.clear_cache
|
||||
end
|
||||
|
||||
def alice_results
|
||||
{
|
||||
result: [
|
||||
|
||||
532
bindings/wasm/Cargo.lock
generated
532
bindings/wasm/Cargo.lock
generated
@@ -25,6 +25,12 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
@@ -36,9 +42,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.100"
|
||||
version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
@@ -57,22 +63,6 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"unty",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.8.0"
|
||||
@@ -90,9 +80,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
|
||||
[[package]]
|
||||
name = "borrow-or-share"
|
||||
@@ -112,9 +102,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.19.1"
|
||||
version = "3.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
||||
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
||||
|
||||
[[package]]
|
||||
name = "bytecount"
|
||||
@@ -130,9 +120,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.55"
|
||||
version = "1.2.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29"
|
||||
checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
@@ -145,10 +135,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.43"
|
||||
name = "chacha20"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
|
||||
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
@@ -167,12 +168,30 @@ dependencies = [
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cobs"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.10.0"
|
||||
@@ -199,6 +218,18 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
@@ -207,9 +238,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "fancy-regex"
|
||||
version = "0.14.0"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298"
|
||||
checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"regex-automata",
|
||||
@@ -224,15 +255,27 @@ checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "fluent-uri"
|
||||
version = "0.3.2"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1918b65d96df47d3591bed19c5cca17e3fa5d0707318e4b5ef2eae01764df7e5"
|
||||
checksum = "bc74ac4d8359ae70623506d512209619e5cf8f347124910440dbc221714b328e"
|
||||
dependencies = [
|
||||
"borrow-or-share",
|
||||
"ref-cast",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.2"
|
||||
@@ -254,26 +297,25 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.31"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.31"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.31"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
@@ -299,11 +341,27 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"r-efi 5.3.0",
|
||||
"wasip2",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"r-efi 6.0.0",
|
||||
"rand_core",
|
||||
"wasip2",
|
||||
"wasip3",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.18"
|
||||
@@ -316,11 +374,31 @@ dependencies = [
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash 0.1.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
@@ -427,6 +505,12 @@ dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.1.0"
|
||||
@@ -455,56 +539,59 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"hashbrown 0.16.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.11.0"
|
||||
version = "2.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
||||
checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.17"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.85"
|
||||
version = "0.3.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
|
||||
checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"futures-util",
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonschema"
|
||||
version = "0.30.0"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1b46a0365a611fbf1d2143104dcf910aada96fafd295bab16c60b802bf6fa1d"
|
||||
checksum = "6f29616f6e19415398eb186964fb7cbbeef572c79bede3622a8277667924bbe3"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"base64",
|
||||
"bytecount",
|
||||
"data-encoding",
|
||||
"email_address",
|
||||
"fancy-regex",
|
||||
"fraction",
|
||||
"getrandom 0.3.4",
|
||||
"idna",
|
||||
"itoa",
|
||||
"num-cmp",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"referencing",
|
||||
"regex",
|
||||
"regex-syntax",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"unicode-general-category",
|
||||
"uuid-simd",
|
||||
]
|
||||
|
||||
@@ -515,10 +602,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.180"
|
||||
name = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
||||
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.183"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
@@ -548,10 +641,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
name = "lru"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "minicov"
|
||||
@@ -663,9 +762,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
@@ -728,15 +827,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
name = "postcard"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
|
||||
dependencies = [
|
||||
"cobs",
|
||||
"embedded-io 0.4.0",
|
||||
"embedded-io 0.6.1",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
@@ -748,12 +853,13 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -767,9 +873,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.44"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -781,33 +887,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.2"
|
||||
name = "r-efi"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
name = "rand"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"chacha20",
|
||||
"getrandom 0.4.2",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.5"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
]
|
||||
checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
@@ -840,13 +940,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "referencing"
|
||||
version = "0.30.0"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8eff4fa778b5c2a57e85c5f2fe3a709c52f0e60d23146e2151cbef5893f420e"
|
||||
checksum = "b8a618c14f8ba29d8193bb55e2bf13e4fb2b1115313ecb7ae94b43100c7ac7d5"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"fluent-uri",
|
||||
"once_cell",
|
||||
"getrandom 0.3.4",
|
||||
"hashbrown 0.16.1",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"serde_json",
|
||||
@@ -854,9 +955,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.2"
|
||||
version = "1.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
||||
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -866,9 +967,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.13"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
||||
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -877,16 +978,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.8"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||
|
||||
[[package]]
|
||||
name = "regorus"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"data-encoding",
|
||||
@@ -895,9 +995,12 @@ dependencies = [
|
||||
"ipnet",
|
||||
"jsonschema",
|
||||
"lazy_static",
|
||||
"lru",
|
||||
"msvc_spectre_libs",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"parking_lot",
|
||||
"postcard",
|
||||
"rand",
|
||||
"regex",
|
||||
"semver",
|
||||
@@ -912,12 +1015,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regorusjs"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
dependencies = [
|
||||
"getrandom 0.2.17",
|
||||
"getrandom 0.3.4",
|
||||
"getrandom 0.4.2",
|
||||
"regorus",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"serde_json",
|
||||
"uuid",
|
||||
"wasm-bindgen",
|
||||
@@ -932,9 +1037,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.22"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984"
|
||||
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
@@ -967,6 +1072,17 @@ dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-wasm-bindgen"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
@@ -1027,9 +1143,9 @@ checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.11"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
|
||||
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
@@ -1039,9 +1155,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
@@ -1051,9 +1167,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.114"
|
||||
version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
|
||||
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1102,10 +1218,22 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
name = "unicode-general-category"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
checksum = "0b993bddc193ae5bd0d623b49ec06ac3e9312875fdae725a975c51db1cc1677f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
@@ -1113,12 +1241,6 @@ version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
||||
|
||||
[[package]]
|
||||
name = "unty"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.8"
|
||||
@@ -1139,11 +1261,11 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.20.0"
|
||||
version = "1.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f"
|
||||
checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"getrandom 0.4.2",
|
||||
"js-sys",
|
||||
"rand",
|
||||
"wasm-bindgen",
|
||||
@@ -1156,7 +1278,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b082222b4f6619906941c17eb2297fff4c2fb96cb60164170522942a200bd8"
|
||||
dependencies = [
|
||||
"outref",
|
||||
"uuid",
|
||||
"vsimd",
|
||||
]
|
||||
|
||||
@@ -1198,10 +1319,19 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.108"
|
||||
name = "wasip3"
|
||||
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
|
||||
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
@@ -1212,23 +1342,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.58"
|
||||
version = "0.4.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f"
|
||||
checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"futures-util",
|
||||
"js-sys",
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.108"
|
||||
version = "0.2.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
|
||||
checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -1236,9 +1362,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.108"
|
||||
version = "0.2.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
|
||||
checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
@@ -1249,18 +1375,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.108"
|
||||
version = "0.2.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
|
||||
checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test"
|
||||
version = "0.3.58"
|
||||
version = "0.3.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45649196a53b0b7a15101d845d44d2dda7374fc1b5b5e2bbf58b7577ff4b346d"
|
||||
checksum = "941c102b3f0c15b6d72a53205e09e6646aafcf2991e18412cc331dbac1806bc0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"cast",
|
||||
@@ -1280,9 +1406,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test-macro"
|
||||
version = "0.3.58"
|
||||
version = "0.3.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f579cdd0123ac74b94e1a4a72bd963cf30ebac343f2df347da0b8df24cdebed2"
|
||||
checksum = "a26bd6570f39bb1440fd8f01b63461faaf2a3f6078a508e4e54efa99363108d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1291,18 +1417,42 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test-shared"
|
||||
version = "0.2.108"
|
||||
version = "0.2.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8145dd1593bf0fb137dbfa85b8be79ec560a447298955877804640e40c2d6ea"
|
||||
checksum = "1c29582b14d5bf030b02fa232b9b57faf2afc322d2c61964dd80bad02bf76207"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.85"
|
||||
name = "wasm-encoder"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598"
|
||||
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"leb128fmt",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1387,6 +1537,88 @@ name = "wit-bindgen"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||
dependencies = [
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-macro"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-encoder",
|
||||
"wasm-metadata",
|
||||
"wasmparser",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"unicode-xid",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
@@ -1419,18 +1651,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.36"
|
||||
version = "0.8.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dafd85c832c1b68bbb4ec0c72c7f6f4fc5179627d2bc7c26b30e4c0cc11e76cc"
|
||||
checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.36"
|
||||
version = "0.8.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cb7e4e8436d9db52fbd6625dbf2f45243ab84994a72882ec8227b99e72b439a"
|
||||
checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1493,6 +1725,6 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.17"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
[package]
|
||||
name = "regorusjs"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/microsoft/regorus/bindings/wasm"
|
||||
description = "WASM bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
|
||||
license = "MIT AND Apache-2.0 AND BSD-3-Clause"
|
||||
keywords = ["interpreter", "opa", "policy-as-code", "rego"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -31,9 +32,11 @@ default = [
|
||||
"regorus/time",
|
||||
"regorus/uuid",
|
||||
"regorus/urlquery",
|
||||
"regorus/yaml"
|
||||
"regorus/yaml",
|
||||
"cache"
|
||||
]
|
||||
ast = ["regorus/ast"]
|
||||
cache = ["regorus/cache"]
|
||||
coverage = ["regorus/coverage"]
|
||||
|
||||
[dependencies]
|
||||
@@ -41,15 +44,18 @@ regorus = { path = "../..", default-features = false, features = ["arc", "rvm"]
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_json = "1.0.140"
|
||||
wasm-bindgen = "0.2.100"
|
||||
serde-wasm-bindgen = "0.6"
|
||||
# Specify uuid as a mandatory dependency so as to enable `js` feature which is now required
|
||||
# when targeting wasm32-unknown-unknown.
|
||||
uuid = { version = "1.15.1", default-features = false, features = ["v4", "fast-rng", "js"]}
|
||||
# Enable wasm_js. See https://docs.rs/getrandom/latest/getrandom/#webassembly-support
|
||||
getrandom_for_jsonschema = { package = "getrandom", version = "0.2.15", features = ["std", "js"] }
|
||||
getrandom = { version = "0.3.1", features = ["std", "wasm_js"] }
|
||||
uuid = { version = "1.22.0", default-features = false, features = ["v4", "fast-rng", "js"]}
|
||||
# Configure getrandom for WebAssembly: 0.2 uses the `js` feature, while 0.3+ use `wasm_js`.
|
||||
# See https://docs.rs/getrandom/latest/getrandom/#webassembly-support
|
||||
getrandom02 = { package = "getrandom", version = "0.2.15", features = ["std", "js"] }
|
||||
getrandom03 = { package = "getrandom", version = "0.3.1", features = ["std", "wasm_js"] }
|
||||
getrandom = { version = "0.4.2", features = ["wasm_js"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.40"
|
||||
wasm-bindgen-test = "0.3.67"
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(wasm_bindgen_unstable_test_coverage)'] }
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use core::num::{NonZeroU32, NonZeroUsize};
|
||||
use regorus::languages::rego::compiler::Compiler;
|
||||
use regorus::rvm::program::{
|
||||
generate_assembly_listing, generate_tabular_assembly_listing, AssemblyListingConfig,
|
||||
@@ -26,6 +27,42 @@ struct ModuleSpec {
|
||||
content: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct PolicyLengthSpec {
|
||||
max_col: u32,
|
||||
max_file_bytes: usize,
|
||||
max_lines: usize,
|
||||
}
|
||||
|
||||
#[cfg(feature = "cache")]
|
||||
#[derive(Deserialize)]
|
||||
struct CacheConfigSpec {
|
||||
regex: usize,
|
||||
glob: usize,
|
||||
}
|
||||
|
||||
/// Configure the global pattern caches used by regex and glob builtins.
|
||||
///
|
||||
/// Accepts a JS object: `{ regex, glob }`.
|
||||
#[cfg(feature = "cache")]
|
||||
#[wasm_bindgen(js_name = "setCacheConfig")]
|
||||
pub fn set_cache_config(config: JsValue) -> Result<(), JsValue> {
|
||||
let spec: CacheConfigSpec = serde_wasm_bindgen::from_value(config).map_err(error_to_jsvalue)?;
|
||||
regorus::cache::configure(regorus::cache::Config {
|
||||
regex: spec.regex,
|
||||
glob: spec.glob,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clear all entries from every pattern cache.
|
||||
#[cfg(feature = "cache")]
|
||||
#[wasm_bindgen(js_name = "clearCache")]
|
||||
pub fn clear_cache() {
|
||||
regorus::cache::clear();
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct Program {
|
||||
program: Arc<RvmProgram>,
|
||||
@@ -183,6 +220,29 @@ impl Engine {
|
||||
self.engine.set_gather_prints(b)
|
||||
}
|
||||
|
||||
/// Set the policy length limits used when loading policies.
|
||||
///
|
||||
/// Accepts a JS object: `{ maxCol, maxFileBytes, maxLines }`.
|
||||
pub fn setPolicyLengthConfig(&mut self, config: JsValue) -> Result<(), JsValue> {
|
||||
let spec: PolicyLengthSpec =
|
||||
serde_wasm_bindgen::from_value(config).map_err(error_to_jsvalue)?;
|
||||
self.engine
|
||||
.set_policy_length_config(regorus::PolicyLengthConfig {
|
||||
max_col: NonZeroU32::new(spec.max_col)
|
||||
.ok_or_else(|| JsValue::from_str("maxCol must be non-zero"))?,
|
||||
max_file_bytes: NonZeroUsize::new(spec.max_file_bytes)
|
||||
.ok_or_else(|| JsValue::from_str("maxFileBytes must be non-zero"))?,
|
||||
max_lines: NonZeroUsize::new(spec.max_lines)
|
||||
.ok_or_else(|| JsValue::from_str("maxLines must be non-zero"))?,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clear the policy length configuration, reverting to defaults.
|
||||
pub fn clearPolicyLengthConfig(&mut self) {
|
||||
self.engine.clear_policy_length_config();
|
||||
}
|
||||
|
||||
/// Take the gathered output of print statements.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.take_prints
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user