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, &currentMap); 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  }