Skip to content

Commit

Permalink
feat: detect free variables in rule expressions
Browse files Browse the repository at this point in the history
Free variables are already forbidden in rule heads, but not in rule expressions.
  • Loading branch information
divarvel committed May 24, 2024
1 parent 2281ac5 commit b64cbab
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 23 deletions.
18 changes: 15 additions & 3 deletions biscuit-auth/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,25 @@ mod tests {
}

#[test]
fn rule_with_unused_head_variables() {
fn rule_with_free_head_variables() {
assert_eq!(
biscuit_parser::parser::rule("right($0, $test) <- resource($0), operation(\"read\")"),
Err( nom::Err::Failure(Error {
input: "right($0, $test)",
input: "right($0, $test) <- resource($0), operation(\"read\")",
code: ErrorKind::Satisfy,
message: Some("rule head contains variables that are not used in predicates of the rule's body: $test".to_string()),
message: Some("the rule contains variables that are not bound by predicates in the rule's body: $test".to_string()),
}))
);
}

#[test]
fn rule_with_free_expression_variables() {
assert_eq!(
biscuit_parser::parser::rule("right($0) <- resource($0), operation(\"read\"), $test"),
Err( nom::Err::Failure(Error {
input: "right($0) <- resource($0), operation(\"read\"), $test",
code: ErrorKind::Satisfy,
message: Some("the rule contains variables that are not bound by predicates in the rule's body: $test".to_string()),
}))
);
}
Expand Down
35 changes: 20 additions & 15 deletions biscuit-parser/src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! helper functions and structure to create tokens and blocks
use std::{
collections::{BTreeSet, HashMap},
collections::{BTreeSet, HashMap, HashSet},
time::{SystemTime, UNIX_EPOCH},
};

Expand Down Expand Up @@ -328,33 +328,38 @@ impl Rule {
}

pub fn validate_variables(&self) -> Result<(), String> {
let mut head_variables: std::collections::HashSet<String> = self
.head
.terms
.iter()
.filter_map(|term| match term {
Term::Variable(s) => Some(s.to_string()),
_ => None,
})
.collect();
let mut free_variables: HashSet<String> = HashSet::default();
for term in self.head.terms.iter() {
if let Term::Variable(s) = term {
free_variables.insert(s.to_string());
}
}

for e in self.expressions.iter() {
for op in e.ops.iter() {
if let Op::Value(Term::Variable(s)) = op {
free_variables.insert(s.to_string());
}
}
}

for predicate in self.body.iter() {
for term in predicate.terms.iter() {
if let Term::Variable(v) = term {
head_variables.remove(v);
if head_variables.is_empty() {
free_variables.remove(v);
if free_variables.is_empty() {
return Ok(());
}
}
}
}

if head_variables.is_empty() {
if free_variables.is_empty() {
Ok(())
} else {
Err(format!(
"rule head contains variables that are not used in predicates of the rule's body: {}",
head_variables
"the rule contains variables that are not bound by predicates in the rule's body: {}",
free_variables
.iter()
.map(|s| format!("${}", s))
.collect::<Vec<_>>()
Expand Down
14 changes: 9 additions & 5 deletions biscuit-parser/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,18 +175,22 @@ pub fn rule(i: &str) -> IResult<&str, builder::Rule, Error> {
}

pub fn rule_inner(i: &str) -> IResult<&str, builder::Rule, Error> {
let (i, (head_input, head)) = consumed(rule_head)(i)?;
let (i, _) = space0(i)?;
let (i, (input, (head, body, expressions, scopes))) = consumed(|i| {
let (i, head) = rule_head(i)?;
let (i, _) = space0(i)?;

let (i, _) = tag("<-")(i)?;

let (i, _) = tag("<-")(i)?;
let (i, (body, expressions, scopes)) = cut(rule_body)(i)?;

let (i, (body, expressions, scopes)) = cut(rule_body)(i)?;
Ok((i, (head, body, expressions, scopes)))
})(i)?;

let rule = builder::Rule::new(head, body, expressions, scopes);

if let Err(message) = rule.validate_variables() {
return Err(nom::Err::Failure(Error {
input: head_input,
input,
code: ErrorKind::Satisfy,
message: Some(message),
}));
Expand Down

0 comments on commit b64cbab

Please sign in to comment.