1#![deny(
19 missing_docs,
20 rustdoc::broken_intra_doc_links,
21 rustdoc::private_intra_doc_links,
22 rustdoc::invalid_codeblock_attributes,
23 rustdoc::invalid_html_tags,
24 rustdoc::invalid_rust_codeblocks,
25 rustdoc::bare_urls,
26 clippy::doc_markdown
27)]
28#![cfg_attr(feature = "wasm", allow(non_snake_case))]
29
30use crate::ast::{Policy, PolicySet, Template};
31use std::collections::HashSet;
32mod level_validate;
33
34mod coreschema;
35#[cfg(feature = "entity-manifest")]
36pub mod entity_manifest;
37pub use coreschema::*;
38mod diagnostics;
39pub use diagnostics::*;
40mod expr_iterator;
41mod extension_schema;
42mod extensions;
43mod rbac;
44mod schema;
45pub use schema::err::*;
46pub use schema::*;
47mod deprecated_schema_compat;
48pub mod json_schema;
49mod str_checks;
50pub use str_checks::confusable_string_checks;
51pub mod cedar_schema;
52pub mod typecheck;
53use typecheck::Typechecker;
54mod partition_nonempty;
55pub mod types;
56
57#[derive(Default, Eq, PartialEq, Copy, Clone, Debug)]
59pub enum ValidationMode {
60 #[default]
62 Strict,
63 Permissive,
65 #[cfg(feature = "partial-validate")]
68 Partial,
69}
70
71impl ValidationMode {
72 fn is_partial(self) -> bool {
75 match self {
76 ValidationMode::Strict | ValidationMode::Permissive => false,
77 #[cfg(feature = "partial-validate")]
78 ValidationMode::Partial => true,
79 }
80 }
81
82 fn is_strict(self) -> bool {
84 match self {
85 ValidationMode::Strict => true,
86 ValidationMode::Permissive => false,
87 #[cfg(feature = "partial-validate")]
88 ValidationMode::Partial => false,
89 }
90 }
91}
92
93#[derive(Debug, Clone)]
96pub struct Validator {
97 schema: ValidatorSchema,
98}
99
100impl Validator {
101 pub fn new(schema: ValidatorSchema) -> Validator {
103 Self { schema }
104 }
105
106 pub fn schema(&self) -> &ValidatorSchema {
108 &self.schema
109 }
110
111 pub fn validate(&self, policies: &PolicySet, mode: ValidationMode) -> ValidationResult {
114 let validate_policy_results: (Vec<_>, Vec<_>) = policies
115 .all_templates()
116 .map(|p| self.validate_policy(p, mode))
117 .unzip();
118 let template_and_static_policy_errs = validate_policy_results.0.into_iter().flatten();
119 let template_and_static_policy_warnings = validate_policy_results.1.into_iter().flatten();
120 let link_errs = policies
121 .policies()
122 .filter_map(|p| self.validate_slots(p, mode))
123 .flatten();
124 ValidationResult::new(
125 template_and_static_policy_errs.chain(link_errs),
126 template_and_static_policy_warnings
127 .chain(confusable_string_checks(policies.all_templates())),
128 )
129 }
130
131 pub fn validate_with_level(
136 &self,
137 policies: &PolicySet,
138 mode: ValidationMode,
139 max_deref_level: u32,
140 ) -> ValidationResult {
141 let validate_policy_results: (Vec<_>, Vec<_>) = policies
142 .all_templates()
143 .map(|p| self.validate_policy_with_level(p, mode, max_deref_level))
144 .unzip();
145 let template_and_static_policy_errs = validate_policy_results.0.into_iter().flatten();
146 let template_and_static_policy_warnings = validate_policy_results.1.into_iter().flatten();
147 let link_errs = policies
148 .policies()
149 .filter_map(|p| self.validate_slots(p, mode))
150 .flatten();
151 ValidationResult::new(
152 template_and_static_policy_errs.chain(link_errs),
153 template_and_static_policy_warnings
154 .chain(confusable_string_checks(policies.all_templates())),
155 )
156 }
157
158 fn validate_policy<'a>(
162 &'a self,
163 p: &'a Template,
164 mode: ValidationMode,
165 ) -> (
166 impl Iterator<Item = ValidationError> + 'a,
167 impl Iterator<Item = ValidationWarning> + 'a,
168 ) {
169 let validation_errors = if mode.is_partial() {
170 None
175 } else {
176 Some(
177 Validator::validate_entity_types(&self.schema, p)
178 .chain(Validator::validate_enum_entity(&self.schema, p))
179 .chain(Validator::validate_action_ids(&self.schema, p))
180 .chain(self.validate_template_action_application(p)),
185 )
186 }
187 .into_iter()
188 .flatten();
189 let (errors, warnings) = self.typecheck_policy(p, mode);
190 (validation_errors.chain(errors), warnings)
191 }
192
193 pub fn validate_entity_types_and_literals<'a>(
198 schema: &'a ValidatorSchema,
199 p: &'a Template,
200 ) -> impl Iterator<Item = ValidationError> + 'a {
201 Validator::validate_entity_types(&schema, p)
202 .chain(Validator::validate_enum_entity(&schema, p))
203 .chain(Validator::validate_action_ids(&schema, p))
204 }
205
206 fn validate_slots<'a>(
209 &'a self,
210 p: &'a Policy,
211 mode: ValidationMode,
212 ) -> Option<impl Iterator<Item = ValidationError> + 'a> {
213 if p.is_static() {
215 return None;
216 }
217 if mode.is_partial() {
221 return None;
222 }
223 Some(
227 self.validate_entity_types_in_slots(p.id(), p.env())
228 .chain(self.validate_linked_action_application(p)),
229 )
230 }
231
232 fn typecheck_policy<'a>(
238 &'a self,
239 t: &'a Template,
240 mode: ValidationMode,
241 ) -> (
242 impl Iterator<Item = ValidationError> + 'a,
243 impl Iterator<Item = ValidationWarning> + 'a,
244 ) {
245 let typecheck = Typechecker::new(&self.schema, mode);
246 let mut errors = HashSet::new();
247 let mut warnings = HashSet::new();
248 typecheck.typecheck_policy(t, &mut errors, &mut warnings);
249 (errors.into_iter(), warnings.into_iter())
250 }
251}
252
253#[cfg(test)]
254mod test {
255 use itertools::Itertools;
256 use std::{collections::HashMap, sync::Arc};
257
258 use crate::validator::types::Type;
259 use crate::validator::validation_errors::UnrecognizedActionIdHelp;
260 use crate::validator::Result;
261
262 use super::*;
263 use crate::{
264 ast::{self, PolicyID},
265 est::Annotations,
266 parser::{self, Loc},
267 };
268
269 use similar_asserts::assert_eq;
270
271 #[test]
272 fn top_level_validate() -> Result<()> {
273 let mut set = PolicySet::new();
274 let foo_type = "foo_type";
275 let bar_type = "bar_type";
276 let action_name = "action";
277 let schema_file = json_schema::NamespaceDefinition::new(
278 [
279 (
280 foo_type.parse().unwrap(),
281 json_schema::StandardEntityType {
282 member_of_types: vec![],
283 shape: json_schema::AttributesOrContext::default(),
284 tags: None,
285 }
286 .into(),
287 ),
288 (
289 bar_type.parse().unwrap(),
290 json_schema::StandardEntityType {
291 member_of_types: vec![],
292 shape: json_schema::AttributesOrContext::default(),
293 tags: None,
294 }
295 .into(),
296 ),
297 ],
298 [(
299 action_name.into(),
300 json_schema::ActionType {
301 applies_to: Some(json_schema::ApplySpec {
302 principal_types: vec!["foo_type".parse().unwrap()],
303 resource_types: vec!["bar_type".parse().unwrap()],
304 context: json_schema::AttributesOrContext::default(),
305 }),
306 member_of: None,
307 attributes: None,
308 annotations: Annotations::new(),
309 loc: None,
310 #[cfg(feature = "extended-schema")]
311 defn_loc: None,
312 },
313 )],
314 );
315 let schema = schema_file.try_into().unwrap();
316 let validator = Validator::new(schema);
317
318 let policy_a_src = r#"permit(principal in foo_type::"a", action == Action::"actin", resource == bar_type::"b");"#;
319 let policy_a = parser::parse_policy(Some(PolicyID::from_string("pola")), policy_a_src)
320 .expect("Test Policy Should Parse");
321 set.add_static(policy_a)
322 .expect("Policy already present in PolicySet");
323
324 let policy_b_src = r#"permit(principal in foo_tye::"a", action == Action::"action", resource == br_type::"b");"#;
325 let policy_b = parser::parse_policy(Some(PolicyID::from_string("polb")), policy_b_src)
326 .expect("Test Policy Should Parse");
327 set.add_static(policy_b)
328 .expect("Policy already present in PolicySet");
329
330 let result = validator.validate(&set, ValidationMode::default());
331 let principal_err = ValidationError::unrecognized_entity_type(
332 Some(Loc::new(20..27, Arc::from(policy_b_src))),
333 PolicyID::from_string("polb"),
334 "foo_tye".to_string(),
335 Some("foo_type".to_string()),
336 );
337 let resource_err = ValidationError::unrecognized_entity_type(
338 Some(Loc::new(74..81, Arc::from(policy_b_src))),
339 PolicyID::from_string("polb"),
340 "br_type".to_string(),
341 Some("bar_type".to_string()),
342 );
343 let action_err = ValidationError::unrecognized_action_id(
344 Some(Loc::new(45..60, Arc::from(policy_a_src))),
345 PolicyID::from_string("pola"),
346 "Action::\"actin\"".to_string(),
347 Some(UnrecognizedActionIdHelp::SuggestAlternative(
348 "Action::\"action\"".to_string(),
349 )),
350 );
351
352 assert!(!result.validation_passed());
353 assert!(
354 result.validation_errors().contains(&principal_err),
355 "{result:?}"
356 );
357 assert!(
358 result.validation_errors().contains(&resource_err),
359 "{result:?}"
360 );
361 assert!(
362 result.validation_errors().contains(&action_err),
363 "{result:?}"
364 );
365 Ok(())
366 }
367
368 #[test]
369 fn top_level_validate_with_links() -> Result<()> {
370 let mut set = PolicySet::new();
371 let schema: ValidatorSchema = json_schema::Fragment::from_json_str(
372 r#"
373 {
374 "some_namespace": {
375 "entityTypes": {
376 "User": {
377 "shape": {
378 "type": "Record",
379 "attributes": {
380 "department": {
381 "type": "String"
382 },
383 "jobLevel": {
384 "type": "Long"
385 }
386 }
387 },
388 "memberOfTypes": [
389 "UserGroup"
390 ]
391 },
392 "UserGroup": {},
393 "Photo" : {}
394 },
395 "actions": {
396 "view": {
397 "appliesTo": {
398 "resourceTypes": [
399 "Photo"
400 ],
401 "principalTypes": [
402 "User"
403 ]
404 }
405 }
406 }
407 }
408 }
409 "#,
410 )
411 .expect("Schema parse error.")
412 .try_into()
413 .expect("Expected valid schema.");
414 let validator = Validator::new(schema);
415
416 let t = parser::parse_policy_or_template(
417 Some(PolicyID::from_string("template")),
418 r#"permit(principal == some_namespace::User::"Alice", action, resource in ?resource);"#,
419 )
420 .expect("Parse Error");
421 let loc = t.loc().cloned();
422 set.add_template(t)
423 .expect("Template already present in PolicySet");
424
425 let result = validator.validate(&set, ValidationMode::default());
427 assert_eq!(
428 result.validation_errors().collect::<Vec<_>>(),
429 Vec::<&ValidationError>::new()
430 );
431
432 let mut values = HashMap::new();
434 values.insert(
435 ast::SlotId::resource(),
436 ast::EntityUID::from_components(
437 "some_namespace::Photo".parse().unwrap(),
438 ast::Eid::new("foo"),
439 None,
440 ),
441 );
442 set.link(
443 ast::PolicyID::from_string("template"),
444 ast::PolicyID::from_string("link1"),
445 values,
446 )
447 .expect("Linking failed!");
448 let result = validator.validate(&set, ValidationMode::default());
449 assert!(result.validation_passed());
450
451 let mut values = HashMap::new();
453 values.insert(
454 ast::SlotId::resource(),
455 ast::EntityUID::from_components(
456 "some_namespace::Undefined".parse().unwrap(),
457 ast::Eid::new("foo"),
458 None,
459 ),
460 );
461 set.link(
462 ast::PolicyID::from_string("template"),
463 ast::PolicyID::from_string("link2"),
464 values,
465 )
466 .expect("Linking failed!");
467 let result = validator.validate(&set, ValidationMode::default());
468 assert!(!result.validation_passed());
469 assert_eq!(result.validation_errors().count(), 2);
470 let undefined_err = ValidationError::unrecognized_entity_type(
471 None,
472 PolicyID::from_string("link2"),
473 "some_namespace::Undefined".to_string(),
474 Some("some_namespace::User".to_string()),
475 );
476 let invalid_action_err = ValidationError::invalid_action_application(
477 loc.clone(),
478 PolicyID::from_string("link2"),
479 false,
480 false,
481 );
482
483 let actual_undef_error = result
484 .validation_errors()
485 .find(|e| matches!(e, ValidationError::UnrecognizedEntityType(_)))
486 .unwrap();
487 assert_eq!(actual_undef_error, &undefined_err);
488
489 let actual_action_error = result
490 .validation_errors()
491 .find(|e| matches!(e, ValidationError::InvalidActionApplication(_)))
492 .unwrap();
493 assert_eq!(actual_action_error, &invalid_action_err);
494
495 let mut values = HashMap::new();
497 values.insert(
498 ast::SlotId::resource(),
499 ast::EntityUID::from_components(
500 "some_namespace::User".parse().unwrap(),
501 ast::Eid::new("foo"),
502 None,
503 ),
504 );
505 set.link(
506 ast::PolicyID::from_string("template"),
507 ast::PolicyID::from_string("link3"),
508 values,
509 )
510 .expect("Linking failed!");
511 let result = validator.validate(&set, ValidationMode::default());
512 assert!(!result.validation_passed());
513 assert_eq!(result.validation_errors().count(), 3);
515 let invalid_action_err = ValidationError::invalid_action_application(
516 loc,
517 PolicyID::from_string("link3"),
518 false,
519 false,
520 );
521 assert!(result.validation_errors().contains(&invalid_action_err));
522
523 Ok(())
524 }
525
526 #[test]
527 fn validate_finds_warning_and_error() {
528 let schema: ValidatorSchema = json_schema::Fragment::from_json_str(
529 r#"
530 {
531 "": {
532 "entityTypes": {
533 "User": { }
534 },
535 "actions": {
536 "view": {
537 "appliesTo": {
538 "resourceTypes": [ "User" ],
539 "principalTypes": [ "User" ]
540 }
541 }
542 }
543 }
544 }
545 "#,
546 )
547 .expect("Schema parse error.")
548 .try_into()
549 .expect("Expected valid schema.");
550 let validator = Validator::new(schema);
551
552 let mut set = PolicySet::new();
553 let src = r#"permit(principal == User::"าปenry", action, resource) when {1 > true};"#;
554 let p = parser::parse_policy(None, src).unwrap();
555 set.add_static(p).unwrap();
556
557 let result = validator.validate(&set, ValidationMode::default());
558 assert_eq!(
559 result.validation_errors().collect::<Vec<_>>(),
560 vec![&ValidationError::expected_type(
561 typecheck::test::test_utils::get_loc(src, "true"),
562 PolicyID::from_string("policy0"),
563 Type::primitive_long(),
564 Type::singleton_boolean(true),
565 None,
566 )]
567 );
568 assert_eq!(
569 result.validation_warnings().collect::<Vec<_>>(),
570 vec![&ValidationWarning::mixed_script_identifier(
571 None,
572 PolicyID::from_string("policy0"),
573 "าปenry"
574 )]
575 );
576 }
577}
578
579#[cfg(test)]
580mod enumerated_entity_types {
581 use std::collections::HashMap;
582
583 use crate::{
584 ast::{Eid, EntityUID, ExprBuilder, PolicyID, PolicySet, SlotId, Template},
585 expr_builder::ExprBuilder as _,
586 extensions::Extensions,
587 parser::parse_policy_or_template,
588 };
589 use cool_asserts::assert_matches;
590 use itertools::Itertools;
591
592 use crate::validator::{
593 typecheck::test::test_utils::get_loc,
594 types::{EntityLUB, Type},
595 validation_errors::AttributeAccess,
596 ValidationError, ValidationWarning, Validator, ValidatorSchema,
597 };
598
599 #[track_caller]
600 fn schema() -> ValidatorSchema {
601 ValidatorSchema::from_json_value(
602 serde_json::json!(
603 {
604 "": { "entityTypes": {
605 "Foo": {
606 "enum": [ "foo" ],
607 },
608 "Bar": {
609 "memberOfTypes": ["Foo"],
610 },
611 "Other": { },
612 },
613 "actions": {
614 "a": {
615 "appliesTo": {
616 "principalTypes": ["Foo"],
617 "resourceTypes": ["Bar"],
618 }
619 }
620 }
621 }
622 }
623 ),
624 Extensions::none(),
625 )
626 .unwrap()
627 }
628
629 #[test]
630 fn basic() {
631 let schema = schema();
632 let template = parse_policy_or_template(None, r#"permit(principal, action == Action::"a", resource) when { principal == Foo::"foo" };"#).unwrap();
633 let validator = Validator::new(schema);
634 let (errors, warnings) =
635 validator.validate_policy(&template, crate::validator::ValidationMode::Strict);
636 assert!(warnings.collect_vec().is_empty());
637 assert!(errors.collect_vec().is_empty());
638 }
639
640 #[test]
641 fn link() {
642 let schema = schema();
643 let template = parse_policy_or_template(
644 None,
645 r#"permit(principal in ?principal, action == Action::"a", resource);"#,
646 )
647 .unwrap();
648 let policy = Template::link(
649 std::sync::Arc::new(template),
650 PolicyID::from_string("test"),
651 HashMap::from_iter([(SlotId::principal(), r#"Other::"foo""#.parse().unwrap())]),
652 )
653 .unwrap();
654 let mut policy_set = PolicySet::new();
655 let _ = policy_set.add(policy);
656 let validator = Validator::new(schema);
657 let result = validator.validate(&policy_set, crate::validator::ValidationMode::Strict);
658
659 assert_eq!(result.validation_errors().collect_vec().len(), 1);
660 }
661
662 #[test]
663 #[allow(clippy::cognitive_complexity)]
664 fn basic_invalid() {
665 let schema = schema();
666 let template = parse_policy_or_template(None, r#"permit(principal, action == Action::"a", resource) when { principal == Foo::"fo" };"#).unwrap();
667 let validator = Validator::new(schema.clone());
668 let (errors, warnings) =
669 validator.validate_policy(&template, crate::validator::ValidationMode::Strict);
670 assert!(warnings.collect_vec().is_empty());
671 assert_matches!(&errors.collect_vec(), [ValidationError::InvalidEnumEntity(err)] => {
672 assert_eq!(err.err.choices, vec![Eid::new("foo")]);
673 assert_eq!(err.err.uid, EntityUID::with_eid_and_type("Foo", "fo").unwrap());
674 });
675
676 let template = parse_policy_or_template(
677 None,
678 r#"permit(principal == Foo::"๐", action == Action::"a", resource);"#,
679 )
680 .unwrap();
681 let validator = Validator::new(schema.clone());
682 let (errors, warnings) =
683 validator.validate_policy(&template, crate::validator::ValidationMode::Strict);
684 assert!(warnings.collect_vec().is_empty());
685 assert_matches!(&errors.collect_vec(), [ValidationError::InvalidEnumEntity(err)] => {
686 assert_eq!(err.err.choices, vec![Eid::new("foo")]);
687 assert_eq!(err.err.uid, EntityUID::with_eid_and_type("Foo", "๐").unwrap());
688 });
689
690 let template = parse_policy_or_template(
691 None,
692 r#"permit(principal in Foo::"๐", action == Action::"a", resource);"#,
693 )
694 .unwrap();
695 let validator = Validator::new(schema.clone());
696 let (errors, warnings) =
697 validator.validate_policy(&template, crate::validator::ValidationMode::Strict);
698 assert!(warnings.collect_vec().is_empty());
699 assert_matches!(&errors.collect_vec(), [ValidationError::InvalidEnumEntity(err)] => {
700 assert_eq!(err.err.choices, vec![Eid::new("foo")]);
701 assert_eq!(err.err.uid, EntityUID::with_eid_and_type("Foo", "๐").unwrap());
702 });
703
704 let template = parse_policy_or_template(
705 None,
706 r#"permit(principal, action == Action::"a", resource)
707 when { {"๐": Foo::"๐"} has "๐" };
708 "#,
709 )
710 .unwrap();
711 let validator = Validator::new(schema.clone());
712 let (errors, warnings) =
713 validator.validate_policy(&template, crate::validator::ValidationMode::Strict);
714 assert!(warnings.collect_vec().is_empty());
715 assert_matches!(&errors.collect_vec(), [ValidationError::InvalidEnumEntity(err)] => {
716 assert_eq!(err.err.choices, vec![Eid::new("foo")]);
717 assert_eq!(err.err.uid, EntityUID::with_eid_and_type("Foo", "๐").unwrap());
718 });
719
720 let template = parse_policy_or_template(
721 None,
722 r#"permit(principal, action == Action::"a", resource)
723 when { [Foo::"๐"].isEmpty() };
724 "#,
725 )
726 .unwrap();
727 let validator = Validator::new(schema.clone());
728 let (errors, warnings) =
729 validator.validate_policy(&template, crate::validator::ValidationMode::Strict);
730 assert!(warnings.collect_vec().is_empty());
731 assert_matches!(&errors.collect_vec(), [ValidationError::InvalidEnumEntity(err)] => {
732 assert_eq!(err.err.choices, vec![Eid::new("foo")]);
733 assert_eq!(err.err.uid, EntityUID::with_eid_and_type("Foo", "๐").unwrap());
734 });
735
736 let template = parse_policy_or_template(
737 None,
738 r#"permit(principal, action == Action::"a", resource)
739 when { [{"๐": Foo::"๐"}].isEmpty() };
740 "#,
741 )
742 .unwrap();
743 let validator = Validator::new(schema);
744 let (errors, warnings) =
745 validator.validate_policy(&template, crate::validator::ValidationMode::Strict);
746 assert!(warnings.collect_vec().is_empty());
747 assert_matches!(&errors.collect_vec(), [ValidationError::InvalidEnumEntity(err)] => {
748 assert_eq!(err.err.choices, vec![Eid::new("foo")]);
749 assert_eq!(err.err.uid, EntityUID::with_eid_and_type("Foo", "๐").unwrap());
750 });
751 }
752
753 #[test]
754 fn no_attrs_allowed() {
755 let schema = schema();
756 let src = r#"permit(principal, action == Action::"a", resource) when { principal.foo == "foo" };"#;
757 let template = parse_policy_or_template(None, src).unwrap();
758 let validator = Validator::new(schema);
759 let (errors, warnings) =
760 validator.validate_policy(&template, crate::validator::ValidationMode::Strict);
761 assert!(warnings.collect_vec().is_empty());
762 assert_eq!(
763 errors.collect_vec(),
764 [ValidationError::unsafe_attribute_access(
765 get_loc(src, "principal.foo"),
766 PolicyID::from_string("policy0"),
767 AttributeAccess::EntityLUB(
768 EntityLUB::single_entity("Foo".parse().unwrap()),
769 vec!["foo".into()],
770 ),
771 None,
772 false,
773 )]
774 );
775 }
776
777 #[test]
778 fn no_ancestors() {
779 let schema = schema();
780 let src = r#"permit(principal, action == Action::"a", resource) when { principal in Bar::"bar" };"#;
781 let template = parse_policy_or_template(None, src).unwrap();
782 let validator = Validator::new(schema);
783 let (errors, warnings) =
784 validator.validate_policy(&template, crate::validator::ValidationMode::Strict);
785 assert_eq!(
786 warnings.collect_vec(),
787 [ValidationWarning::impossible_policy(
788 get_loc(src, src),
789 PolicyID::from_string("policy0")
790 )]
791 );
792 assert!(errors.collect_vec().is_empty());
793 }
794
795 #[test]
796 fn no_tags_allowed() {
797 let schema = schema();
798 let src = r#"permit(principal, action == Action::"a", resource) when { principal.getTag("foo") == "foo" };"#;
799 let template = parse_policy_or_template(None, src).unwrap();
800 let validator = Validator::new(schema);
801 let (errors, warnings) =
802 validator.validate_policy(&template, crate::validator::ValidationMode::Strict);
803 assert!(warnings.collect_vec().is_empty());
804 assert_eq!(
805 errors.collect_vec(),
806 [ValidationError::unsafe_tag_access(
807 get_loc(src, r#"principal.getTag("foo")"#),
808 PolicyID::from_string("policy0"),
809 Some(EntityLUB::single_entity("Foo".parse().unwrap()),),
810 {
811 let builder = ExprBuilder::new();
812 let mut expr = builder.val("foo");
813 expr.set_data(Some(Type::primitive_string()));
814 expr
815 },
816 )]
817 );
818 }
819}