Skip to content

Commit e9a899a

Browse files
nikpivkinsimar7
andauthored
feat(misconf): log causes of HCL file parsing errors (#7634)
Signed-off-by: nikpivkin <nikita.pivkin@smartforce.io> Co-authored-by: Simar <simar@linux.com> Co-authored-by: simar7 <1254783+simar7@users.noreply.github.com>
1 parent 9054303 commit e9a899a

File tree

2 files changed

+78
-4
lines changed

2 files changed

+78
-4
lines changed

pkg/iac/scanners/terraform/parser/parser.go

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package parser
22

33
import (
4+
"bufio"
45
"context"
56
"errors"
7+
"fmt"
68
"io"
79
"io/fs"
810
"os"
@@ -166,18 +168,69 @@ func (p *Parser) ParseFS(ctx context.Context, dir string) error {
166168
}
167169
sort.Strings(paths)
168170
for _, path := range paths {
169-
if err := p.ParseFile(ctx, path); err != nil {
170-
if p.stopOnHCLError {
171+
var err error
172+
if err = p.ParseFile(ctx, path); err == nil {
173+
continue
174+
}
175+
176+
if p.stopOnHCLError {
177+
return err
178+
}
179+
var diags hcl.Diagnostics
180+
if errors.As(err, &diags) {
181+
errc := p.showParseErrors(p.moduleFS, path, diags)
182+
if errc == nil {
183+
continue
184+
}
185+
p.logger.Error("Failed to get the causes of the parsing error", log.Err(errc))
186+
}
187+
p.logger.Error("Error parsing file", log.FilePath(path), log.Err(err))
188+
continue
189+
}
190+
191+
return nil
192+
}
193+
194+
func (p *Parser) showParseErrors(fsys fs.FS, filePath string, diags hcl.Diagnostics) error {
195+
file, err := fsys.Open(filePath)
196+
if err != nil {
197+
return fmt.Errorf("failed to read file: %w", err)
198+
}
199+
defer file.Close()
200+
201+
for _, diag := range diags {
202+
if subj := diag.Subject; subj != nil {
203+
lines, err := readLinesFromFile(file, subj.Start.Line, subj.End.Line)
204+
if err != nil {
171205
return err
172206
}
173-
p.logger.Error("Error parsing file", log.FilePath(path), log.Err(err))
174-
continue
207+
208+
cause := strings.Join(lines, "\n")
209+
p.logger.Error("Error parsing file", log.FilePath(filePath),
210+
log.String("cause", cause), log.Err(diag))
175211
}
176212
}
177213

178214
return nil
179215
}
180216

217+
func readLinesFromFile(f io.Reader, from, to int) ([]string, error) {
218+
scanner := bufio.NewScanner(f)
219+
rawLines := make([]string, 0, to-from+1)
220+
221+
for lineNum := 0; scanner.Scan() && lineNum < to; lineNum++ {
222+
if lineNum >= from-1 {
223+
rawLines = append(rawLines, scanner.Text())
224+
}
225+
}
226+
227+
if err := scanner.Err(); err != nil {
228+
return nil, fmt.Errorf("failed to scan file: %w", err)
229+
}
230+
231+
return rawLines, nil
232+
}
233+
181234
var ErrNoFiles = errors.New("no files found")
182235

183236
func (p *Parser) Load(ctx context.Context) (*evaluator, error) {

pkg/iac/scanners/terraform/parser/parser_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2010,6 +2010,27 @@ variable "baz" {}
20102010
assert.Contains(t, buf.String(), "variables=\"foo\"")
20112011
}
20122012

2013+
func TestLogParseErrors(t *testing.T) {
2014+
var buf bytes.Buffer
2015+
slog.SetDefault(slog.New(log.NewHandler(&buf, nil)))
2016+
2017+
src := `resource "aws-s3-bucket" "name" {
2018+
bucket = <
2019+
}`
2020+
2021+
fsys := fstest.MapFS{
2022+
"main.tf": &fstest.MapFile{
2023+
Data: []byte(src),
2024+
},
2025+
}
2026+
2027+
parser := New(fsys, "")
2028+
err := parser.ParseFS(context.TODO(), ".")
2029+
require.NoError(t, err)
2030+
2031+
assert.Contains(t, buf.String(), `cause=" bucket = <"`)
2032+
}
2033+
20132034
func Test_PassingNullToChildModule_DoesNotEraseType(t *testing.T) {
20142035
tests := []struct {
20152036
name string

0 commit comments

Comments
 (0)