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!();
|
panic!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Literal::NotExpr { expr, .. } => matches!(self.eval_expr(expr)?, Value::Bool(false)),
|
||||||
_ => unimplemented!(),
|
_ => 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<()> {
|
fn eval_default_rule(&mut self, rule: &'source Rule<'source>) -> Result<()> {
|
||||||
// Skip reprocessing rule.
|
// Skip reprocessing rule.
|
||||||
if self.processed.contains(rule) {
|
if self.processed.contains(rule) {
|
||||||
@@ -1368,12 +1471,7 @@ impl<'source> Interpreter<'source> {
|
|||||||
Parser::get_path_ref_components_into(refr, &mut path)?;
|
Parser::get_path_ref_components_into(refr, &mut path)?;
|
||||||
let paths: Vec<&str> = path.iter().map(|s| s.text()).collect();
|
let paths: Vec<&str> = path.iter().map(|s| s.text()).collect();
|
||||||
|
|
||||||
if matches!(
|
self.check_default_value(value)?;
|
||||||
value,
|
|
||||||
Expr::Var(_) | Expr::RefBrack { .. } | Expr::RefDot { .. }
|
|
||||||
) {
|
|
||||||
bail!("illegal default rule (value contains a variable or reference)");
|
|
||||||
}
|
|
||||||
let value = self.eval_expr(value)?;
|
let value = self.eval_expr(value)?;
|
||||||
|
|
||||||
// Assume at this point that all the non-default rules have been evaluated.
|
// 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.data = data.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.check_default_rules()?;
|
||||||
self.update_function_table()?;
|
self.update_function_table()?;
|
||||||
self.gather_rules()?;
|
self.gather_rules()?;
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,14 @@ impl<'source> Span<'source> {
|
|||||||
pub fn text(&self) -> &'source str {
|
pub fn text(&self) -> &'source str {
|
||||||
&self.source.contents[self.start as usize..self.end as usize]
|
&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> {
|
impl<'source> Debug for Span<'source> {
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ pub struct Parser<'source> {
|
|||||||
line: u16,
|
line: u16,
|
||||||
end: u16,
|
end: u16,
|
||||||
future_keywords: BTreeMap<&'source str, Span<'source>>,
|
future_keywords: BTreeMap<&'source str, Span<'source>>,
|
||||||
in_default_value: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const FUTURE_KEYWORDS: [&str; 4] = ["contains", "every", "if", "in"];
|
const FUTURE_KEYWORDS: [&str; 4] = ["contains", "every", "if", "in"];
|
||||||
@@ -31,7 +30,6 @@ impl<'source> Parser<'source> {
|
|||||||
line: 0,
|
line: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
future_keywords: BTreeMap::new(),
|
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)
|
Ok(term)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1418,9 +1400,7 @@ impl<'source> Parser<'source> {
|
|||||||
|
|
||||||
// todo: Rego errors for binary expressions here, but they are
|
// todo: Rego errors for binary expressions here, but they are
|
||||||
// somehow valid in a comprehension
|
// somehow valid in a comprehension
|
||||||
self.in_default_value = true;
|
|
||||||
let value = self.parse_term()?;
|
let value = self.parse_term()?;
|
||||||
self.in_default_value = false;
|
|
||||||
span.end = self.end;
|
span.end = self.end;
|
||||||
Ok(Rule::Default {
|
Ok(Rule::Default {
|
||||||
span,
|
span,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ cases:
|
|||||||
|
|
||||||
a = b
|
a = b
|
||||||
|
|
||||||
default b = 6
|
default b = -6
|
||||||
|
|
||||||
c = d
|
c = d
|
||||||
|
|
||||||
@@ -34,11 +34,23 @@ cases:
|
|||||||
|
|
||||||
complex["hello"] = "world"
|
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
|
query: data.test
|
||||||
want_result:
|
want_result:
|
||||||
x: 5
|
x: 5
|
||||||
a: 6
|
a: -6
|
||||||
b: 6
|
b: -6
|
||||||
c: "has_default"
|
c: "has_default"
|
||||||
d: "has_default"
|
d: "has_default"
|
||||||
object:
|
object:
|
||||||
@@ -53,4 +65,284 @@ cases:
|
|||||||
value: "bool_false"
|
value: "bool_false"
|
||||||
- key: "hello"
|
- key: "hello"
|
||||||
value: "world"
|
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