github.com/khulnasoft-lab/defsec@v1.0.5-0.20230827010352-5e9f46893d95/pkg/scanners/terraform/parser/load_blocks.go (about)

     1  package parser
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/khulnasoft-lab/defsec/pkg/types"
    10  
    11  	"github.com/khulnasoft-lab/defsec/pkg/terraform"
    12  
    13  	"github.com/hashicorp/hcl/v2"
    14  )
    15  
    16  func loadBlocksFromFile(file sourceFile, moduleSource string) (hcl.Blocks, []terraform.Ignore, error) {
    17  	ignores := parseIgnores(file.file.Bytes, file.path, moduleSource)
    18  	contents, diagnostics := file.file.Body.Content(terraform.Schema)
    19  	if diagnostics != nil && diagnostics.HasErrors() {
    20  		return nil, nil, diagnostics
    21  	}
    22  	if contents == nil {
    23  		return nil, nil, nil
    24  	}
    25  	return contents.Blocks, ignores, nil
    26  }
    27  
    28  func parseIgnores(data []byte, path string, moduleSource string) []terraform.Ignore {
    29  	var ignores []terraform.Ignore
    30  	for i, line := range strings.Split(string(data), "\n") {
    31  		line = strings.TrimSpace(line)
    32  		lineIgnores := parseIgnoresFromLine(line)
    33  		for _, lineIgnore := range lineIgnores {
    34  			lineIgnore.Range = types.NewRange(path, i+1, i+1, moduleSource, nil)
    35  			ignores = append(ignores, lineIgnore)
    36  		}
    37  	}
    38  	for a, ignoreA := range ignores {
    39  		if !ignoreA.Block {
    40  			continue
    41  		}
    42  		for _, ignoreB := range ignores {
    43  			if !ignoreB.Block {
    44  				continue
    45  			}
    46  			if ignoreA.Range.GetStartLine()+1 == ignoreB.Range.GetStartLine() {
    47  				ignoreA.Range = ignoreB.Range
    48  				ignores[a] = ignoreA
    49  			}
    50  		}
    51  	}
    52  	return ignores
    53  
    54  }
    55  
    56  var commentPattern = regexp.MustCompile(`^\s*([/]+|/\*|#)+\s*terrasec:`)
    57  var vulCommentPattern = regexp.MustCompile(`^\s*([/]+|/\*|#)+\s*vul:`)
    58  
    59  func parseIgnoresFromLine(input string) []terraform.Ignore {
    60  
    61  	var ignores []terraform.Ignore
    62  
    63  	input = commentPattern.ReplaceAllString(input, "terrasec:")
    64  	input = vulCommentPattern.ReplaceAllString(input, "vul:")
    65  
    66  	bits := strings.Split(strings.TrimSpace(input), " ")
    67  	for i, bit := range bits {
    68  		bit := strings.TrimSpace(bit)
    69  		bit = strings.TrimPrefix(bit, "#")
    70  		bit = strings.TrimPrefix(bit, "//")
    71  		bit = strings.TrimPrefix(bit, "/*")
    72  
    73  		if strings.HasPrefix(bit, "terrasec:") || strings.HasPrefix(bit, "vul:") {
    74  			ignore, err := parseIgnoreFromComment(bit)
    75  			if err != nil {
    76  				continue
    77  			}
    78  			ignore.Block = i == 0
    79  			ignores = append(ignores, *ignore)
    80  		}
    81  	}
    82  
    83  	return ignores
    84  }
    85  
    86  func parseIgnoreFromComment(input string) (*terraform.Ignore, error) {
    87  	var ignore terraform.Ignore
    88  	if !strings.HasPrefix(input, "terrasec:") && !strings.HasPrefix(input, "vul:") {
    89  		return nil, fmt.Errorf("invalid ignore")
    90  	}
    91  
    92  	input = input[6:]
    93  
    94  	segments := strings.Split(input, ":")
    95  
    96  	for i := 0; i < len(segments)-1; i += 2 {
    97  		key := segments[i]
    98  		val := segments[i+1]
    99  		switch key {
   100  		case "ignore":
   101  			ignore.RuleID, ignore.Params = parseIDWithParams(val)
   102  		case "exp":
   103  			parsed, err := time.Parse("2006-01-02", val)
   104  			if err != nil {
   105  				return &ignore, err
   106  			}
   107  			ignore.Expiry = &parsed
   108  		case "ws":
   109  			ignore.Workspace = val
   110  		}
   111  	}
   112  
   113  	return &ignore, nil
   114  }
   115  
   116  func parseIDWithParams(input string) (string, map[string]string) {
   117  	params := make(map[string]string)
   118  	if !strings.Contains(input, "[") {
   119  		return input, params
   120  	}
   121  	parts := strings.Split(input, "[")
   122  	id := parts[0]
   123  	paramStr := strings.TrimSuffix(parts[1], "]")
   124  	for _, pair := range strings.Split(paramStr, ",") {
   125  		parts := strings.Split(pair, "=")
   126  		if len(parts) != 2 {
   127  			continue
   128  		}
   129  		params[parts[0]] = parts[1]
   130  	}
   131  	return id, params
   132  }