From e35e0994f9669ca1d301f6c9b78ed8bab90c7b99 Mon Sep 17 00:00:00 2001 From: nikpivkin Date: Wed, 2 Oct 2024 15:53:32 +0600 Subject: [PATCH 1/3] feat(misconf): log causes of HCL file parsing errors Signed-off-by: nikpivkin --- pkg/iac/scanners/terraform/parser/parser.go | 50 +++++++++++++++++++ .../scanners/terraform/parser/parser_test.go | 21 ++++++++ 2 files changed, 71 insertions(+) diff --git a/pkg/iac/scanners/terraform/parser/parser.go b/pkg/iac/scanners/terraform/parser/parser.go index 60940d6c6241..72cf6041165d 100644 --- a/pkg/iac/scanners/terraform/parser/parser.go +++ b/pkg/iac/scanners/terraform/parser/parser.go @@ -1,8 +1,10 @@ package parser import ( + "bufio" "context" "errors" + "fmt" "io" "io/fs" "os" @@ -163,6 +165,14 @@ func (p *Parser) ParseFS(ctx context.Context, dir string) error { if p.stopOnHCLError { return err } + var diags hcl.Diagnostics + if errors.As(err, &diags) { + errc := p.showParseErrors(p.moduleFS, path, diags) + if errc == nil { + continue + } + p.logger.Error("Failed to get the causes of the parsing error", log.Err(errc)) + } p.logger.Error("Error parsing file", log.FilePath(path), log.Err(err)) continue } @@ -171,6 +181,46 @@ func (p *Parser) ParseFS(ctx context.Context, dir string) error { return nil } +func (p *Parser) showParseErrors(fsys fs.FS, path string, diags hcl.Diagnostics) error { + file, err := fsys.Open(path) + if err != nil { + return fmt.Errorf("failed to read file: %w", err) + } + defer file.Close() + + for _, diag := range diags { + if subj := diag.Subject; subj != nil { + lines, err := readLinesFromFile(file, subj.Start.Line, subj.End.Line) + if err != nil { + return err + } + + cause := strings.Join(lines, "\n") + p.logger.Error("Error parsing file", log.FilePath(path), + log.String("cause", cause), log.Err(diag)) + } + } + + return nil +} + +func readLinesFromFile(f io.Reader, from, to int) ([]string, error) { + scanner := bufio.NewScanner(f) + rawLines := make([]string, 0, to-from+1) + + for lineNum := 0; scanner.Scan() && lineNum < to; lineNum++ { + if lineNum >= from-1 { + rawLines = append(rawLines, scanner.Text()) + } + } + + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("failed to scan file: %w", err) + } + + return rawLines, nil +} + var ErrNoFiles = errors.New("no files found") func (p *Parser) Load(ctx context.Context) (*evaluator, error) { diff --git a/pkg/iac/scanners/terraform/parser/parser_test.go b/pkg/iac/scanners/terraform/parser/parser_test.go index e3bd817748f6..1e1f98ca21d0 100644 --- a/pkg/iac/scanners/terraform/parser/parser_test.go +++ b/pkg/iac/scanners/terraform/parser/parser_test.go @@ -1884,3 +1884,24 @@ variable "baz" {} assert.Contains(t, buf.String(), "Variable values was not found in the environment or variable files.") assert.Contains(t, buf.String(), "variables=\"foo\"") } + +func TestLogParseErrors(t *testing.T) { + var buf bytes.Buffer + slog.SetDefault(slog.New(log.NewHandler(&buf, nil))) + + src := `resource "aws-s3-bucket" "name" { + bucket = < +}` + + fsys := fstest.MapFS{ + "main.tf": &fstest.MapFile{ + Data: []byte(src), + }, + } + + parser := New(fsys, "") + err := parser.ParseFS(context.TODO(), ".") + require.NoError(t, err) + + assert.Contains(t, buf.String(), `cause=" bucket = <"`) +} From 4d4da058f2969a263d355d4c0d8182a66c26cf72 Mon Sep 17 00:00:00 2001 From: Simar Date: Fri, 22 Nov 2024 18:26:12 -0700 Subject: [PATCH 2/3] fix lint --- pkg/iac/scanners/terraform/parser/parser.go | 33 +++++++++++---------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/pkg/iac/scanners/terraform/parser/parser.go b/pkg/iac/scanners/terraform/parser/parser.go index 72cf6041165d..047d97a89f79 100644 --- a/pkg/iac/scanners/terraform/parser/parser.go +++ b/pkg/iac/scanners/terraform/parser/parser.go @@ -161,28 +161,29 @@ func (p *Parser) ParseFS(ctx context.Context, dir string) error { } sort.Strings(paths) for _, path := range paths { - if err := p.ParseFile(ctx, path); err != nil { - if p.stopOnHCLError { - return err - } - var diags hcl.Diagnostics - if errors.As(err, &diags) { - errc := p.showParseErrors(p.moduleFS, path, diags) - if errc == nil { - continue - } - p.logger.Error("Failed to get the causes of the parsing error", log.Err(errc)) - } - p.logger.Error("Error parsing file", log.FilePath(path), log.Err(err)) + if err := p.ParseFile(ctx, path); err == nil { continue } + + if p.stopOnHCLError { + return err + } + var diags hcl.Diagnostics + if errors.As(err, &diags) { + errc := p.showParseErrors(p.moduleFS, path, diags) + if errc == nil { + continue + } + p.logger.Error("Failed to get the causes of the parsing error", log.Err(errc)) + } + p.logger.Error("Error parsing file", log.FilePath(path), log.Err(err)) } return nil } -func (p *Parser) showParseErrors(fsys fs.FS, path string, diags hcl.Diagnostics) error { - file, err := fsys.Open(path) +func (p *Parser) showParseErrors(fsys fs.FS, filePath string, diags hcl.Diagnostics) error { + file, err := fsys.Open(filePath) if err != nil { return fmt.Errorf("failed to read file: %w", err) } @@ -196,7 +197,7 @@ func (p *Parser) showParseErrors(fsys fs.FS, path string, diags hcl.Diagnostics) } cause := strings.Join(lines, "\n") - p.logger.Error("Error parsing file", log.FilePath(path), + p.logger.Error("Error parsing file", log.FilePath(filePath), log.String("cause", cause), log.Err(diag)) } } From 87b4f771ef67e88a0d5367de662667c92866ce68 Mon Sep 17 00:00:00 2001 From: Simar Date: Fri, 22 Nov 2024 23:26:38 -0700 Subject: [PATCH 3/3] fix lint --- pkg/iac/scanners/terraform/parser/parser.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/iac/scanners/terraform/parser/parser.go b/pkg/iac/scanners/terraform/parser/parser.go index 9c08763f8000..48ed799155ea 100644 --- a/pkg/iac/scanners/terraform/parser/parser.go +++ b/pkg/iac/scanners/terraform/parser/parser.go @@ -168,7 +168,8 @@ func (p *Parser) ParseFS(ctx context.Context, dir string) error { } sort.Strings(paths) for _, path := range paths { - if err := p.ParseFile(ctx, path); err == nil { + var err error + if err = p.ParseFile(ctx, path); err == nil { continue } @@ -184,6 +185,7 @@ func (p *Parser) ParseFS(ctx context.Context, dir string) error { p.logger.Error("Failed to get the causes of the parsing error", log.Err(errc)) } p.logger.Error("Error parsing file", log.FilePath(path), log.Err(err)) + continue } return nil