parse `result` rule
(res rego.ResultSet, policy *engine.Policy, rawData *engine.RawData)
| 221 | |
| 222 | // parse `result` rule |
| 223 | func parseResultRule(res rego.ResultSet, policy *engine.Policy, rawData *engine.RawData) (*engine.EvaluationResult, error) { |
| 224 | result := &engine.EvaluationResult{Violations: make([]*engine.PolicyViolation, 0)} |
| 225 | result.RawData = rawData |
| 226 | for _, exp := range res { |
| 227 | for _, val := range exp.Expressions { |
| 228 | ruleResult, ok := val.Value.(map[string]any) |
| 229 | if !ok { |
| 230 | return nil, engine.ResultFormatError{Field: mainRule} |
| 231 | } |
| 232 | |
| 233 | var skipped bool |
| 234 | if val, ok := ruleResult["skipped"].(bool); ok { |
| 235 | skipped = val |
| 236 | } |
| 237 | |
| 238 | var reason string |
| 239 | if val, ok := ruleResult["skip_reason"].(string); ok { |
| 240 | reason = val |
| 241 | } |
| 242 | |
| 243 | var ignore bool |
| 244 | if val, ok := ruleResult["ignore"].(bool); ok { |
| 245 | ignore = val |
| 246 | } |
| 247 | |
| 248 | result.Skipped = skipped |
| 249 | result.SkipReason = reason |
| 250 | result.Ignore = ignore |
| 251 | |
| 252 | // Prefer non-empty "findings" (structured objects) over "violations" for backward compatibility. |
| 253 | findingsRaw, _ := ruleResult["findings"].([]any) |
| 254 | if len(findingsRaw) > 0 { |
| 255 | for _, f := range findingsRaw { |
| 256 | obj, ok := f.(map[string]any) |
| 257 | if !ok { |
| 258 | return nil, fmt.Errorf("finding must be an object, got %T", f) |
| 259 | } |
| 260 | pv, err := engine.NewStructuredViolation(policy.Name, obj) |
| 261 | if err != nil { |
| 262 | return nil, fmt.Errorf("structured finding in policy %q: %w", policy.Name, err) |
| 263 | } |
| 264 | result.Violations = append(result.Violations, pv) |
| 265 | } |
| 266 | } else if violations, ok := ruleResult["violations"].([]any); ok { |
| 267 | // Fallback: violations (strings or deprecated structured objects). |
| 268 | // TODO: remove structured object support once policies are fully migrated to findings. |
| 269 | for _, violation := range violations { |
| 270 | switch v := violation.(type) { |
| 271 | case string: |
| 272 | result.Violations = append(result.Violations, &engine.PolicyViolation{Subject: policy.Name, Violation: v}) |
| 273 | case map[string]any: |
| 274 | pv, err := engine.NewStructuredViolation(policy.Name, v) |
| 275 | if err != nil { |
| 276 | return nil, fmt.Errorf("structured violation in policy %q: %w", policy.Name, err) |
| 277 | } |
| 278 | result.Violations = append(result.Violations, pv) |
| 279 | default: |
| 280 | return nil, fmt.Errorf("violation must be a string or object, got %T", violation) |
no test coverage detected