@@ -20,7 +20,6 @@ import (
2020 "context"
2121 "encoding/json"
2222 "fmt"
23- "strings"
2423
2524 "github.com/chainloop-dev/chainloop/pkg/policies/engine"
2625 "github.com/open-policy-agent/opa/ast"
@@ -92,8 +91,8 @@ const (
9291 EnvironmentModePermissive EnvironmentMode = 1
9392 inputArgs = "args"
9493 inputElements = "elements"
95- deprecatedRule = "violations"
96- mainRule = "result"
94+ deprecatedRule = ". violations"
95+ mainRule = ". result"
9796)
9897
9998// builtinFuncNotAllowed is a list of builtin functions that are not allowed in the compiler
@@ -113,12 +112,6 @@ func (r *Engine) Verify(ctx context.Context, policy *engine.Policy, input []byte
113112 return nil , fmt .Errorf ("failed to parse rego policy: %w" , err )
114113 }
115114
116- // Extract package name
117- pkgPath := parsedModule .Package .Path .String ()
118- if pkgPath == "" {
119- return nil , fmt .Errorf ("policy has no package declaration" )
120- }
121-
122115 // Decode input as json
123116 decoder := json .NewDecoder (bytes .NewReader (input ))
124117 decoder .UseNumber ()
@@ -134,7 +127,7 @@ func (r *Engine) Verify(ctx context.Context, policy *engine.Policy, input []byte
134127 decodedInput = inputMap
135128 }
136129
137- // put arguments embedded in the input
130+ // put arguments embedded in the input object
138131 if args != nil {
139132 inputMap , ok := decodedInput .(map [string ]interface {})
140133 if ! ok {
@@ -144,72 +137,59 @@ func (r *Engine) Verify(ctx context.Context, policy *engine.Policy, input []byte
144137 decodedInput = inputMap
145138 }
146139
147- // Prepare rego options
148- options := []func (* rego.Rego ){
149- rego .ParsedModule (parsedModule ),
150- rego .Input (decodedInput ),
151- rego .Capabilities (r .Capabilities ()),
152- }
140+ // add input
141+ regoInput := rego .Input (decodedInput )
142+
143+ // add module
144+ regoFunc := rego .ParsedModule (parsedModule )
153145
154- if r .operatingMode == EnvironmentModeRestrictive {
155- options = append (options , rego .StrictBuiltinErrors (true ))
146+ var res rego.ResultSet
147+ // Function to execute the query with appropriate parameters
148+ executeQuery := func (rule string , strict bool ) error {
149+ if strict {
150+ res , err = queryRego (ctx , rule , parsedModule , regoInput , regoFunc , rego .Capabilities (r .Capabilities ()), rego .StrictBuiltinErrors (true ))
151+ } else {
152+ res , err = queryRego (ctx , rule , parsedModule , regoInput , regoFunc , rego .Capabilities (r .Capabilities ()))
153+ }
154+ return err
156155 }
157156
158- // First try evaluating the entire package
159- res , err := r .evaluatePackage (ctx , pkgPath , options )
160- if err != nil {
157+ // Get raw results first
158+ if err := executeQuery ("" , r .operatingMode == EnvironmentModeRestrictive ); err != nil {
161159 return nil , err
162160 }
163161
164- // If we got results, parse them
165- if res != nil {
166- return parseResultRule (res , policy , decodedInput )
162+ rawData := & engine. RawData {
163+ Input : decodedInput ,
164+ Output : regoResultSetToRawResults (res ),
167165 }
168166
169- // Fallback to evaluating specific rules if package evaluation returned nothing
170- return r .evaluateSpecificRules (ctx , parsedModule , options , policy , decodedInput )
171- }
172-
173- // Evaluate the entire package by querying data.<package>
174- func (r * Engine ) evaluatePackage (ctx context.Context , pkgPath string , options []func (* rego.Rego )) (rego.ResultSet , error ) {
175- query := fmt .Sprintf ("data.%s" , strings .TrimPrefix (pkgPath , "data." ))
176- regoEval := rego .New (append (options , rego .Query (query ))... )
177- return regoEval .Eval (ctx )
178- }
179-
180- func (r * Engine ) evaluateSpecificRules (ctx context.Context , module * ast.Module , options []func (* rego.Rego ), policy * engine.Policy , decodedInput interface {}) (* engine.EvaluationResult , error ) {
181- // Try the result rule first
182- res , err := queryRego (ctx , mainRule , module , options ... )
183- if err != nil {
167+ // Try the main rule first
168+ if err := executeQuery (mainRule , r .operatingMode == EnvironmentModeRestrictive ); err != nil {
184169 return nil , err
185170 }
186171
187- if res != nil {
188- return parseResultRule (res , policy , decodedInput )
189- }
172+ // If res is nil, it means that the rule hasn't been found
173+ // TODO: Remove when this deprecated rule is not used anymore
174+ if res == nil {
175+ // Try with the deprecated main rule
176+ if err := executeQuery (deprecatedRule , r .operatingMode == EnvironmentModeRestrictive ); err != nil {
177+ return nil , err
178+ }
190179
191- // Fallback to deprecated violations rule
192- res , err = queryRego (ctx , deprecatedRule , module , options ... )
193- if err != nil {
194- return nil , err
195- }
180+ if res == nil {
181+ return nil , fmt .Errorf ("failed to evaluate policy: neither '%s' nor '%s' rule found" , mainRule , deprecatedRule )
182+ }
196183
197- if res != nil {
198- return parseViolationsRule (res , policy , decodedInput )
184+ return parseViolationsRule (res , policy , rawData )
199185 }
200186
201- return nil , fmt .Errorf ("failed to evaluate policy: no results from package or rule evaluation" )
202- }
203-
204- func queryRego (ctx context.Context , ruleName string , module * ast.Module , options ... func (* rego.Rego )) (rego.ResultSet , error ) {
205- query := fmt .Sprintf ("data.%s.%s" , strings .TrimPrefix (module .Package .Path .String (), "data." ), ruleName )
206- regoEval := rego .New (append (options , rego .Query (query ))... )
207- return regoEval .Eval (ctx )
187+ return parseResultRule (res , policy , rawData )
208188}
209189
210190// Parse deprecated list of violations.
211191// TODO: Remove this path once `result` rule is consolidated
212- func parseViolationsRule (res rego.ResultSet , policy * engine.Policy , decodedInput interface {} ) (* engine.EvaluationResult , error ) {
192+ func parseViolationsRule (res rego.ResultSet , policy * engine.Policy , rawData * engine. RawData ) (* engine.EvaluationResult , error ) {
213193 violations := make ([]* engine.PolicyViolation , 0 )
214194 for _ , exp := range res {
215195 for _ , val := range exp .Expressions {
@@ -237,20 +217,14 @@ func parseViolationsRule(res rego.ResultSet, policy *engine.Policy, decodedInput
237217 Skipped : false , // best effort
238218 SkipReason : "" ,
239219 Ignore : false , // Assume old rules should not be ignored
240- RawData : & engine.RawData {
241- Input : decodedInput ,
242- Output : regoResultSetToRawResults (res ),
243- },
220+ RawData : rawData ,
244221 }, nil
245222}
246223
247224// parse `result` rule
248- func parseResultRule (res rego.ResultSet , policy * engine.Policy , decodedInput interface {} ) (* engine.EvaluationResult , error ) {
225+ func parseResultRule (res rego.ResultSet , policy * engine.Policy , rawData * engine. RawData ) (* engine.EvaluationResult , error ) {
249226 result := & engine.EvaluationResult {Violations : make ([]* engine.PolicyViolation , 0 )}
250- result .RawData = & engine.RawData {
251- Input : decodedInput ,
252- Output : regoResultSetToRawResults (res ),
253- }
227+ result .RawData = rawData
254228 for _ , exp := range res {
255229 for _ , val := range exp .Expressions {
256230 ruleResult , ok := val .Value .(map [string ]any )
@@ -295,6 +269,17 @@ func parseResultRule(res rego.ResultSet, policy *engine.Policy, decodedInput int
295269 return result , nil
296270}
297271
272+ func queryRego (ctx context.Context , ruleName string , parsedModule * ast.Module , options ... func (r * rego.Rego )) (rego.ResultSet , error ) {
273+ query := rego .Query (fmt .Sprintf ("%v%s\n " , parsedModule .Package .Path , ruleName ))
274+ regoEval := rego .New (append (options , query )... )
275+ res , err := regoEval .Eval (ctx )
276+ if err != nil {
277+ return nil , fmt .Errorf ("failed to evaluate policy: %w" , err )
278+ }
279+
280+ return res , nil
281+ }
282+
298283// Capabilities returns the capabilities of the environment based on the mode of operation
299284// defaulting to EnvironmentModeRestrictive if not provided.
300285func (r * Engine ) Capabilities () * ast.Capabilities {
0 commit comments