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