1515package dev .cel .policy ;
1616
1717import static com .google .common .base .Preconditions .checkNotNull ;
18+ import static com .google .common .collect .ImmutableSet .toImmutableSet ;
1819import static dev .cel .common .formats .YamlHelper .ERROR ;
1920import static dev .cel .common .formats .YamlHelper .assertRequiredFields ;
2021import static dev .cel .common .formats .YamlHelper .assertYamlType ;
2829import dev .cel .common .formats .YamlParserContextImpl ;
2930import dev .cel .common .internal .CelCodePointArray ;
3031import dev .cel .policy .CelPolicy .Import ;
32+ import dev .cel .policy .CelPolicy .Invariant ;
3133import dev .cel .policy .CelPolicy .Match ;
3234import dev .cel .policy .CelPolicy .Match .Result ;
3335import dev .cel .policy .CelPolicy .Variable ;
@@ -47,6 +49,8 @@ final class CelPolicyYamlParser implements CelPolicyParser {
4749 Match .newBuilder (0 ).setCondition (ERROR_VALUE ).setResult (Result .ofOutput (ERROR_VALUE )).build ();
4850 private static final Variable ERROR_VARIABLE =
4951 Variable .newBuilder ().setExpression (ERROR_VALUE ).setName (ERROR_VALUE ).build ();
52+ private static final Invariant ERROR_INVARIANT =
53+ Invariant .newBuilder (0 ).setInvariantId (ERROR_VALUE ).setAssertClause (ERROR_VALUE ).build ();
5054
5155 private final TagVisitor <Node > tagVisitor ;
5256 private final boolean enableSimpleVariables ;
@@ -137,17 +141,82 @@ public CelPolicy parsePolicy(PolicyParserContext<Node> ctx, Node node) {
137141 case "rule" :
138142 policyBuilder .setRule (parseRule (ctx , policyBuilder , valueNode ));
139143 break ;
144+ case "verification" :
145+ parseVerification (policyBuilder , ctx , valueNode );
146+ break ;
140147 default :
141148 tagVisitor .visitPolicyTag (ctx , keyId , fieldName , valueNode , policyBuilder );
142149 break ;
143150 }
144151 }
145152
153+ ImmutableSet <String > ruleVarNames =
154+ policyBuilder .rule ().variables ().stream ()
155+ .map (CelPolicy .Variable ::name )
156+ .filter (name -> !name .equals (ERROR_VALUE ))
157+ .map (ValueString ::value )
158+ .collect (toImmutableSet ());
159+ for (Variable verVar : policyBuilder .verificationVariables ()) {
160+ if (!verVar .name ().equals (ERROR_VALUE )
161+ && ruleVarNames .contains (verVar .name ().value ())) {
162+ ctx .reportError (
163+ verVar .name ().id (),
164+ "Duplicate variable name '"
165+ + verVar .name ().value ()
166+ + "' in verification.variables; already defined in rule.variables" );
167+ }
168+ }
169+
146170 return policyBuilder
147171 .setPolicySource (policySource .toBuilder ().setPositionsMap (ctx .getIdToOffsetMap ()).build ())
148172 .build ();
149173 }
150174
175+ private void parseVerification (
176+ CelPolicy .Builder policyBuilder , PolicyParserContext <Node > ctx , Node node ) {
177+ long id = ctx .collectMetadata (node );
178+ if (!assertYamlType (ctx , id , node , YamlNodeType .MAP )) {
179+ return ;
180+ }
181+ MappingNode mappingNode = (MappingNode ) node ;
182+ for (NodeTuple nodeTuple : mappingNode .getValue ()) {
183+ Node key = nodeTuple .getKeyNode ();
184+ long keyId = ctx .collectMetadata (key );
185+ if (!assertYamlType (ctx , keyId , key , YamlNodeType .STRING , YamlNodeType .TEXT )) {
186+ continue ;
187+ }
188+ String fieldName = ((ScalarNode ) key ).getValue ();
189+ Node valueNode = nodeTuple .getValueNode ();
190+ switch (fieldName ) {
191+ case "invariants" : {
192+ long valueId = ctx .collectMetadata (valueNode );
193+ if (!assertYamlType (ctx , valueId , valueNode , YamlNodeType .LIST )) {
194+ continue ;
195+ }
196+ SequenceNode invariantListNode = (SequenceNode ) valueNode ;
197+ for (Node invariantNode : invariantListNode .getValue ()) {
198+ policyBuilder .addInvariant (parseInvariant (ctx , policyBuilder , invariantNode ));
199+ }
200+ break ;
201+ }
202+ case "variables" : {
203+ long valueId = ctx .collectMetadata (valueNode );
204+ if (!assertYamlType (ctx , valueId , valueNode , YamlNodeType .LIST )) {
205+ continue ;
206+ }
207+ SequenceNode variableListNode = (SequenceNode ) valueNode ;
208+ for (Node varNode : variableListNode .getValue ()) {
209+ policyBuilder .addVerificationVariable (parseVariable (ctx , policyBuilder , varNode ));
210+ }
211+ break ;
212+ }
213+ default :
214+ ctx .reportError (keyId , "Unexpected key in verification block: " + fieldName );
215+ break ;
216+ }
217+ }
218+ }
219+
151220 private void parseImports (
152221 CelPolicy .Builder policyBuilder , PolicyParserContext <Node > ctx , Node node ) {
153222 long id = ctx .collectMetadata (node );
@@ -409,6 +478,82 @@ private Variable parseVariableObject(
409478 return builder .build ();
410479 }
411480
481+ @ Override
482+ public CelPolicy .Invariant parseInvariant (
483+ PolicyParserContext <Node > ctx , CelPolicy .Builder policyBuilder , Node node ) {
484+ long id = ctx .collectMetadata (node );
485+ Invariant .Builder builder = Invariant .newBuilder (id );
486+ if (!assertYamlType (ctx , id , node , YamlNodeType .MAP )) {
487+ return ERROR_INVARIANT ;
488+ }
489+
490+ MappingNode invariantMap = (MappingNode ) node ;
491+ for (NodeTuple nodeTuple : invariantMap .getValue ()) {
492+ Node keyNode = nodeTuple .getKeyNode ();
493+ long keyId = ctx .collectMetadata (keyNode );
494+ if (!assertYamlType (ctx , keyId , keyNode , YamlNodeType .STRING , YamlNodeType .TEXT )) {
495+ continue ;
496+ }
497+ Node valueNode = nodeTuple .getValueNode ();
498+ String keyName = ((ScalarNode ) keyNode ).getValue ();
499+ switch (keyName ) {
500+ case "id" :
501+ builder .setInvariantId (ctx .newYamlString (valueNode ));
502+ break ;
503+ case "description" :
504+ builder .setDescription (ctx .newYamlString (valueNode ));
505+ break ;
506+ case "assume" : {
507+ if (!assertYamlType (
508+ ctx ,
509+ ctx .collectMetadata (valueNode ),
510+ valueNode ,
511+ YamlNodeType .STRING ,
512+ YamlNodeType .TEXT ,
513+ YamlNodeType .LIST )) {
514+ break ;
515+ }
516+ if (valueNode instanceof SequenceNode ) {
517+ for (Node itemNode : ((SequenceNode ) valueNode ).getValue ()) {
518+ builder .addAssume (ctx .newSourceString (itemNode ));
519+ }
520+ } else {
521+ builder .addAssume (ctx .newSourceString (valueNode ));
522+ }
523+ break ;
524+ }
525+ case "assert" : {
526+ if (!assertYamlType (
527+ ctx ,
528+ ctx .collectMetadata (valueNode ),
529+ valueNode ,
530+ YamlNodeType .STRING ,
531+ YamlNodeType .TEXT ,
532+ YamlNodeType .LIST )) {
533+ break ;
534+ }
535+ if (valueNode instanceof SequenceNode ) {
536+ for (Node itemNode : ((SequenceNode ) valueNode ).getValue ()) {
537+ builder .addAssertClause (ctx .newSourceString (itemNode ));
538+ }
539+ } else {
540+ builder .addAssertClause (ctx .newSourceString (valueNode ));
541+ }
542+ break ;
543+ }
544+ default :
545+ ctx .reportError (keyId , "Unexpected key in invariant block: " + keyName );
546+ break ;
547+ }
548+ }
549+
550+ if (!assertRequiredFields (ctx , id , builder .getMissingRequiredFieldNames ())) {
551+ return ERROR_INVARIANT ;
552+ }
553+
554+ return builder .build ();
555+ }
556+
412557 private ParserImpl (
413558 TagVisitor <Node > tagVisitor ,
414559 boolean enableSimpleVariables ,
0 commit comments