1lalrpop_mod!(
21 #[allow(warnings, unused)]
22 pub grammar,
23 "/src/parser/grammar.rs"
24);
25
26use super::*;
27
28thread_local!(static POLICIES_PARSER: grammar::PoliciesParser = grammar::PoliciesParser::new());
30thread_local!(static POLICY_PARSER: grammar::PolicyParser = grammar::PolicyParser::new());
31thread_local!(static EXPR_PARSER: grammar::ExprParser = grammar::ExprParser::new());
32thread_local!(static REF_PARSER: grammar::RefParser = grammar::RefParser::new());
33thread_local!(static PRIMARY_PARSER: grammar::PrimaryParser = grammar::PrimaryParser::new());
34thread_local!(static NAME_PARSER: grammar::NameParser = grammar::NameParser::new());
35thread_local!(static IDENT_PARSER: grammar::IdentParser = grammar::IdentParser::new());
36
37macro_rules! parse_collect_errors {
41 ($parser:ident, $text:ident) => {
42 $parser.with(|parser| {
43 let mut errs = Vec::new();
45 let result = parser.parse(&mut errs, $text);
46
47 let mut errors: Vec<err::ParseError> =
49 errs.into_iter().map(err::ParseError::from).collect();
50 let result = result.map_err(err::ParseError::from);
51
52 match result {
54 Ok(parsed) => {
55 if !errors.is_empty() {
56 Err(errors)
60 } else {
61 Ok(parsed)
62 }
63 }
64 Err(e) => {
65 errors.push(e);
66 Err(errors)
67 }
68 }
69 })
70 };
71}
72
73pub fn parse_policies(
75 text: &str,
76) -> Result<node::ASTNode<Option<cst::Policies>>, Vec<err::ParseError>> {
77 parse_collect_errors!(POLICIES_PARSER, text)
78}
79
80pub fn parse_policy(
82 text: &str,
83) -> Result<node::ASTNode<Option<cst::Policy>>, Vec<err::ParseError>> {
84 parse_collect_errors!(POLICY_PARSER, text)
85}
86
87pub fn parse_expr(text: &str) -> Result<node::ASTNode<Option<cst::Expr>>, Vec<err::ParseError>> {
89 parse_collect_errors!(EXPR_PARSER, text)
90}
91
92pub fn parse_ref(text: &str) -> Result<node::ASTNode<Option<cst::Ref>>, Vec<err::ParseError>> {
94 parse_collect_errors!(REF_PARSER, text)
95}
96
97pub fn parse_primary(
99 text: &str,
100) -> Result<node::ASTNode<Option<cst::Primary>>, Vec<err::ParseError>> {
101 parse_collect_errors!(PRIMARY_PARSER, text)
102}
103
104pub fn parse_name(text: &str) -> Result<node::ASTNode<Option<cst::Name>>, Vec<err::ParseError>> {
106 parse_collect_errors!(NAME_PARSER, text)
107}
108
109pub fn parse_ident(text: &str) -> Result<node::ASTNode<Option<cst::Ident>>, Vec<err::ParseError>> {
111 parse_collect_errors!(IDENT_PARSER, text)
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn expr1() {
120 assert!(parse_expr(
121 r#"
122 1
123 "#
124 )
125 .expect("parser fail")
126 .node
127 .is_some());
128 }
129
130 #[test]
131 fn expr2() {
132 assert!(parse_expr(
133 r#"
134 "string"
135 "#
136 )
137 .expect("parser fail")
138 .node
139 .is_some());
140 }
141
142 #[test]
143 fn expr3() {
144 assert!(parse_expr(
145 r#"
146 "string".foo == !7
147 "#
148 )
149 .expect("parser fail")
150 .node
151 .is_some());
152 }
153
154 #[test]
155 fn expr4() {
156 let result = parse_expr(
157 r#"
158 5 < 3 || -7 == 2 && 3 >= 6
159 "#,
160 )
161 .expect("parser fail")
162 .node;
163 assert!(result.is_some());
164 }
165
166 #[test]
167 fn expr5() {
168 assert!(parse_expr(
169 r#"
170 if 7 then 6 > 5 else !5 || "thursday"
171 "#
172 )
173 .expect("parser fail")
174 .node
175 .is_some());
176 }
177
178 #[test]
179 fn expr6() {
180 let result = parse_expr(
181 r#"
182 if 7 then 6 > 5 else !5 || "thursday" && ((8) >= "fish")
183 "#,
184 )
185 .expect("parser fail")
186 .node;
187 assert!(result.is_some());
188 }
189
190 #[test]
191 fn expr_overflow() {
192 assert!(parse_expr(
194 r#"
195 principal == -5555555555555555555555
196 "#
197 )
198 .is_err());
199 assert!(parse_expr(
200 r#"
201 principal == 5555555555555555555555
202 "#
203 )
204 .is_err());
205 }
206
207 #[test]
208 fn variable1() {
209 let policy = parse_policy(
210 r#"
211 permit(principal, action, var:h in 1);
212 "#,
213 );
214 assert!(policy.is_ok());
215 }
216
217 #[test]
218 fn variable2() {
219 let policy = parse_policy(
220 r#"
221 permit(principal, action, more in 2);
222 "#,
223 );
224 assert!(policy.is_ok());
225 }
226
227 #[test]
228 fn variable3() {
229 let policy = parse_policy(
230 r#"
231 permit(principal, action:a_name, resource);
232 "#,
233 );
234 assert!(policy.is_ok());
235 }
236
237 #[test]
238 fn variable4() {
239 let policy = parse_policy(
240 r#"
241 permit(principalorsomeotherident, action, resource);
242 "#,
243 );
244 assert!(policy.is_ok());
245 }
246
247 #[test]
248 fn variable6() {
249 let policy = parse_policy(
250 r#"
251 permit(var : in 6, action, resource);
252 "#,
253 );
254 assert!(policy.is_err());
255 }
256
257 #[test]
258 fn member1() {
259 let policy = parse_policy(
260 r#"
261 permit(principal, action, resource)
262 when{
263 2._field // oh, look, comments!
264 };
265 "#,
266 );
267 assert!(policy.is_ok());
268 }
269
270 #[test]
271 fn member2() {
272 let policy = parse_policy(
273 r#"
274 permit(principal, action, resource)
275 when{
276 "first".some_ident()
277 };
278 "#,
279 );
280 assert!(policy.is_ok());
281 }
282
283 #[test]
284 fn member3() {
285 let policy = parse_policy(
286 r#"
287 permit(principal, action, resource)
288 when{
289 [2,3,4].foo[2]
290 };
291 "#,
292 );
293 assert!(policy.is_ok());
294 }
295
296 #[test]
297 fn member4() {
298 let policy = parse_policy(
299 r#"
300 permit(principal, action, resource)
301 when{
302 {3<-4:"what?","ok then":-5>4}
303 };
304 "#,
305 );
306 assert!(policy.is_ok());
307 }
308
309 #[test]
310 fn member5() {
311 let policy = parse_policy(
312 r#"
313 permit(principal, action, resource)
314 when{
315 [3<4,"ok then",17,("none")]
316 };
317 "#,
318 );
319 assert!(policy.is_ok());
320 }
321
322 #[test]
323 fn member6() {
324 let policy = parse_policy(
325 r#"
326 permit(principal, action, resource)
327 when{
328 one.two
329 };
330 "#,
331 );
332 assert!(policy.is_ok());
333 }
334
335 #[test]
336 #[should_panic] fn member7() {
338 let policy = parse_policy(
339 r#"
340 permit(principal, action, resource)
341 when{
342 one{num:true,trivia:"first!"}
343 };
344 "#,
345 );
346 assert!(policy.is_ok());
347 }
348
349 #[test]
350 fn member8() {
351 let policy = parse_policy(
352 r#"
353 permit(principal, action, resource)
354 when{
355 {2:true,4:me}.with["pizza"]
356 };
357 "#,
358 );
359 assert!(policy.is_ok());
360 }
361
362 #[test]
363 fn member9() {
364 let policy = parse_policy(
365 r#"
366 permit(principal, action, resource)
367 when{
368 AllRects({two:2,four:3+5/5})
369 };
370 "#,
371 );
372 assert!(policy.is_ok());
373 }
374
375 #[test]
376 fn ident1() {
377 let ident = parse_ident(
378 r#"
379 principal
380 "#,
381 );
382 assert!(ident.is_ok());
383 }
384
385 #[test]
386 fn ident2() {
387 let ident = parse_ident(
388 r#"
389 if
390 "#,
391 );
392 assert!(ident.is_ok());
394 let ident = parse_ident(
395 r#"
396 false
397 "#,
398 );
399 assert!(ident.is_ok());
401 }
402
403 #[test]
404 fn ident3() {
405 let ident = parse_expr(
406 r#"
407 if
408 "#,
409 );
410 assert!(ident.is_err());
412 let name = parse_expr(
413 r#"
414 if::then::else
415 "#,
416 );
417 assert!(name.is_ok());
419 let names = parse_expr(
420 r#"
421 if::true::then::false::else::true
422 "#,
423 );
424 assert!(names.is_ok());
426 }
427
428 #[test]
429 fn ident4() {
430 let ident = parse_expr(
431 r#"
432 true(true)
433 "#,
434 );
435 assert!(ident.is_ok());
437 let ident = parse_expr(
438 r#"
439 if(true)
440 "#,
441 );
442 assert!(ident.is_err());
444 }
445
446 #[test]
447 fn ident5() {
448 let ident = parse_expr(
449 r#"
450 {true : false}
451 "#,
452 );
453 assert!(ident.is_ok());
456 let ident = parse_expr(
457 r#"
458 { if : true }
459 "#,
460 );
461 assert!(ident.is_ok());
463 }
464
465 #[test]
466 fn ident6() {
467 let ident = parse_expr(
468 r#"
469 {true : false} has false
470 "#,
471 );
472 assert!(ident.is_ok());
475 let ident = parse_expr(
476 r#"
477 { if : true } has if
478 "#,
479 );
480 assert!(ident.is_ok());
482 }
483
484 #[test]
485 fn comments_has() {
486 let policy_text = r#"
488 permit(principal, action,resource)
489 when{ principal //comment p
490 has //comment has
491 age //comment
492 };
493 "#;
494 let policy = parse_policy(policy_text);
495 assert!(policy.is_ok());
496 }
497
498 #[test]
499 fn comments_like() {
500 let policy_text = r#"
502 permit(principal, action,resource)
503 when{ principal //comment p
504 like //comment like
505
506 age //comment
507 };
508 "#;
509 let policy = parse_policy(policy_text);
510 assert!(policy.is_ok());
511 }
512
513 #[test]
514 fn comments_and() {
515 let policy_text = r#"
517 permit(principal, action,resource)
518 when{ 1 //comment p
519 && //comment &&
520 //comment &&
521 "hello" //comment
522 };
523 "#;
524 let policy = parse_policy(policy_text);
525 assert!(policy.is_ok());
526 }
527
528 #[test]
529 fn comments_or() {
530 let policy_text = r#"
532 permit(principal, action,resource)
533 when{ 1 //comment 1
534 // comment 1
535 || //comment ||
536 //comments ||
537 "hello" //comment
538 //comment hello
539 };
540 "#;
541 let policy = parse_policy(policy_text);
542 assert!(policy.is_ok());
543 }
544
545 #[test]
546 fn comments_add() {
547 let policy_text = r#"
549 permit(principal, action,resource)
550 when{ 1 //comment 1
551 //comment 1_2
552 + //comment +
553 //comment +
554 2 //comment 2
555 //comment 2
556 };
557 "#;
558 let policy = parse_policy(policy_text);
559 assert!(policy.is_ok());
560 }
561
562 #[test]
563 fn comments_paren() {
564 let policy_text = r#"
566 permit(principal, action,resource)
567 when{
568 ( //comment 1
569 ( //comment 2
570 1
571 ) //comment 3
572 ) //comment 4
573 };
574 "#;
575 let policy = parse_policy(policy_text);
576 assert!(policy.is_ok());
577 }
578
579 #[test]
580 fn comments_set() {
581 let policy_text = r#"
583 permit(principal, action,resource)
584 when{
585 [ // comment 1
586 "hello" //comment 2
587 , // comment 3
588 // comment 3-2
589 1 //comment 4
590 //comment 5
591 ] //comment 5-0
592
593 . //comment 5-1
594
595 contains //comment 5-2
596
597 ( //comment 6
598
599 "a" //comment 7
600
601 ) //comment 20
602 };
603 "#;
604 let policy = parse_policy(policy_text);
605 assert!(policy.is_ok());
606 }
607
608 #[test]
609 fn comments_if() {
610 let policy_text = r#"
612 permit(principal, action,resource)
613 when{
614 ( //comment open outer
615 ( //comment open inner
616 if //comment if
617 1 //comment
618 < //comment <
619 2 //commment 2
620 then // comment then
621 "hello" //comment hello
622 else //comment else
623 1 //comment 1
624 ) //comment close inner
625 ) //comment close outer
626 };
627 "#;
628 let policy = parse_policy(policy_text);
629 assert!(policy.is_ok());
630 }
631
632 #[test]
633 fn comments_member_access() {
634 let policy_text = r#"
636 permit(principal, action,resource)
637 when{ principal. //comment .
638 age // comment age
639 };
640 "#;
641 let policy = parse_policy(policy_text);
642 assert!(policy.is_ok());
643 }
644
645 #[test]
646 fn comments_principal() {
647 let policy_text = r#"
649 permit(principal //comment 1
650 ==
651 User::"alice" //comment 3
652 , //comment 4
653 action,resource);
654 "#;
655 let policy = parse_policy(policy_text);
656 assert!(policy.is_ok());
657 }
658
659 #[test]
660 fn comments_annotation() {
661 let policy_text = r#"
663 //comment policy
664 // comment policy 2
665 @anno("good annotation") // comments after annotation
666 // comments after annotation 2
667 permit(principal //comment 1
668 ==
669 User::"alice" //comment 3
670 , //comment 4
671 action,resource);
672 "#;
673 let policy = parse_policy(policy_text);
674 assert!(policy.is_ok());
675 }
676
677 #[test]
678 fn comments_policy() {
679 let policy_text = r#"
681 //comment policy 1
682 //comment policy 2
683 permit( //comment 3
684 // comment 4
685 principal //comment principal
686 == //comment == 1
687 //comment == 2
688 User::"alice" //comment alice
689 , //comment comma 1
690 //comment comma 2
691 action //comment action 1
692 //comment action 2
693 , //comment comma action
694 resource // comment resource
695 )
696 //comment 5
697 //comment 6
698 ;
699 "#;
700 let policy = parse_policy(policy_text);
701 assert!(policy.is_ok());
702 let policy = parse_policy(
704 r#" /* multi-line
705 comment */
706 permit(principal, action, resource)
707 when{
708 one.two
709 };
710 "#,
711 );
712 assert!(policy.is_err());
713 let expr = parse_expr(
714 r#"
715 1 /* multi-line
716 comment */d
717 "#,
718 );
719 assert!(expr.is_err());
720 }
721
722 #[test]
723 fn no_comments_policy() {
724 let policy_text = r#"
726 permit(
727 principal
728 ==
729 User::"alice"
730 ,
731 action
732
733 ,
734 resource
735 )
736 ;
737 "#;
738 let policy = parse_policy(policy_text);
739 assert!(policy.is_ok());
740 }
741
742 #[test]
743 fn no_comments_policy2() {
744 let policy_text = r#"permit (
745 principal == IAM::Principal::"arn:aws:iam::12345678901:user/Dave",
746 action == S3::Action::"GetAccountPublicAccessBlock",
747 resource == Account::"12345678901"
748 );"#;
749 let policy = parse_policy(policy_text);
750 assert!(policy.is_ok());
751 }
752
753 #[test]
754 fn no_comments_policy4() {
755 let policy_text = r#"
756 permit(principal,action,resource,context)
757 when {
758 context.contains(3,"four",five(6,7))
759};"#;
760 let policy = parse_policy(policy_text);
761 assert!(policy.is_ok());
762 }
763 #[test]
764 fn no_comments_policy5() {
765 let policy_text = r#"
766 permit (
767 principal,
768 action,
769 resource == Album::{uid: "772358b3-de11-42dc-8681-f0a32e34aab8",
770 displayName: "vacation_photos"}
771);"#;
772 let policy = parse_policy(policy_text);
773 assert!(policy.is_ok());
774 }
775
776 #[test]
777 fn policies1() {
778 let policy = parse_policy(
779 r#"
780 permit(principal:p,action:a,resource:r)when{w}unless{u}advice{"doit"};
781 "#,
782 );
783 assert!(policy.is_ok());
784 }
785
786 #[test]
787 fn policies2() {
788 let result = parse_policies(
789 r#"
790 permit(
791 principal in Group::"jane_friends", // Policy c1
792 action in [PhotoOp::"view", PhotoOp::"comment"],
793 resource in Album::"jane_trips",
794 context:Group
795 );
796 "#,
797 );
798 assert!(result.is_ok());
799 }
800
801 #[test]
802 fn policies3() {
803 assert!(parse_policies(
804 r#"
805 forbid(principal, action, resource) // Policy c2
806 when { "private" in resource.tags } // resource.tags is a set of strings
807 unless { resource in user.account };
808 "#
809 )
810 .expect("parse fail")
812 .node
813 .expect("no data")
814 .0
815 .iter()
816 .all(|p| p.node.is_some()));
817 }
818
819 #[test]
820 fn policies3p() {
823 assert!(parse_policies(
824 r#"
825 forbid(principality, action, resource) // Policy c2
826 when { "private" in resource.tags } // resource.tags is a set of strings
827 unless { resource in user.account };
828 "#
829 )
830 .expect("parse fail")
832 .node
833 .expect("no data")
834 .0
835 .iter()
836 .all(|p| p.node.is_some()));
837 }
838
839 #[test]
840 fn policies4() {
841 let result = parse_policies(
842 r#"
843 permit(principal:p,action:a,resource:r)when{w}unless{u}advice{"doit"};
844
845 permit(principal in Group::"jane_friends", // Policy c1
846 action in [PhotoOp::"view", PhotoOp::"comment"],
847 resource in Album::"jane_trips");
848
849 forbid(principal, action, resource) // Policy c2
850 when { "private" in resource.tags } // resource.tags is a set of strings
851 unless { resource in user.account };
852 "#,
853 )
854 .expect("parse fail")
855 .node
856 .expect("no data");
857 assert!(result.0.iter().all(|p| p.node.is_some()));
858 }
859
860 #[test]
861 fn policies5() {
862 assert!(parse_policies(
863 r#"
864 permit (
865 principal == User::"alice",
866 action in PhotoflashRole::"viewer",
867 resource in Account::"jane"
868 )
869 advice {
870 "{\"type\":\"PhotoFilterInstruction\", \"anonymize\":true}"
871 };
872 "#
873 )
874 .expect("parse fail")
875 .node
876 .expect("no data")
877 .0
878 .into_iter()
879 .all(|p| p.node.is_some()));
880 }
881
882 #[test]
883 fn policies6() {
884 let policies = POLICIES_PARSER.with(|p| {
886 p.parse(
887 &mut Vec::new(),
888 r#"
889 // use a number to error
890 3(principal:p,action:a,resource:r)when{w}unless{u}advice{"doit"};
891 permit(principal:p,action:a,resource:r)when{w}unless{u}advice{"doit"};
892 permit(principal:p,action:a,resource:r)when{w}unless{u}advice{"doit"};
893 "#,
894 )
895 .expect("parser error")
896 .node
897 .expect("no data")
898 });
899 let success = policies
900 .0
901 .into_iter()
902 .filter_map(|p| p.node)
903 .collect::<Vec<_>>();
904 assert!(success.len() == 2);
905 }
906
907 #[test]
908 fn policy_annotations() {
909 let policies = parse_policies(
910 r#"
911 @anno("good annotation") permit (principal, action, resource);
912 @anno1("good")@anno2("annotation") permit (principal, action, resource);
913 @long6wordphraseisident007("good annotation") permit (principal, action, resource);
914 @ spacy ( " good annotation " ) permit (principal, action, resource);
915 "#,
916 )
917 .expect("parse fail")
918 .node
919 .expect("no data");
920 let success = policies
921 .0
922 .into_iter()
923 .filter_map(|p| p.node)
924 .collect::<Vec<_>>();
925 assert!(success.len() == 4);
926
927 let _policy = parse_policy(
928 r#"
929 @bad-annotation("bad") permit (principal, action, resource);
930 "#,
931 )
932 .expect_err("should fail on dash");
933
934 let _policy = parse_policy(
935 r#"
936 @bad_annotation("bad","annotation") permit (principal, action, resource);
937 "#,
938 )
939 .expect_err("should fail on list");
940
941 let _policy = parse_policy(
942 r#"
943 @bad_annotation(bad_annotation) permit (principal, action, resource);
944 "#,
945 )
946 .expect_err("should fail without string");
947
948 let _policy = parse_policy(
949 r#"
950 permit (@comment("your name here") principal, action, resource);
951 "#,
952 )
953 .expect_err("should fail with poor placement");
954 }
955
956 #[test]
957 fn parse_idempotent() {
958 let many_policies =
959 std::fs::read_to_string("src/parser/testfiles/policies.txt").expect("missing file");
960 let cst1 = parse_policies(&many_policies)
961 .expect("parse fail")
962 .node
963 .expect("no data");
964 let revert = format!("{}", cst1);
965 let cst2 = parse_policies(&revert)
967 .expect("parse fail")
968 .node
969 .expect("no data");
970 assert!(cst1 == cst2);
971 }
972}