@@ -33,6 +33,11 @@ type CompiledRule struct {
3333 matches []* CompiledMatch
3434}
3535
36+ // SourceID returns the source metadata identifier associated with the compiled rule.
37+ func (r * CompiledRule ) SourceID () int64 {
38+ return r .ID ().ID
39+ }
40+
3641// ID returns the expression id associated with the rule.
3742func (r * CompiledRule ) ID () * ValueString {
3843 return r .id
@@ -50,11 +55,17 @@ func (r *CompiledRule) Matches() []*CompiledMatch {
5055
5156// CompiledVariable represents the variable name, expression, and associated type-check declaration.
5257type CompiledVariable struct {
58+ id int64
5359 name string
5460 expr * cel.Ast
5561 varDecl * decls.VariableDecl
5662}
5763
64+ // SourceID returns the source metadata identifier associated with the variable.
65+ func (v * CompiledVariable ) SourceID () int64 {
66+ return v .id
67+ }
68+
5869// Name returns the variable name.
5970func (v * CompiledVariable ) Name () string {
6071 return v .name
@@ -110,12 +121,28 @@ func (o *OutputValue) Expr() *cel.Ast {
110121 return o .expr
111122}
112123
124+ // CompilerOption specifies a functional option to be applied to new RuleComposer instances.
125+ type CompilerOption func (* compiler ) error
126+
127+ // MaxNestedExpressions limits the number of variable and nested rule expressions during compilation.
128+ //
129+ // Defaults to 100 if not set.
130+ func MaxNestedExpressions (limit int ) CompilerOption {
131+ return func (c * compiler ) error {
132+ if limit <= 0 {
133+ return fmt .Errorf ("nested expression limit must be non-negative, non-zero value: %d" , limit )
134+ }
135+ c .maxNestedExpressions = limit
136+ return nil
137+ }
138+ }
139+
113140// Compile combines the policy compilation and composition steps into a single call.
114141//
115142// This generates a single CEL AST from a collection of policy expressions associated with a
116143// CEL environment.
117- func Compile (env * cel.Env , p * Policy ) (* cel.Ast , * cel.Issues ) {
118- rule , iss := CompileRule (env , p )
144+ func Compile (env * cel.Env , p * Policy , opts ... CompilerOption ) (* cel.Ast , * cel.Issues ) {
145+ rule , iss := CompileRule (env , p , opts ... )
119146 if iss .Err () != nil {
120147 return nil , iss
121148 }
@@ -126,14 +153,21 @@ func Compile(env *cel.Env, p *Policy) (*cel.Ast, *cel.Issues) {
126153// CompileRule creates a compiled rules from the policy which contains a set of compiled variables and
127154// match statements. The compiled rule defines an expression graph, which can be composed into a single
128155// expression via the RuleComposer.Compose method.
129- func CompileRule (env * cel.Env , p * Policy ) (* CompiledRule , * cel.Issues ) {
156+ func CompileRule (env * cel.Env , p * Policy , opts ... CompilerOption ) (* CompiledRule , * cel.Issues ) {
130157 c := & compiler {
131- env : env ,
132- info : p .SourceInfo (),
133- src : p .Source (),
158+ env : env ,
159+ info : p .SourceInfo (),
160+ src : p .Source (),
161+ maxNestedExpressions : defaultMaxNestedExpressions ,
134162 }
135163 errs := common .NewErrors (c .src )
136164 iss := cel .NewIssuesWithSourceInfo (errs , c .info )
165+ for _ , o := range opts {
166+ if err := o (c ); err != nil {
167+ iss .ReportErrorAtID (p .Name ().ID , "error configuring compiler option: %s" , err )
168+ return nil , iss
169+ }
170+ }
137171 rule , ruleIss := c .compileRule (p .Rule (), c .env , iss )
138172 iss = iss .Append (ruleIss )
139173 return rule , iss
@@ -143,6 +177,9 @@ type compiler struct {
143177 env * cel.Env
144178 info * ast.SourceInfo
145179 src * Source
180+
181+ maxNestedExpressions int
182+ nestedCount int
146183}
147184
148185func (c * compiler ) compileRule (r * Rule , ruleEnv * cel.Env , iss * cel.Issues ) (* CompiledRule , * cel.Issues ) {
@@ -170,12 +207,22 @@ func (c *compiler) compileRule(r *Rule, ruleEnv *cel.Env, iss *cel.Issues) (*Com
170207 if err != nil {
171208 iss .ReportErrorAtID (v .Expression ().ID , "invalid variable declaration" )
172209 }
173- compiledVars [i ] = & CompiledVariable {
210+ compiledVar := & CompiledVariable {
211+ id : v .name .ID ,
174212 name : v .name .Value ,
175213 expr : varAST ,
176214 varDecl : varDecl ,
177215 }
216+ compiledVars [i ] = compiledVar
217+
218+ // Increment the nesting count post-compile.
219+ c .nestedCount ++
220+ if c .nestedCount == c .maxNestedExpressions + 1 {
221+ iss .ReportErrorAtID (compiledVar .SourceID (), "variable exceeds nested expression limit" )
222+ }
178223 }
224+
225+ // Compile the set of match conditions under the rule.
179226 compiledMatches := []* CompiledMatch {}
180227 for _ , m := range r .Matches () {
181228 condSrc := c .relSource (m .Condition ())
@@ -208,6 +255,12 @@ func (c *compiler) compileRule(r *Rule, ruleEnv *cel.Env, iss *cel.Issues) (*Com
208255 cond : condAST ,
209256 nestedRule : nestedRule ,
210257 })
258+
259+ // Increment the nesting count post-compile.
260+ c .nestedCount ++
261+ if c .nestedCount == c .maxNestedExpressions + 1 {
262+ iss .ReportErrorAtID (nestedRule .SourceID (), "rule exceeds nested expression limit" )
263+ }
211264 }
212265 }
213266 return & CompiledRule {
@@ -232,4 +285,6 @@ func (c *compiler) relSource(pstr ValueString) *RelativeSource {
232285const (
233286 // Consider making the variables namespace configurable.
234287 variablePrefix = "variables"
288+
289+ defaultMaxNestedExpressions = 100
235290)
0 commit comments