mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
Locked down supported values in default rule.
Only scalars, composites and comprehensions containing scalars are supported. This will be changed as OPA fixes bug with default values Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
@@ -474,6 +474,7 @@ impl<'source> Interpreter<'source> {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
Literal::NotExpr { expr, .. } => matches!(self.eval_expr(expr)?, Value::Bool(false)),
|
||||
_ => unimplemented!(),
|
||||
});
|
||||
|
||||
@@ -1347,6 +1348,108 @@ impl<'source> Interpreter<'source> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_default_value(&self, expr: &'source Expr<'source>) -> Result<()> {
|
||||
use Expr::*;
|
||||
let (kind, span) = match expr {
|
||||
// Scalars are supported
|
||||
String(_) | RawString(_) | Number(_) | True(_) | False(_) | Null(_) => return Ok(()),
|
||||
|
||||
// Uminus of number is treated as a single expression,
|
||||
UnaryExpr { expr, .. } if matches!(expr.as_ref(), Number(_)) => return Ok(()),
|
||||
|
||||
Var(span) => ("var", span),
|
||||
|
||||
// Check each item in array/set.
|
||||
Array { items, .. } | Set { items, .. } => {
|
||||
for item in items {
|
||||
self.check_default_value(item)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Check each field in object
|
||||
Object { fields, .. } => {
|
||||
for (_, key, value) in fields {
|
||||
self.check_default_value(key)?;
|
||||
self.check_default_value(value)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Check each statement in comprehensions
|
||||
ArrayCompr { term, query, .. } | SetCompr { term, query, .. } => {
|
||||
self.check_default_value(term)?;
|
||||
for stmt in &query.stmts {
|
||||
self.check_default_value_in_stmt(stmt)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
ObjectCompr {
|
||||
key, value, query, ..
|
||||
} => {
|
||||
self.check_default_value(key)?;
|
||||
self.check_default_value(value)?;
|
||||
for stmt in &query.stmts {
|
||||
self.check_default_value_in_stmt(stmt)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Call { span, .. } => ("call", span),
|
||||
UnaryExpr { span, .. } => ("unaryexpr", span),
|
||||
RefDot { span, .. } => ("ref", span),
|
||||
RefBrack { span, .. } => ("ref", span),
|
||||
BinExpr { span, .. } => ("binexpr", span),
|
||||
BoolExpr { span, .. } => ("boolexpr", span),
|
||||
ArithExpr { span, .. } => ("arithexpr", span),
|
||||
AssignExpr { span, .. } => ("assignexpr", span),
|
||||
Membership { span, .. } => ("membership", span),
|
||||
};
|
||||
|
||||
Err(span.error(format!("invalid `{kind}` in default value").as_str()))
|
||||
}
|
||||
|
||||
fn check_default_value_in_stmt(&self, stmt: &'source LiteralStmt<'source>) -> Result<()> {
|
||||
for m in &stmt.with_mods {
|
||||
self.check_default_value(&m.refr)?;
|
||||
self.check_default_value(&m.r#as)?;
|
||||
}
|
||||
|
||||
match &stmt.literal {
|
||||
Literal::SomeVars { span, .. } => {
|
||||
Err(span.error("invalid `some vars` in default value"))
|
||||
}
|
||||
Literal::SomeIn {
|
||||
key,
|
||||
value,
|
||||
collection,
|
||||
..
|
||||
} => {
|
||||
self.check_default_value(key)?;
|
||||
if let Some(value) = &value {
|
||||
self.check_default_value(value)?;
|
||||
}
|
||||
self.check_default_value(collection)
|
||||
}
|
||||
Literal::Expr { expr, .. } | Literal::NotExpr { expr, .. } => {
|
||||
self.check_default_value(expr)
|
||||
}
|
||||
Literal::Every { span, .. } => Err(span.error("invalid `every` in default value")),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_default_rules(&self) -> Result<()> {
|
||||
for module in &self.modules {
|
||||
for rule in &module.policy {
|
||||
if let Rule::Default { value, .. } = rule {
|
||||
self.check_default_value(value)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn eval_default_rule(&mut self, rule: &'source Rule<'source>) -> Result<()> {
|
||||
// Skip reprocessing rule.
|
||||
if self.processed.contains(rule) {
|
||||
@@ -1368,12 +1471,7 @@ impl<'source> Interpreter<'source> {
|
||||
Parser::get_path_ref_components_into(refr, &mut path)?;
|
||||
let paths: Vec<&str> = path.iter().map(|s| s.text()).collect();
|
||||
|
||||
if matches!(
|
||||
value,
|
||||
Expr::Var(_) | Expr::RefBrack { .. } | Expr::RefDot { .. }
|
||||
) {
|
||||
bail!("illegal default rule (value contains a variable or reference)");
|
||||
}
|
||||
self.check_default_value(value)?;
|
||||
let value = self.eval_expr(value)?;
|
||||
|
||||
// Assume at this point that all the non-default rules have been evaluated.
|
||||
@@ -1495,6 +1593,7 @@ impl<'source> Interpreter<'source> {
|
||||
self.data = data.clone();
|
||||
}
|
||||
|
||||
self.check_default_rules()?;
|
||||
self.update_function_table()?;
|
||||
self.gather_rules()?;
|
||||
|
||||
|
||||
@@ -61,6 +61,14 @@ impl<'source> Span<'source> {
|
||||
pub fn text(&self) -> &'source str {
|
||||
&self.source.contents[self.start as usize..self.end as usize]
|
||||
}
|
||||
|
||||
pub fn message(&self, kind: &str, msg: &str) -> String {
|
||||
self.source.message(self.line, self.col, kind, msg)
|
||||
}
|
||||
|
||||
pub fn error(&self, msg: &str) -> anyhow::Error {
|
||||
self.source.error(self.line, self.col, msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'source> Debug for Span<'source> {
|
||||
|
||||
@@ -15,7 +15,6 @@ pub struct Parser<'source> {
|
||||
line: u16,
|
||||
end: u16,
|
||||
future_keywords: BTreeMap<&'source str, Span<'source>>,
|
||||
in_default_value: bool,
|
||||
}
|
||||
|
||||
const FUTURE_KEYWORDS: [&str; 4] = ["contains", "every", "if", "in"];
|
||||
@@ -31,7 +30,6 @@ impl<'source> Parser<'source> {
|
||||
line: 0,
|
||||
end: 0,
|
||||
future_keywords: BTreeMap::new(),
|
||||
in_default_value: false,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -550,22 +548,6 @@ impl<'source> Parser<'source> {
|
||||
}
|
||||
}
|
||||
|
||||
if self.in_default_value {
|
||||
if let Some((kind, span)) = match &term {
|
||||
Expr::Var(v) => Some(("var", v)),
|
||||
Expr::RefDot { span, .. } => Some(("ref", span)),
|
||||
Expr::Call { span, .. } => Some(("call", span)),
|
||||
Expr::RefBrack { span, .. } => Some(("ref", span)),
|
||||
_ => None,
|
||||
} {
|
||||
return Err(self.source.error(
|
||||
span.line,
|
||||
span.col,
|
||||
format!("invalid {kind} in default value").as_str(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(term)
|
||||
}
|
||||
|
||||
@@ -1418,9 +1400,7 @@ impl<'source> Parser<'source> {
|
||||
|
||||
// todo: Rego errors for binary expressions here, but they are
|
||||
// somehow valid in a comprehension
|
||||
self.in_default_value = true;
|
||||
let value = self.parse_term()?;
|
||||
self.in_default_value = false;
|
||||
span.end = self.end;
|
||||
Ok(Rule::Default {
|
||||
span,
|
||||
|
||||
@@ -12,7 +12,7 @@ cases:
|
||||
|
||||
a = b
|
||||
|
||||
default b = 6
|
||||
default b = -6
|
||||
|
||||
c = d
|
||||
|
||||
@@ -34,11 +34,23 @@ cases:
|
||||
|
||||
complex["hello"] = "world"
|
||||
|
||||
default empty_array = []
|
||||
default empty_set = set()
|
||||
default empty_object = {}
|
||||
default null_value = null
|
||||
default true_value = true
|
||||
default false_value = false
|
||||
default string_value = "abc"
|
||||
default composite_value = [ 1, {2}, {"a": 5}]
|
||||
default array_compr = [5 | true]
|
||||
default set_compr = {5 | true}
|
||||
default object_compr = {"a":5 | true}
|
||||
default empty_array_1 = [1 | not 1]
|
||||
query: data.test
|
||||
want_result:
|
||||
x: 5
|
||||
a: 6
|
||||
b: 6
|
||||
a: -6
|
||||
b: -6
|
||||
c: "has_default"
|
||||
d: "has_default"
|
||||
object:
|
||||
@@ -53,4 +65,284 @@ cases:
|
||||
value: "bool_false"
|
||||
- key: "hello"
|
||||
value: "world"
|
||||
|
||||
empty_array: []
|
||||
empty_set:
|
||||
set!: []
|
||||
empty_object:
|
||||
object!: []
|
||||
null_value: null
|
||||
true_value: true
|
||||
false_value: false
|
||||
string_value: "abc"
|
||||
composite_value:
|
||||
- 1
|
||||
- set!: [2]
|
||||
- a: 5
|
||||
array_compr: [5]
|
||||
set_compr:
|
||||
set!: [5]
|
||||
object_compr:
|
||||
a: 5
|
||||
empty_array_1: []
|
||||
|
||||
- note: invalid-var
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = x
|
||||
query: data
|
||||
error: invalid `var` in default value
|
||||
|
||||
- note: invalid-var-in-set-aray
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = [ {x} ]
|
||||
query: data
|
||||
error: invalid `var` in default value
|
||||
|
||||
- note: invalid-var-in-object-key
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = { x : 5 }
|
||||
query: data
|
||||
error: invalid `var` in default value
|
||||
|
||||
- note: invalid-var-in-object-value
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = { "x" : x }
|
||||
query: data
|
||||
error: invalid `var` in default value
|
||||
|
||||
- note: invalid-var-in-array-compr-output
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = [ x | 1 ]
|
||||
query: data
|
||||
error: invalid `var` in default value
|
||||
|
||||
- note: invalid-var-in-array-compr-query
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = [ 1 | true; x ]
|
||||
query: data
|
||||
error: invalid `var` in default value
|
||||
|
||||
- note: invalid-var-in-set-compr-output
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = { x | 1 }
|
||||
query: data
|
||||
error: invalid `var` in default value
|
||||
|
||||
- note: invalid-var-in-array-compr-query
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = { 1 | true; x }
|
||||
query: data
|
||||
error: invalid `var` in default value
|
||||
|
||||
- note: invalid-var-in-object-compr-key
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = { x : 5 | true }
|
||||
query: data
|
||||
error: invalid `var` in default value
|
||||
|
||||
- note: invalid-var-in-object-compr-value
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = { "x" : x | true }
|
||||
query: data
|
||||
error: invalid `var` in default value
|
||||
|
||||
- note: invalid-var-in-object-compr-query
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = { "a" : 5 | x }
|
||||
query: data
|
||||
error: invalid `var` in default value
|
||||
|
||||
- note: invalid-call
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = incr(5)
|
||||
query: data
|
||||
error: invalid `call` in default value
|
||||
|
||||
- note: invalid-unaryexpr
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = -b
|
||||
query: data
|
||||
error: invalid `unaryexpr` in default value
|
||||
|
||||
- note: invalid-refdot
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = {"b" : 5}.b
|
||||
query: data
|
||||
error: invalid `ref` in default value
|
||||
|
||||
- note: invalid-refbrack
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = ["abc"][0]
|
||||
query: data
|
||||
error: invalid `ref` in default value
|
||||
|
||||
- note: invalid-binexpr
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = ({5} | {6})
|
||||
query: data
|
||||
error: invalid `binexpr` in default value
|
||||
|
||||
- note: invalid-boolexpr
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = (5 > 6)
|
||||
query: data
|
||||
error: invalid `boolexpr` in default value
|
||||
|
||||
- note: invalid-arithexpr
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = (5 + 6)
|
||||
query: data
|
||||
error: invalid `arithexpr` in default value
|
||||
|
||||
- note: invalid-assignexpr
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
# This is rejected by parser.
|
||||
default a = (x = 5)
|
||||
query: data
|
||||
error: expecting `)`
|
||||
|
||||
- note: invalid-membership
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
import future.keywords
|
||||
# Following is rejected by parser
|
||||
default a = 5 in {5}
|
||||
query: data
|
||||
error: unexpected keyword `in`
|
||||
|
||||
- note: invalid-some-vars
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = [5 | some a]
|
||||
query: data
|
||||
error: invalid `some vars` in default value
|
||||
|
||||
- note: invalid-some-vars
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
default a = [5 | some a]
|
||||
query: data
|
||||
error: invalid `some vars` in default value
|
||||
|
||||
- note: invalid-every
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
import future.keywords
|
||||
default a = [5 | every x in [1,2,3] { true }]
|
||||
query: data
|
||||
error: invalid `every` in default value
|
||||
|
||||
- note: invalid-var-in-some-in-key
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
import future.keywords
|
||||
default a = [5 | some x in {5}]
|
||||
query: data
|
||||
error: invalid `var` in default value
|
||||
|
||||
- note: invalid-var-in-some-in-value
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
import future.keywords
|
||||
default a = [5 | some 5, x in {5}]
|
||||
query: data
|
||||
error: invalid `var` in default value
|
||||
|
||||
- note: invalid-var-in-some-in-collection
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
import future.keywords
|
||||
default a = [5 | some 5, 5 in x]
|
||||
query: data
|
||||
error: invalid `var` in default value
|
||||
|
||||
- note: invalid-var-in-not-expr
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
import future.keywords
|
||||
default a = [5 | not x]
|
||||
query: data
|
||||
error: invalid `var` in default value
|
||||
|
||||
- note: invalid-var-in-expr
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package t
|
||||
import future.keywords
|
||||
default a = [5 | x]
|
||||
query: data
|
||||
error: invalid `var` in default value
|
||||
|
||||
|
||||
Reference in New Issue
Block a user