github.com/khulnasoft-lab/defsec@v1.0.5-0.20230827010352-5e9f46893d95/pkg/scanners/helm/parser/vals.go (about) 1 package parser 2 3 import ( 4 "fmt" 5 "io" 6 "net/url" 7 "os" 8 "strings" 9 10 "gopkg.in/yaml.v3" 11 "helm.sh/helm/v3/pkg/getter" 12 "helm.sh/helm/v3/pkg/strvals" 13 ) 14 15 type ValueOptions struct { 16 ValueFiles []string 17 StringValues []string 18 Values []string 19 FileValues []string 20 } 21 22 // MergeValues merges values from files specified via -f/--values and directly 23 // via --set, --set-string, or --set-file, marshaling them to YAML 24 func (opts *ValueOptions) MergeValues() (map[string]interface{}, error) { 25 base := map[string]interface{}{} 26 27 // User specified a values files via -f/--values 28 for _, filePath := range opts.ValueFiles { 29 currentMap := map[string]interface{}{} 30 31 bytes, err := readFile(filePath) 32 if err != nil { 33 return nil, err 34 } 35 36 if err := yaml.Unmarshal(bytes, ¤tMap); err != nil { 37 return nil, fmt.Errorf("failed to parse %s: %w", filePath, err) 38 } 39 // Merge with the previous map 40 base = mergeMaps(base, currentMap) 41 } 42 43 // User specified a value via --set 44 for _, value := range opts.Values { 45 if err := strvals.ParseInto(value, base); err != nil { 46 return nil, fmt.Errorf("failed parsing --set data, %w", err) 47 } 48 } 49 50 // User specified a value via --set-string 51 for _, value := range opts.StringValues { 52 if err := strvals.ParseIntoString(value, base); err != nil { 53 return nil, fmt.Errorf("failed parsing --set-string data %w", err) 54 } 55 } 56 57 // User specified a value via --set-file 58 for _, value := range opts.FileValues { 59 reader := func(rs []rune) (interface{}, error) { 60 bytes, err := readFile(string(rs)) 61 if err != nil { 62 return nil, err 63 } 64 return string(bytes), err 65 } 66 if err := strvals.ParseIntoFile(value, base, reader); err != nil { 67 return nil, fmt.Errorf("failed parsing --set-file data: %w", err) 68 } 69 } 70 71 return base, nil 72 } 73 74 func mergeMaps(a, b map[string]interface{}) map[string]interface{} { 75 out := make(map[string]interface{}, len(a)) 76 for k, v := range a { 77 out[k] = v 78 } 79 for k, v := range b { 80 if v, ok := v.(map[string]interface{}); ok { 81 if bv, ok := out[k]; ok { 82 if bv, ok := bv.(map[string]interface{}); ok { 83 out[k] = mergeMaps(bv, v) 84 continue 85 } 86 } 87 } 88 out[k] = v 89 } 90 return out 91 } 92 93 // readFile load a file from stdin, the local directory, or a remote file with a url. 94 func readFile(filePath string) ([]byte, error) { 95 if strings.TrimSpace(filePath) == "-" { 96 return io.ReadAll(os.Stdin) 97 } 98 u, _ := url.Parse(filePath) 99 100 // FIXME: maybe someone handle other protocols like ftp. 101 if u.Scheme == "http" || u.Scheme == "https" { 102 g, err := getter.NewHTTPGetter() 103 if err != nil { 104 return nil, err 105 } 106 data, err := g.Get(filePath, getter.WithURL(filePath)) 107 if err != nil { 108 return nil, err 109 } 110 return data.Bytes(), err 111 } else { 112 return os.ReadFile(filePath) 113 } 114 }