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:
Anand Krishnamoorthi
2023-02-10 06:09:39 -08:00
parent c92707433e
commit 3a3bbcdb7d
4 changed files with 409 additions and 30 deletions

View File

@@ -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()?;

View File

@@ -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> {

View File

@@ -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,

View File

@@ -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