def update_would_cause_errors(self, events): """Return a list of PolicyException. Return a list of PolicyException if we were to apply the insert/deletes of policy statements dictated by EVENTS to the current policy. """ self.log(None, "update_would_cause_errors %s", iterstr(events)) errors = [] for event in events: if not compile.is_datalog(event.formula): errors.append(PolicyException( "Non-formula found: {}".format( str(event.formula)))) else: if event.formula.is_atom(): errors.extend(compile.fact_errors( event.formula, self.theories, self.name)) else: errors.extend(compile.rule_errors( event.formula, self.theories, self.name)) # Would also check that rules are non-recursive, but that # is currently being handled by Runtime. The current implementation # disallows recursion in all theories. return errors
def update_would_cause_errors(self, events): """Return a list of PolicyException. Return a list of PolicyException if we were to apply the insert/deletes of policy statements dictated by EVENTS to the current policy. """ self.log(None, "update_would_cause_errors %s", utility.iterstr(events)) errors = [] for event in events: if not compile.is_datalog(event.formula): errors.append( exception.PolicyException("Non-formula found: {}".format( str(event.formula)))) else: if event.formula.is_atom(): errors.extend( compile.fact_errors(event.formula, self.theories, self.name)) else: errors.extend( compile.rule_errors(event.formula, self.theories, self.name)) # Would also check that rules are non-recursive, but that # is currently being handled by Runtime. The current implementation # disallows recursion in all theories. return errors
def test_module_schemas(self): """Test that rules are properly checked against module schemas.""" run = agnostic.Runtime() run.create_policy('mod1') run.create_policy('mod2') run.set_schema('mod1', compile.Schema({'p': (1, 2, 3), 'q': (1,)}), complete=True) run.set_schema('mod2', compile.Schema({'p': (1,), 'q': (1, 2)}), complete=True) def check_err(code_string, theory, emsg, msg, f=compile.rule_errors): rule = compile.parse1(code_string) errs = f(rule, run.theory, theory) self.assertTrue(any(emsg in str(err) for err in errs), msg + ":: Failed to find error message '" + emsg + "' in: " + ";".join(str(e) for e in errs)) # no errors rule = compile.parse1('p(x) :- q(x), mod1:p(x, y, z), mod2:q(x, y), ' 'mod1:q(t), mod2:p(t)') errs = compile.rule_errors(rule, run.theory) self.assertEqual(len(errs), 0, "Should not have found any errors") # unknown table within module check_err('p(x) :- q(x), mod1:r(x), r(x)', 'mod3', 'unknown table', 'Unknown table for rule') # wrong number of arguments check_err('p(x) :- q(x), mod1:p(x,y,z,w), r(x)', 'mod3', 'only 3 arguments are permitted', 'Wrong number of arguments for rule') # same tests for an atom # no errors atom = compile.parse1('p(1, 2, 2)') errs = compile.fact_errors(atom, run.theory, 'mod1') self.assertEqual(len(errs), 0, "Should not have found any errors") # unknown table within module check_err('r(1)', 'mod1', 'unknown table', 'Unknown table for atom', f=compile.fact_errors) # wrong number of arguments check_err('p(1, 2, 3, 4)', 'mod1', 'only 3 arguments are permitted', 'Wrong number of arguments for atom', f=compile.fact_errors)
def test_rule_validation(self): """Test that rules are properly validated.""" # unsafe var in head rule = compile.parse1('p(x) :- q(y)') errs = compile.rule_errors(rule) self.assertEqual(len(errs), 1) # multiple unsafe vars in head rule = compile.parse1('p(x,y,z) :- q(w)') errs = compile.rule_errors(rule) self.assertEqual(len(set([str(x) for x in errs])), 3) # unsafe var in negtative literal: rule = compile.parse1('p(x) :- q(x), not r(y)') errs = compile.rule_errors(rule) self.assertEqual(len(set([str(x) for x in errs])), 1) # unsafe var in negative literal: ensure head doesn't make safe rule = compile.parse1('p(x) :- not q(x)') errs = compile.rule_errors(rule) self.assertEqual(len(set([str(x) for x in errs])), 1) # unsafe var in negative literal: # ensure partial safety not total safety rule = compile.parse1('p(x) :- q(x), not r(x,y)') errs = compile.rule_errors(rule) self.assertEqual(len(set([str(x) for x in errs])), 1) # unsafe var in negative literal: ensure double negs doesn't make safe rule = compile.parse1('p(x) :- q(x), not r(x,y), not s(x, y)') errs = compile.rule_errors(rule) self.assertEqual(len(set([str(x) for x in errs])), 1) # multiple heads with modal rule = compile.parse1('execute[p(x)], r(x) :- q(x)') errs = compile.rule_errors(rule) self.assertEqual(len(set([str(x) for x in errs])), 1) # modal in body rule = compile.parse1('p(x) :- execute[q(x)]') errs = compile.rule_errors(rule) self.assertEqual(len(set([str(x) for x in errs])), 1) # keywords rule = compile.parse1('equal(x) :- q(x)') errs = compile.rule_errors(rule) self.assertEqual(len(set([str(x) for x in errs])), 1)
def update_would_cause_errors(self, events): """Return a list of PolicyException. Return a list of PolicyException if we were to apply the events EVENTS to the current policy. """ self.log(None, "update_would_cause_errors %s", utility.iterstr(events)) errors = [] # compute new rule set for event in events: assert compile.is_datalog(event.formula), "update_would_cause_errors operates only on objects" self.log(None, "Updating %s", event.formula) if event.formula.is_atom(): errors.extend(compile.fact_errors(event.formula, self.theories, self.name)) else: errors.extend(compile.rule_errors(event.formula, self.theories, self.name)) return errors
def update_would_cause_errors(self, events): """Return a list of PolicyException. Return a list of PolicyException if we were to apply the events EVENTS to the current policy. """ self.log(None, "update_would_cause_errors %s", iterstr(events)) errors = [] # compute new rule set for event in events: assert compile.is_datalog(event.formula), ( "update_would_cause_errors operates only on objects") self.log(None, "Updating %s", event.formula) if event.formula.is_atom(): errors.extend( compile.fact_errors(event.formula, self.theories, self.name)) else: errors.extend( compile.rule_errors(event.formula, self.theories, self.name)) return errors
def test_module_schemas(self): """Test that rules are properly checked against module schemas.""" run = agnostic.Runtime() run.create_policy('mod1') run.create_policy('mod2') run.set_schema('mod1', compile.Schema({ 'p': (1, 2, 3), 'q': (1, ) }), complete=True) run.set_schema('mod2', compile.Schema({ 'p': (1, ), 'q': (1, 2) }), complete=True) def check_err(code_string, theory, emsg, msg, f=compile.rule_errors): rule = compile.parse1(code_string) errs = f(rule, run.theory, theory) self.assertTrue( any(emsg in str(err) for err in errs), msg + ":: Failed to find error message '" + emsg + "' in: " + ";".join(str(e) for e in errs)) # no errors rule = compile.parse1('p(x) :- q(x), mod1:p(x, y, z), mod2:q(x, y), ' 'mod1:q(t), mod2:p(t)') errs = compile.rule_errors(rule, run.theory) self.assertEqual(len(errs), 0, "Should not have found any errors") # unknown table within module check_err('p(x) :- q(x), mod1:r(x), r(x)', 'mod3', 'unknown table', 'Unknown table for rule') # wrong number of arguments check_err('p(x) :- q(x), mod1:p(x,y,z,w), r(x)', 'mod3', 'only 3 arguments are permitted', 'Wrong number of arguments for rule') # same tests for an atom # no errors atom = compile.parse1('p(1, 2, 2)') errs = compile.fact_errors(atom, run.theory, 'mod1') self.assertEqual(len(errs), 0, "Should not have found any errors") # unknown table within module check_err('r(1)', 'mod1', 'unknown table', 'Unknown table for atom', f=compile.fact_errors) # wrong number of arguments check_err('p(1, 2, 3, 4)', 'mod1', 'only 3 arguments are permitted', 'Wrong number of arguments for atom', f=compile.fact_errors) # schema update schema = compile.Schema() rule1 = compile.parse1('p(x) :- q(x, y)') change1 = schema.update(rule1.head, True) rule2 = compile.parse1('p(x) :- r(x, y)') change2 = schema.update(rule2.head, True) self.assertEqual(schema.count['p'], 2) schema.revert(change2) self.assertEqual(schema.count['p'], 1) schema.revert(change1) self.assertEqual('p' in schema.count, False) schema.update(rule1.head, True) schema.update(rule2.head, True) change1 = schema.update(rule1.head, False) change2 = schema.update(rule2.head, False) self.assertEqual('p' in schema.count, False) schema.revert(change2) self.assertEqual(schema.count['p'], 1) schema.revert(change1) self.assertEqual(schema.count['p'], 2)