From 40de868df729a1b1137b60b7565131662e3ee41d Mon Sep 17 00:00:00 2001 From: Anand Krishnamoorthi Date: Mon, 20 May 2024 17:37:35 -0700 Subject: [PATCH] Fix bindings and add CI tests Also update version numbers of binding Rust projects to match Regorus Signed-off-by: Anand Krishnamoorthi --- .github/workflows/test-c-cpp.yml | 43 ++++++++++++++++++ .github/workflows/test-python.yml | 36 ++++++++++++++++ .github/workflows/test-wasm.yml | 30 +++++++++++++ bindings/c-nostd/CMakeLists.txt | 6 +-- bindings/c-nostd/main.c | 18 ++++---- bindings/c/main.c | 3 ++ bindings/cpp/CMakeLists.txt | 10 ++++- bindings/cpp/main.cpp | 1 + bindings/ffi/Cargo.toml | 2 +- bindings/ffi/src/lib.rs | 44 +++++++++++++------ bindings/java/Cargo.toml | 2 +- bindings/python/Cargo.toml | 2 +- bindings/python/src/lib.rs | 60 ++++++++++++++------------ bindings/python/test.py | 11 +++-- bindings/ruby/ext/regorusrb/Cargo.toml | 2 +- bindings/wasm/Cargo.toml | 2 +- bindings/wasm/src/lib.rs | 2 +- bindings/wasm/test.js | 5 ++- tests/aci/api.rego | 34 +++++++-------- 19 files changed, 232 insertions(+), 81 deletions(-) create mode 100644 .github/workflows/test-c-cpp.yml create mode 100644 .github/workflows/test-python.yml create mode 100644 .github/workflows/test-wasm.yml diff --git a/.github/workflows/test-c-cpp.yml b/.github/workflows/test-c-cpp.yml new file mode 100644 index 00000000..36f8cc88 --- /dev/null +++ b/.github/workflows/test-c-cpp.yml @@ -0,0 +1,43 @@ +name: test-c-cpp + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + setup: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup gcc, g++, cmake, ninja + run: sudo apt update && sudo apt install -y gcc g++ cmake ninja-build + + - name: Test c binding + run: | + mkdir bindings/c/build + cd bindings/c/build + cmake -G Ninja .. + ninja + ./regorus_test + + - name: Test c-nostd binding + run: | + mkdir bindings/c-nostd/build + cd bindings/c-nostd/build + cmake -G Ninja .. + ninja + ./regorus_test + + - name: Test cpp binding + run: | + mkdir bindings/cpp/build + cd bindings/cpp/build + cmake -G Ninja .. + ninja + ./regorus_test diff --git a/.github/workflows/test-python.yml b/.github/workflows/test-python.yml new file mode 100644 index 00000000..e723e5e1 --- /dev/null +++ b/.github/workflows/test-python.yml @@ -0,0 +1,36 @@ +name: test-python + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + PYTHON_VERSION: "3.10" + +jobs: + setup: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v4 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + args: --release --out dist --find-interpreter --manifest-path bindings/python/Cargo.toml + sccache: 'true' + manylinux: auto + + - name: Test wheel + run: | + pip3 install dist/regorus-*.whl + cd bindings/python + python3 test.py diff --git a/.github/workflows/test-wasm.yml b/.github/workflows/test-wasm.yml new file mode 100644 index 00000000..81d16bc3 --- /dev/null +++ b/.github/workflows/test-wasm.yml @@ -0,0 +1,30 @@ +name: test-wasm + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + setup: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 18 + + - name: Install wasmlpack + run: cargo install wasm-pack + + - name: Test wasm binding + run: | + cd bindings/wasm + wasm-pack build --target nodejs --release + node test.js diff --git a/bindings/c-nostd/CMakeLists.txt b/bindings/c-nostd/CMakeLists.txt index 1d554bef..3cc4e93b 100644 --- a/bindings/c-nostd/CMakeLists.txt +++ b/bindings/c-nostd/CMakeLists.txt @@ -35,7 +35,7 @@ corrosion_import_crate( CRATE_TYPES staticlib FLAGS --crate-type=staticlib ) -add_executable(regorus_test_nostd main.c) +add_executable(regorus_test main.c) # Add path to /bindings/ffi -target_include_directories(regorus_test_nostd PRIVATE "../ffi") -target_link_libraries(regorus_test_nostd regorus-ffi) +target_include_directories(regorus_test PRIVATE "../ffi") +target_link_libraries(regorus_test regorus-ffi) diff --git a/bindings/c-nostd/main.c b/bindings/c-nostd/main.c index 0ac88046..9cd54e71 100644 --- a/bindings/c-nostd/main.c +++ b/bindings/c-nostd/main.c @@ -7,13 +7,14 @@ char* file_to_string(const char* file) { char * buffer = 0; long length; FILE * f = fopen (file, "rb"); - + if (f) { fseek (f, 0, SEEK_END); length = ftell (f); fseek (f, 0, SEEK_SET); - buffer = malloc (length); + buffer = malloc (length + 1); + buffer[length] = '\0'; if (buffer) { fread (buffer, 1, length, f); @@ -26,8 +27,6 @@ char* file_to_string(const char* file) { // If regorus is built with custom-allocator, then provide implementation. uint8_t* regorus_aligned_alloc(size_t alignment, size_t size) { - printf("%ld %ld\n", size, alignment); - fflush(stdout); return aligned_alloc(alignment, size); } @@ -47,18 +46,21 @@ int main() { free(buffer); if (r.status != RegorusStatusOk) goto error; + printf("Loaded policy %s\n", r.output); regorus_result_drop(r); r = regorus_engine_add_policy(engine, "api.rego", (buffer = file_to_string("../../../tests/aci/api.rego"))); free(buffer); if (r.status != RegorusStatusOk) goto error; + printf("Loaded policy %s\n", r.output); regorus_result_drop(r); - + r = regorus_engine_add_policy(engine, "policy.rego", (buffer = file_to_string("../../../tests/aci/policy.rego"))); free(buffer); if (r.status != RegorusStatusOk) goto error; + printf("Loaded policy %s\n", r.output); regorus_result_drop(r); // Add data @@ -83,14 +85,14 @@ int main() { // Print output printf("%s", r.output); regorus_result_drop(r); - - + + // Free the engine. regorus_engine_drop(engine); return 0; error: printf("%s", r.error_message); - + return 1; } diff --git a/bindings/c/main.c b/bindings/c/main.c index 53197989..17651ba6 100644 --- a/bindings/c/main.c +++ b/bindings/c/main.c @@ -10,16 +10,19 @@ int main() { r = regorus_engine_add_policy_from_file(engine, "../../../tests/aci/framework.rego"); if (r.status != RegorusStatusOk) goto error; + printf("Loaded policy %s\n", r.output); regorus_result_drop(r); r = regorus_engine_add_policy_from_file(engine, "../../../tests/aci/api.rego"); if (r.status != RegorusStatusOk) goto error; + printf("Loaded policy %s\n", r.output); regorus_result_drop(r); r = regorus_engine_add_policy_from_file(engine, "../../../tests/aci/policy.rego"); if (r.status != RegorusStatusOk) goto error; + printf("Loaded policy %s\n", r.output); regorus_result_drop(r); // Add data diff --git a/bindings/cpp/CMakeLists.txt b/bindings/cpp/CMakeLists.txt index 18ce8fb8..a510438b 100644 --- a/bindings/cpp/CMakeLists.txt +++ b/bindings/cpp/CMakeLists.txt @@ -19,8 +19,14 @@ corrosion_import_crate( MANIFEST_PATH "../ffi/Cargo.toml" # Always build regorus in Release mode. PROFILE "release" - # Only build the "regorusc" crate. - CRATES "regorus-ffi") + # Only build the "regorus-ffi" crate. + CRATES "regorus-ffi" + + # Select specific features in regorus. + FEATURES "regorus/semver" + + # Link statically + CRATE_TYPES "staticlib") add_executable(regorus_test main.cpp) # Add path to /bindings/ffi diff --git a/bindings/cpp/main.cpp b/bindings/cpp/main.cpp index 7706e1bf..08145249 100644 --- a/bindings/cpp/main.cpp +++ b/bindings/cpp/main.cpp @@ -89,6 +89,7 @@ int main() { std::cerr< *mut c_char { } } -fn from_c_str(s: *const c_char) -> Result { +fn from_c_str(name: &str, s: *const c_char) -> Result { if s.is_null() { bail!("null pointer"); } unsafe { CStr::from_ptr(s) .to_str() - .map_err(|_| anyhow!("`path`: invalid utf8")) + .map_err(|e| anyhow!("`{name}`: invalid utf8.\n{e}")) .map(|s| s.to_string()) } } @@ -70,6 +70,21 @@ fn to_regorus_result(r: Result<()>) -> RegorusResult { } } +fn to_regorus_string_result(r: Result) -> RegorusResult { + match r { + Ok(s) => RegorusResult { + status: RegorusStatus::RegorusStatusOk, + output: to_c_str(s), + error_message: std::ptr::null_mut(), + }, + Err(e) => RegorusResult { + status: RegorusStatus::RegorusStatusError, + output: std::ptr::null_mut(), + error_message: to_c_str(format!("{e}")), + }, + } +} + /// Wrapper for `regorus::Engine`. #[derive(Clone)] pub struct RegorusEngine { @@ -81,10 +96,13 @@ pub struct RegorusEngine { /// `output` and `error_message` strings are not valid after drop. #[no_mangle] pub extern "C" fn regorus_result_drop(r: RegorusResult) { - if !r.error_message.is_null() { - unsafe { + unsafe { + if !r.error_message.is_null() { let _ = CString::from_raw(r.error_message); } + if !r.output.is_null() { + let _ = CString::from_raw(r.output); + } } } @@ -133,10 +151,10 @@ pub extern "C" fn regorus_engine_add_policy( path: *const c_char, rego: *const c_char, ) -> RegorusResult { - to_regorus_result(|| -> Result<()> { + to_regorus_string_result(|| -> Result { to_ref(&engine)? .engine - .add_policy(from_c_str(path)?, from_c_str(rego)?) + .add_policy(from_c_str("path", path)?, from_c_str("rego", rego)?) }()) } @@ -146,10 +164,10 @@ pub extern "C" fn regorus_engine_add_policy_from_file( engine: *mut RegorusEngine, path: *const c_char, ) -> RegorusResult { - to_regorus_result(|| -> Result<()> { + to_regorus_string_result(|| -> Result { to_ref(&engine)? .engine - .add_policy_from_file(from_c_str(path)?) + .add_policy_from_file(from_c_str("path", path)?) }()) } @@ -165,7 +183,7 @@ pub extern "C" fn regorus_engine_add_data_json( to_regorus_result(|| -> Result<()> { to_ref(&engine)? .engine - .add_data(regorus::Value::from_json_str(&from_c_str(data)?)?) + .add_data(regorus::Value::from_json_str(&from_c_str("data", data)?)?) }()) } @@ -178,7 +196,7 @@ pub extern "C" fn regorus_engine_add_data_from_json_file( to_regorus_result(|| -> Result<()> { to_ref(&engine)? .engine - .add_data(regorus::Value::from_json_file(&from_c_str(path)?)?) + .add_data(regorus::Value::from_json_file(&from_c_str("path", path)?)?) }()) } @@ -205,7 +223,7 @@ pub extern "C" fn regorus_engine_set_input_json( to_regorus_result(|| -> Result<()> { to_ref(&engine)? .engine - .set_input(regorus::Value::from_json_str(&from_c_str(input)?)?); + .set_input(regorus::Value::from_json_str(&from_c_str("input", input)?)?); Ok(()) }()) } @@ -219,7 +237,7 @@ pub extern "C" fn regorus_engine_set_input_from_json_file( to_regorus_result(|| -> Result<()> { to_ref(&engine)? .engine - .set_input(regorus::Value::from_json_file(&from_c_str(path)?)?); + .set_input(regorus::Value::from_json_file(&from_c_str("path", path)?)?); Ok(()) }()) } @@ -236,7 +254,7 @@ pub extern "C" fn regorus_engine_eval_query( let output = || -> Result { let results = to_ref(&engine)? .engine - .eval_query(from_c_str(query)?, false)?; + .eval_query(from_c_str("query", query)?, false)?; Ok(serde_json::to_string_pretty(&results)?) }(); match output { diff --git a/bindings/java/Cargo.toml b/bindings/java/Cargo.toml index 57471aa4..33b1840f 100644 --- a/bindings/java/Cargo.toml +++ b/bindings/java/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "regorus-java" -version = "0.1.0" +version = "0.1.5" edition = "2021" repository = "https://github.com/microsoft/regorus/bindings/java" description = "Java bindings for Regorus - a fast, lightweight Rego interpreter written in Rust" diff --git a/bindings/python/Cargo.toml b/bindings/python/Cargo.toml index e47e29ee..d62f688d 100644 --- a/bindings/python/Cargo.toml +++ b/bindings/python/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "regoruspy" -version = "0.1.0" +version = "0.1.5" edition = "2021" repository = "https://github.com/microsoft/regorus/bindings/python" description = "Python bindings for Regorus - a fast, lightweight Rego interpreter written in Rust" diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index ee1dc93d..4502cddc 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -33,12 +33,12 @@ impl Clone for Engine { } } -fn from<'source>(ob: &'source PyAny) -> Result { +fn from<'source>(ob: &Bound<'_, PyAny>) -> Result { // dicts Ok(if let Ok(dict) = ob.downcast::() { let mut map = BTreeMap::new(); for (k, v) in dict { - map.insert(from(k)?, from(v)?); + map.insert(from(&k)?, from(&v)?); } map.into() } @@ -46,7 +46,7 @@ fn from<'source>(ob: &'source PyAny) -> Result { else if let Ok(pset) = ob.downcast::() { let mut set = BTreeSet::new(); for v in pset { - set.insert(from(v)?); + set.insert(from(&v)?); } set.into() } @@ -55,7 +55,7 @@ fn from<'source>(ob: &'source PyAny) -> Result { // let mut set = BTreeSet::new(); for v in pfset { - set.insert(from(v)?); + set.insert(from(&v)?); } set.into() } @@ -63,30 +63,30 @@ fn from<'source>(ob: &'source PyAny) -> Result { else if let Ok(plist) = ob.downcast::() { let mut array = Vec::new(); for v in plist { - array.push(from(v)?); + array.push(from(&v)?); } array.into() } else if let Ok(ptuple) = ob.downcast::() { let mut array = Vec::new(); for v in ptuple { - array.push(from(v)?); + array.push(from(&v)?); } array.into() } // String - else if let Ok(s) = String::extract(ob) { + else if let Ok(s) = ob.extract::() { s.into() } // Numeric - else if let Ok(v) = i64::extract(ob) { + else if let Ok(v) = ob.extract::() { v.into() - } else if let Ok(v) = u64::extract(ob) { + } else if let Ok(v) = ob.extract::() { v.into() - } else if let Ok(v) = f64::extract(ob) { + } else if let Ok(v) = ob.extract::() { v.into() } // Boolean - else if let Ok(b) = bool::extract(ob) { + else if let Ok(b) = ob.extract::() { b.into() } // None @@ -97,7 +97,7 @@ fn from<'source>(ob: &'source PyAny) -> Result { else if let Ok(pseq) = ob.downcast::() { let mut array = Vec::new(); for i in 0..pseq.len()? { - array.push(from(pseq.get_item(i)?)?); + array.push(from(&pseq.get_item(i)?)?); } array.into() } @@ -109,7 +109,7 @@ fn from<'source>(ob: &'source PyAny) -> Result { for i in 0..keys.len()? { let key = keys.get_item(i)?; let value = values.get_item(i)?; - map.insert(from(key)?, from(value)?); + map.insert(from(&key)?, from(&value)?); } map.into() } else { @@ -140,7 +140,7 @@ fn to(mut v: Value, py: Python<'_>) -> Result { } Value::Array(_) => { - let list = PyList::empty(py); + let list = PyList::empty_bound(py); for v in std::mem::replace(v.as_array_mut()?, Vec::new()) { list.append(to(v, py)?)?; } @@ -148,7 +148,7 @@ fn to(mut v: Value, py: Python<'_>) -> Result { } Value::Set(_) => { - let set = PySet::empty(py)?; + let set = PySet::empty_bound(py)?; for v in std::mem::replace(v.as_set_mut()?, BTreeSet::new()) { set.add(to(v, py)?)?; } @@ -156,7 +156,7 @@ fn to(mut v: Value, py: Python<'_>) -> Result { } Value::Object(_) => { - let dict = PyDict::new(py); + let dict = PyDict::new_bound(py); for (k, v) in std::mem::replace(v.as_object_mut()?, BTreeMap::new()) { dict.set_item(to(k, py)?, to(v, py)?)?; } @@ -181,7 +181,7 @@ impl Engine { /// /// * `path`: A filename to be associated with the policy. /// * `rego`: Rego policy. - pub fn add_policy(&mut self, path: String, rego: String) -> Result<()> { + pub fn add_policy(&mut self, path: String, rego: String) -> Result { self.engine.add_policy(path, rego) } @@ -190,15 +190,21 @@ impl Engine { /// The policy is parsed into AST. /// /// * `path`: Path to the policy file. - pub fn add_policy_from_file(&mut self, path: String) -> Result<()> { + pub fn add_policy_from_file(&mut self, path: String) -> Result { self.engine.add_policy_from_file(path) } + /// Get the list of packages defined by loaded policies. + /// + pub fn get_packages(&mut self) -> Result> { + self.engine.get_packages() + } + /// Add policy data. /// /// * `data`: Rego value. A Rego value is a number, bool, string, None /// or a list/set/map whose items themselves are Rego values. - pub fn add_data(&mut self, data: &PyAny) -> Result<()> { + pub fn add_data(&mut self, data: &Bound<'_, PyAny>) -> Result<()> { let data = from(data)?; self.engine.add_data(data) } @@ -229,7 +235,7 @@ impl Engine { /// /// * `input`: Rego value. A Rego value is a number, bool, string, None /// or a list/set/map whose items themselves are Rego values. - pub fn set_input(&mut self, input: &PyAny) -> Result<()> { + pub fn set_input(&mut self, input: &Bound<'_, PyAny>) -> Result<()> { let input = from(input)?; self.engine.set_input(input); Ok(()) @@ -259,17 +265,17 @@ impl Engine { pub fn eval_query(&mut self, query: String, py: Python<'_>) -> Result { let results = self.engine.eval_query(query, false)?; - let rlist = PyList::empty(py); + let rlist = PyList::empty_bound(py); for result in results.result.into_iter() { - let rdict = PyDict::new(py); + let rdict = PyDict::new_bound(py); - let elist = PyList::empty(py); + let elist = PyList::empty_bound(py); for expr in result.expressions.into_iter() { - let edict = PyDict::new(py); + let edict = PyDict::new_bound(py); edict.set_item("value".to_object(py), to(expr.value, py)?)?; edict.set_item("text".to_object(py), expr.text.as_ref().to_object(py))?; - let ldict = PyDict::new(py); + let ldict = PyDict::new_bound(py); ldict.set_item("row".to_object(py), expr.location.row.to_object(py))?; ldict.set_item("col".to_object(py), expr.location.col.to_object(py))?; @@ -281,7 +287,7 @@ impl Engine { rdict.set_item("bindings".to_object(py), to(result.bindings, py)?)?; rlist.append(rdict)?; } - let dict = PyDict::new(py); + let dict = PyDict::new_bound(py); dict.set_item("result".to_object(py), rlist)?; Ok(dict.into()) } @@ -296,6 +302,6 @@ impl Engine { } #[pymodule] -pub fn regorus(_py: Python<'_>, m: &PyModule) -> PyResult<()> { +pub fn regorus(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::() } diff --git a/bindings/python/test.py b/bindings/python/test.py index 9cf0d592..014a77fc 100644 --- a/bindings/python/test.py +++ b/bindings/python/test.py @@ -7,9 +7,14 @@ engine = regorus.Engine() # Load policies -engine.add_policy_from_file('../../tests/aci/framework.rego') -engine.add_policy_from_file('../../tests/aci/api.rego') -engine.add_policy_from_file('../../tests/aci/policy.rego') +pkg = engine.add_policy_from_file('../../tests/aci/framework.rego') +print(' Loaded Policy %s' % pkg) + +pkg = engine.add_policy_from_file('../../tests/aci/api.rego') +print(' Loaded Policy %s' % pkg) + +pkg = engine.add_policy_from_file('../../tests/aci/policy.rego') +print(' Loaded Policy %s' % pkg) # Add policy data data = { diff --git a/bindings/ruby/ext/regorusrb/Cargo.toml b/bindings/ruby/ext/regorusrb/Cargo.toml index 180b5c0e..cf6574b6 100644 --- a/bindings/ruby/ext/regorusrb/Cargo.toml +++ b/bindings/ruby/ext/regorusrb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "regorusrb" -version = "0.1.0" +version = "0.1.5" edition = "2021" description = "Ruby bindings for Regorus - a fast, lightweight Rego interpreter written in Rust" publish = false diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index 03ad8358..ba588e03 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "regorusjs" -version = "0.1.0" +version = "0.1.5" edition = "2021" repository = "https://github.com/microsoft/regorus/bindings/wasm" description = "WASM bindings for Regorus - a fast, lightweight Rego interpreter written in Rust" diff --git a/bindings/wasm/src/lib.rs b/bindings/wasm/src/lib.rs index 08435f57..397bb74f 100644 --- a/bindings/wasm/src/lib.rs +++ b/bindings/wasm/src/lib.rs @@ -50,7 +50,7 @@ impl Engine { /// /// * `path`: A filename to be associated with the policy. /// * `rego`: Rego policy. - pub fn add_policy(&mut self, path: String, rego: String) -> Result<(), JsValue> { + pub fn add_policy(&mut self, path: String, rego: String) -> Result { self.engine.add_policy(path, rego).map_err(error_to_jsvalue) } diff --git a/bindings/wasm/test.js b/bindings/wasm/test.js index bd2d7ddc..a0304bc2 100644 --- a/bindings/wasm/test.js +++ b/bindings/wasm/test.js @@ -7,7 +7,7 @@ var regorus = require('./pkg/regorusjs') var engine = new regorus.Engine(); // Add Rego policy. -engine.add_policy( +var pkg = engine.add_policy( // Associate this file name with policy 'hello.rego', @@ -18,6 +18,7 @@ engine.add_policy( # Join messages message = concat(", ", [input.message, data.message]) `) +console.log("Loaded policy " + pkg) // Set policy data engine.add_data_json(` @@ -34,7 +35,7 @@ engine.set_input_json(` `) // Eval query -results = engine.eval_query('data.test.message') +var results = engine.eval_query('data.test.message') // Display console.log(results) diff --git a/tests/aci/api.rego b/tests/aci/api.rego index fa301444..044a62ef 100644 --- a/tests/aci/api.rego +++ b/tests/aci/api.rego @@ -6,21 +6,21 @@ package api version := "0.10.0" enforcement_points := { - "mount_device": {"introducedVersion": "0.1.0", "default_results": {"allowed": false}}, - "mount_overlay": {"introducedVersion": "0.1.0", "default_results": {"allowed": false}}, - "create_container": {"introducedVersion": "0.1.0", "default_results": {"allowed": false, "env_list": null, "allow_stdio_access": false}}, - "unmount_device": {"introducedVersion": "0.2.0", "default_results": {"allowed": true}}, - "unmount_overlay": {"introducedVersion": "0.6.0", "default_results": {"allowed": true}}, - "exec_in_container": {"introducedVersion": "0.2.0", "default_results": {"allowed": true, "env_list": null}}, - "exec_external": {"introducedVersion": "0.3.0", "default_results": {"allowed": true, "env_list": null, "allow_stdio_access": false}}, - "shutdown_container": {"introducedVersion": "0.4.0", "default_results": {"allowed": true}}, - "signal_container_process": {"introducedVersion": "0.5.0", "default_results": {"allowed": true}}, - "plan9_mount": {"introducedVersion": "0.6.0", "default_results": {"allowed": true}}, - "plan9_unmount": {"introducedVersion": "0.6.0", "default_results": {"allowed": true}}, - "get_properties": {"introducedVersion": "0.7.0", "default_results": {"allowed": true}}, - "dump_stacks": {"introducedVersion": "0.7.0", "default_results": {"allowed": true}}, - "runtime_logging": {"introducedVersion": "0.8.0", "default_results": {"allowed": true}}, - "load_fragment": {"introducedVersion": "0.9.0", "default_results": {"allowed": false, "add_module": false}}, - "scratch_mount": {"introducedVersion": "0.10.0", "default_results": {"allowed": true}}, - "scratch_unmount": {"introducedVersion": "0.10.0", "default_results": {"allowed": true}}, + "mount_device": {"introducedVersion": "0.1.0", "default_results": {"allowed": false}}, + "mount_overlay": {"introducedVersion": "0.1.0", "default_results": {"allowed": false}}, + "create_container": {"introducedVersion": "0.1.0", "default_results": {"allowed": false, "env_list": null, "allow_stdio_access": false}}, + "unmount_device": {"introducedVersion": "0.2.0", "default_results": {"allowed": true}}, + "unmount_overlay": {"introducedVersion": "0.6.0", "default_results": {"allowed": true}}, + "exec_in_container": {"introducedVersion": "0.2.0", "default_results": {"allowed": true, "env_list": null}}, + "exec_external": {"introducedVersion": "0.3.0", "default_results": {"allowed": true, "env_list": null, "allow_stdio_access": false}}, + "shutdown_container": {"introducedVersion": "0.4.0", "default_results": {"allowed": true}}, + "signal_container_process": {"introducedVersion": "0.5.0", "default_results": {"allowed": true}}, + "plan9_mount": {"introducedVersion": "0.6.0", "default_results": {"allowed": true}}, + "plan9_unmount": {"introducedVersion": "0.6.0", "default_results": {"allowed": true}}, + "get_properties": {"introducedVersion": "0.7.0", "default_results": {"allowed": true}}, + "dump_stacks": {"introducedVersion": "0.7.0", "default_results": {"allowed": true}}, + "runtime_logging": {"introducedVersion": "0.8.0", "default_results": {"allowed": true}}, + "load_fragment": {"introducedVersion": "0.9.0", "default_results": {"allowed": false, "add_module": false}}, + "scratch_mount": {"introducedVersion": "0.10.0", "default_results": {"allowed": true}}, + "scratch_unmount": {"introducedVersion": "0.10.0", "default_results": {"allowed": true}}, }