From 801e7043d153f9e47aae556ae9e2553e8be01e96 Mon Sep 17 00:00:00 2001 From: Sylwester Piskozub Date: Thu, 21 Aug 2025 15:59:03 +0200 Subject: [PATCH 1/2] enhance lint output Signed-off-by: Sylwester Piskozub --- app/cli/internal/policydevel/lint.go | 66 +++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/app/cli/internal/policydevel/lint.go b/app/cli/internal/policydevel/lint.go index 73eb655dd..862f0e72b 100644 --- a/app/cli/internal/policydevel/lint.go +++ b/app/cli/internal/policydevel/lint.go @@ -33,6 +33,7 @@ import ( "github.com/open-policy-agent/opa/v1/format" "github.com/styrainc/regal/pkg/config" "github.com/styrainc/regal/pkg/linter" + "github.com/styrainc/regal/pkg/report" "github.com/styrainc/regal/pkg/rules" "gopkg.in/yaml.v3" ) @@ -356,13 +357,39 @@ func (p *PolicyToLint) runRegalLinter(filePath, content string) { return } - // Add any violations to the policy errors + // Parse the Rego AST to map line numbers to rule names + regoRuleMap := p.buildRegoRuleMap(content) + + // Add violations to the policy errors for _, v := range report.Violations { - errorStr := strings.ReplaceAll(v.Description, "`opa fmt`", "`--format`") + errorStr := p.formatViolationError(v, regoRuleMap) p.AddError(filePath, errorStr, v.Location.Row) } } +// Creates a formatted error message from a Regal violation +// Follows format :: [] - +func (p *PolicyToLint) formatViolationError(v report.Violation, regoRuleMap map[int]string) string { + // Extract resources + var resources []string + for _, r := range v.RelatedResources { + resources = append(resources, r.Reference) + } + resourceStr := strings.Join(resources, ", ") + + // Try to identify which Rego rule contains this violation + regoRuleName, exists := regoRuleMap[v.Location.Row] + if !exists { + regoRuleName = "" + } else { + regoRuleName = fmt.Sprintf("[%s]", regoRuleName) + } + + // Format the error message + lintError := fmt.Sprintf("%s: %s - %s", regoRuleName, v.Description, resourceStr) + return strings.ReplaceAll(lintError, "`opa fmt`", "`--format`") +} + // Attempts to load configuration in this order: // 1. User-specified config // 2. Default config @@ -465,3 +492,38 @@ func (p *PolicyToLint) updateEmbeddedRegoInYAML(file *File, rootNode *yaml.Node) return nil } + +// Creates a mapping from line numbers to Rego rule names +func (p *PolicyToLint) buildRegoRuleMap(regoSrc string) map[int]string { + ruleMap := make(map[int]string) + + // Parse the Rego source into AST + module, err := opaAst.ParseModule("", regoSrc) + if err != nil { + return ruleMap // Return empty map if parsing fails + } + + // Walk through the AST to find rule definitions + for _, rule := range module.Rules { + if rule.Location != nil { + ruleName := string(rule.Head.Name) + startLine := rule.Location.Row + endLine := startLine + + // Try to find the end line of the rule + if rule.Body != nil && len(rule.Body) > 0 { + lastExpr := rule.Body[len(rule.Body)-1] + if lastExpr.Location != nil { + endLine = lastExpr.Location.Row + } + } + + // Map all lines within this rule to the rule name + for line := startLine; line <= endLine; line++ { + ruleMap[line] = ruleName + } + } + } + + return ruleMap +} From 358b00824f23d794d777a3e29119025d7197ca67 Mon Sep 17 00:00:00 2001 From: Sylwester Piskozub Date: Thu, 21 Aug 2025 16:20:52 +0200 Subject: [PATCH 2/2] fix linter Signed-off-by: Sylwester Piskozub --- app/cli/internal/policydevel/lint.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/cli/internal/policydevel/lint.go b/app/cli/internal/policydevel/lint.go index 974e28154..fd37695ee 100644 --- a/app/cli/internal/policydevel/lint.go +++ b/app/cli/internal/policydevel/lint.go @@ -304,7 +304,7 @@ func (p *PolicyToLint) runRegalLinter(filePath, content string) { // Follows format :: [] - func (p *PolicyToLint) formatViolationError(v report.Violation, regoRuleMap map[int]string) string { // Extract resources - var resources []string + resources := make([]string, 0, len(v.RelatedResources)) for _, r := range v.RelatedResources { resources = append(resources, r.Reference) } @@ -375,7 +375,8 @@ func (p *PolicyToLint) buildRegoRuleMap(regoSrc string) map[int]string { // Parse the Rego source into AST module, err := opaAst.ParseModule("", regoSrc) if err != nil { - return ruleMap // Return empty map if parsing fails + // Return empty map if parsing fails + return ruleMap } // Walk through the AST to find rule definitions @@ -386,7 +387,7 @@ func (p *PolicyToLint) buildRegoRuleMap(regoSrc string) map[int]string { endLine := startLine // Try to find the end line of the rule - if rule.Body != nil && len(rule.Body) > 0 { + if len(rule.Body) > 0 { lastExpr := rule.Body[len(rule.Body)-1] if lastExpr.Location != nil { endLine = lastExpr.Location.Row