mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
OPA Conformance
- Disable default features of jsonschema to reduce binary size (#85) - graph.reachable, graph.reachable_paths builtins. - In progress walk builtin Note: graph.reachable_paths is buggy in upstream OPA and its semantics are not well defined. https://github.com/open-policy-agent/opa/issues/5871 https://github.com/open-policy-agent/opa/issues/6128 - Use correct feature for regex - Ensure that regorus complies with all features disabled. Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
84bedda1c6
commit
2292774446
@@ -14,13 +14,14 @@ crypto = ["dep:constant_time_eq", "dep:hmac", "dep:hex", "dep:md-5", "dep:sha1",
|
||||
deprecated = []
|
||||
hex = ["dep:data-encoding"]
|
||||
glob = ["dep:wax"]
|
||||
graph = []
|
||||
jsonschema = ["dep:jsonschema"]
|
||||
regex = ["dep:regex"]
|
||||
semver = ["dep:semver"]
|
||||
uuid = ["dep:uuid"]
|
||||
urlquery = ["dep:url"]
|
||||
yaml = ["serde_yaml"]
|
||||
full-opa = ["base64", "base64url", "crypto", "deprecated", "glob", "hex", "jsonschema", "regex", "semver", "uuid", "urlquery", "yaml"]
|
||||
full-opa = ["base64", "base64url", "crypto", "deprecated", "glob", "graph", "hex", "jsonschema", "regex", "semver", "uuid", "urlquery", "yaml"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = {version = "1.0.66", features = ["backtrace"] }
|
||||
@@ -42,7 +43,6 @@ sha1 = {version = "0.10.6", optional = true}
|
||||
md-5 = {version = "0.10.6", optional = true}
|
||||
|
||||
data-encoding = { version = "2.4.0", optional = true }
|
||||
jsonschema = { version = "0.17.1", optional = true }
|
||||
scientific = { version = "0.5.2" }
|
||||
|
||||
regex = {version = "1.10.2", optional = true}
|
||||
@@ -50,7 +50,7 @@ semver = {version = "1.0.20", optional = true}
|
||||
wax = { version = "0.6.0", features = [], default-features = false, optional = true }
|
||||
url = { version = "2.5.0", optional = true }
|
||||
uuid = { version = "1.6.1", features = ["v4", "fast-rng"], optional = true }
|
||||
|
||||
jsonschema = { version = "0.17.1", default-features = false, optional = true }
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use crate::ast::{Expr, Ref};
|
||||
use crate::builtins::regex::regex_match;
|
||||
use crate::builtins::utils::{ensure_args_count, ensure_set};
|
||||
use crate::builtins::BuiltinFcn;
|
||||
use crate::lexer::Span;
|
||||
@@ -13,6 +12,9 @@ use std::collections::HashMap;
|
||||
use anyhow::{bail, Result};
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
#[cfg(feature = "regex")]
|
||||
use crate::builtins::regex::regex_match;
|
||||
|
||||
#[rustfmt::skip]
|
||||
lazy_static! {
|
||||
pub static ref DEPRECATED: HashMap<&'static str, BuiltinFcn> = {
|
||||
@@ -28,7 +30,7 @@ lazy_static! {
|
||||
m.insert("cast_string", (cast_string, 1));
|
||||
m.insert("set_diff", (set_diff, 2));
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
#[cfg(feature = "regex")]
|
||||
m.insert("re_match", (regex_match, 2));
|
||||
m
|
||||
};
|
||||
|
||||
184
src/builtins/graph.rs
Normal file
184
src/builtins/graph.rs
Normal file
@@ -0,0 +1,184 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use crate::ast::{Expr, Ref};
|
||||
use crate::builtins;
|
||||
use crate::builtins::utils::{ensure_args_count, ensure_object};
|
||||
use crate::lexer::Span;
|
||||
use crate::value::Value;
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
m.insert("graph.reachable", (reachable, 2));
|
||||
m.insert("graph.reachable_paths", (reachable_paths, 2));
|
||||
m.insert("walk", (walk, 1));
|
||||
}
|
||||
|
||||
fn reachable(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
|
||||
let name = "graph.reachable";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
let graph = ensure_object(name, ¶ms[0], args[0].clone())?;
|
||||
let mut worklist = vec![];
|
||||
|
||||
match &args[1] {
|
||||
Value::Array(arr) => worklist.extend(arr.iter().cloned()),
|
||||
Value::Set(set) => worklist.extend(set.iter().cloned()),
|
||||
_ if strict => bail!(params[1].span().error("initial vertices must be array/set")),
|
||||
_ => return Ok(Value::Undefined),
|
||||
}
|
||||
|
||||
let mut reachable = BTreeSet::new();
|
||||
while let Some(v) = worklist.pop() {
|
||||
if reachable.contains(&v) {
|
||||
continue;
|
||||
}
|
||||
|
||||
match graph.get(&v) {
|
||||
Some(Value::Array(arr)) => worklist.extend(arr.iter().cloned()),
|
||||
Some(Value::Set(set)) => worklist.extend(set.iter().cloned()),
|
||||
Some(_) => (),
|
||||
_ => continue,
|
||||
}
|
||||
|
||||
reachable.insert(v);
|
||||
}
|
||||
|
||||
Ok(Value::from_set(reachable))
|
||||
}
|
||||
|
||||
fn visit(
|
||||
graph: &BTreeMap<Value, Value>,
|
||||
visited: &mut BTreeSet<Value>,
|
||||
node: &Value,
|
||||
path: &mut Vec<Value>,
|
||||
paths: &mut BTreeSet<Value>,
|
||||
) -> Result<()> {
|
||||
if let Value::String(s) = node {
|
||||
if s.as_ref() == "" {
|
||||
if !path.is_empty() {
|
||||
paths.insert(Value::from_array(path.clone()));
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let neighbors = graph.get(node);
|
||||
if neighbors.is_none() {
|
||||
// Current node is not valid. Add path as is.
|
||||
if !path.is_empty() {
|
||||
paths.insert(Value::from_array(path.clone()));
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if visited.contains(node) {
|
||||
paths.insert(Value::from_array(path.clone()));
|
||||
} else {
|
||||
path.push(node.clone());
|
||||
visited.insert(node.clone());
|
||||
let n = match neighbors {
|
||||
Some(Value::Array(arr)) => {
|
||||
for n in arr.iter().rev() {
|
||||
visit(graph, visited, n, path, paths)?;
|
||||
}
|
||||
arr.len()
|
||||
}
|
||||
Some(Value::Set(set)) => {
|
||||
for n in set.iter().rev() {
|
||||
visit(graph, visited, n, path, paths)?;
|
||||
}
|
||||
set.len()
|
||||
}
|
||||
Some(&Value::Null) => 0,
|
||||
_ => bail!(format!("neighbors for node `{node}` must be array/set.")),
|
||||
};
|
||||
|
||||
if n == 0 {
|
||||
// Current node has no neighbors.
|
||||
if !path.is_empty() {
|
||||
paths.insert(Value::from_array(path.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
visited.remove(node);
|
||||
path.pop();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reachable_paths(
|
||||
span: &Span,
|
||||
params: &[Ref<Expr>],
|
||||
args: &[Value],
|
||||
strict: bool,
|
||||
) -> Result<Value> {
|
||||
let name = "graph.reachable_paths";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
|
||||
let graph = ensure_object(name, ¶ms[0], args[0].clone())?;
|
||||
let mut visited = BTreeSet::new();
|
||||
let mut path = vec![];
|
||||
let mut paths = BTreeSet::new();
|
||||
|
||||
match &args[1] {
|
||||
Value::Array(arr) => {
|
||||
for node in arr.iter() {
|
||||
visit(&graph, &mut visited, node, &mut path, &mut paths)?;
|
||||
}
|
||||
}
|
||||
Value::Set(set) => {
|
||||
for node in set.iter() {
|
||||
visit(&graph, &mut visited, node, &mut path, &mut paths)?;
|
||||
}
|
||||
}
|
||||
_ if strict => bail!(params[1].span().error("initial vertices must be array/set")),
|
||||
_ => return Ok(Value::Undefined),
|
||||
}
|
||||
|
||||
Ok(Value::from_set(paths))
|
||||
}
|
||||
|
||||
fn walk_visit(path: &mut Vec<Value>, value: &Value, paths: &mut Vec<Value>) -> Result<()> {
|
||||
{
|
||||
let path = Value::from_array(path.clone());
|
||||
paths.push(Value::from_array([path, value.clone()].into()));
|
||||
}
|
||||
match value {
|
||||
Value::Array(arr) => {
|
||||
for (idx, elem) in arr.iter().enumerate() {
|
||||
path.push(Value::from(idx));
|
||||
walk_visit(path, elem, paths)?;
|
||||
path.pop();
|
||||
}
|
||||
}
|
||||
Value::Set(set) => {
|
||||
for elem in set.iter() {
|
||||
path.push(elem.clone());
|
||||
walk_visit(path, elem, paths)?;
|
||||
path.pop();
|
||||
}
|
||||
}
|
||||
Value::Object(obj) => {
|
||||
for (key, value) in obj.iter() {
|
||||
path.push(key.clone());
|
||||
walk_visit(path, value, paths)?;
|
||||
path.pop();
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn walk(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
||||
let name = "walk";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
let mut paths = vec![];
|
||||
walk_visit(&mut vec![], &args[0], &mut paths)?;
|
||||
Ok(Value::from_array(paths))
|
||||
}
|
||||
@@ -15,6 +15,8 @@ pub mod deprecated;
|
||||
mod encoding;
|
||||
#[cfg(feature = "glob")]
|
||||
mod glob;
|
||||
#[cfg(feature = "graph")]
|
||||
mod graph;
|
||||
pub mod numbers;
|
||||
mod objects;
|
||||
#[cfg(feature = "regex")]
|
||||
@@ -64,6 +66,9 @@ lazy_static! {
|
||||
#[cfg(feature = "glob")]
|
||||
glob::register(&mut m);
|
||||
|
||||
#[cfg(feature = "graph")]
|
||||
graph::register(&mut m);
|
||||
|
||||
bitwise::register(&mut m);
|
||||
conversions::register(&mut m);
|
||||
//units::register(&mut m);
|
||||
@@ -75,7 +80,6 @@ lazy_static! {
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
crypto::register(&mut m);
|
||||
//graphs::register(&mut m);
|
||||
//graphql::register(&mut m);
|
||||
//http::register(&mut m);
|
||||
//net::register(&mut m);
|
||||
|
||||
@@ -1785,6 +1785,9 @@ impl Interpreter {
|
||||
return Ok(Some(builtin));
|
||||
}
|
||||
|
||||
// Mark as used when deprecated feature is not enabled.
|
||||
std::convert::identity((span, self.allow_deprecated));
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
|
||||
@@ -133,6 +133,8 @@ fn get_extra_arg_impl(
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
#[cfg(not(feature = "deprecated"))]
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
if (n_args as usize) + 1 == params.len() {
|
||||
|
||||
Reference in New Issue
Block a user