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 }