github.com/khulnasoft-lab/defsec@v1.0.5-0.20230827010352-5e9f46893d95/pkg/scanners/yaml/parser/parser.go (about) 1 package parser 2 3 import ( 4 "bytes" 5 "context" 6 "io" 7 "io/fs" 8 "path/filepath" 9 "strings" 10 11 "github.com/khulnasoft-lab/defsec/pkg/debug" 12 13 "github.com/khulnasoft-lab/defsec/pkg/detection" 14 "github.com/khulnasoft-lab/defsec/pkg/scanners/options" 15 "gopkg.in/yaml.v3" 16 ) 17 18 var _ options.ConfigurableParser = (*Parser)(nil) 19 20 type Parser struct { 21 debug debug.Logger 22 skipRequired bool 23 } 24 25 func (p *Parser) SetDebugWriter(writer io.Writer) { 26 p.debug = debug.New(writer, "yaml", "parser") 27 } 28 29 func (p *Parser) SetSkipRequiredCheck(b bool) { 30 p.skipRequired = b 31 } 32 33 // New creates a new parser 34 func New(opts ...options.ParserOption) *Parser { 35 p := &Parser{} 36 for _, opt := range opts { 37 opt(p) 38 } 39 return p 40 } 41 42 func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) (map[string][]interface{}, error) { 43 44 files := make(map[string][]interface{}) 45 if err := fs.WalkDir(target, filepath.ToSlash(path), func(path string, entry fs.DirEntry, err error) error { 46 select { 47 case <-ctx.Done(): 48 return ctx.Err() 49 default: 50 } 51 if err != nil { 52 return err 53 } 54 if entry.IsDir() { 55 return nil 56 } 57 if !p.Required(path) { 58 return nil 59 } 60 df, err := p.ParseFile(ctx, target, path) 61 if err != nil { 62 p.debug.Log("Parse error in '%s': %s", path, err) 63 return nil 64 } 65 files[path] = df 66 return nil 67 }); err != nil { 68 return nil, err 69 } 70 return files, nil 71 } 72 73 // ParseFile parses yaml content from the provided filesystem path. 74 func (p *Parser) ParseFile(_ context.Context, fs fs.FS, path string) ([]interface{}, error) { 75 f, err := fs.Open(filepath.ToSlash(path)) 76 if err != nil { 77 return nil, err 78 } 79 defer func() { _ = f.Close() }() 80 81 contents, err := io.ReadAll(f) 82 if err != nil { 83 return nil, err 84 } 85 86 var results []interface{} 87 88 marker := "\n---\n" 89 altMarker := "\r\n---\r\n" 90 if bytes.Contains(contents, []byte(altMarker)) { 91 marker = altMarker 92 } 93 94 for _, partial := range strings.Split(string(contents), marker) { 95 var target interface{} 96 if err := yaml.Unmarshal([]byte(partial), &target); err != nil { 97 return nil, err 98 } 99 results = append(results, target) 100 } 101 102 return results, nil 103 } 104 105 func (p *Parser) Required(path string) bool { 106 if p.skipRequired { 107 return true 108 } 109 return detection.IsType(path, nil, detection.FileTypeYAML) 110 }