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 }