|
| 1 | +package config |
| 2 | + |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + "regexp" |
| 6 | +) |
| 7 | + |
| 8 | +const excludeRuleMinConditionsCount = 2 |
| 9 | + |
| 10 | +var DefaultExcludePatterns = []ExcludePattern{ |
| 11 | + { |
| 12 | + ID: "EXC0001", |
| 13 | + Pattern: "Error return value of .((os\\.)?std(out|err)\\..*|.*Close" + |
| 14 | + "|.*Flush|os\\.Remove(All)?|.*print(f|ln)?|os\\.(Un)?Setenv). is not checked", |
| 15 | + Linter: "errcheck", |
| 16 | + Why: "Almost all programs ignore errors on these functions and in most cases it's ok", |
| 17 | + }, |
| 18 | + { |
| 19 | + ID: "EXC0002", |
| 20 | + Pattern: "(comment on exported (method|function|type|const)|" + |
| 21 | + "should have( a package)? comment|comment should be of the form)", |
| 22 | + Linter: "golint", |
| 23 | + Why: "Annoying issue about not having a comment. The rare codebase has such comments", |
| 24 | + }, |
| 25 | + { |
| 26 | + ID: "EXC0003", |
| 27 | + Pattern: "func name will be used as test\\.Test.* by other packages, and that stutters; consider calling this", |
| 28 | + Linter: "golint", |
| 29 | + Why: "False positive when tests are defined in package 'test'", |
| 30 | + }, |
| 31 | + { |
| 32 | + ID: "EXC0004", |
| 33 | + Pattern: "(possible misuse of unsafe.Pointer|should have signature)", |
| 34 | + Linter: "govet", |
| 35 | + Why: "Common false positives", |
| 36 | + }, |
| 37 | + { |
| 38 | + ID: "EXC0005", |
| 39 | + Pattern: "ineffective break statement. Did you mean to break out of the outer loop", |
| 40 | + Linter: "staticcheck", |
| 41 | + Why: "Developers tend to write in C-style with an explicit 'break' in a 'switch', so it's ok to ignore", |
| 42 | + }, |
| 43 | + { |
| 44 | + ID: "EXC0006", |
| 45 | + Pattern: "Use of unsafe calls should be audited", |
| 46 | + Linter: "gosec", |
| 47 | + Why: "Too many false-positives on 'unsafe' usage", |
| 48 | + }, |
| 49 | + { |
| 50 | + ID: "EXC0007", |
| 51 | + Pattern: "Subprocess launch(ed with variable|ing should be audited)", |
| 52 | + Linter: "gosec", |
| 53 | + Why: "Too many false-positives for parametrized shell calls", |
| 54 | + }, |
| 55 | + { |
| 56 | + ID: "EXC0008", |
| 57 | + Pattern: "(G104|G307)", |
| 58 | + Linter: "gosec", |
| 59 | + Why: "Duplicated errcheck checks", |
| 60 | + }, |
| 61 | + { |
| 62 | + ID: "EXC0009", |
| 63 | + Pattern: "(Expect directory permissions to be 0750 or less|Expect file permissions to be 0600 or less)", |
| 64 | + Linter: "gosec", |
| 65 | + Why: "Too many issues in popular repos", |
| 66 | + }, |
| 67 | + { |
| 68 | + ID: "EXC0010", |
| 69 | + Pattern: "Potential file inclusion via variable", |
| 70 | + Linter: "gosec", |
| 71 | + Why: "False positive is triggered by 'src, err := ioutil.ReadFile(filename)'", |
| 72 | + }, |
| 73 | + { |
| 74 | + ID: "EXC0011", |
| 75 | + Pattern: "(comment on exported (method|function|type|const)|" + |
| 76 | + "should have( a package)? comment|comment should be of the form)", |
| 77 | + Linter: "stylecheck", |
| 78 | + Why: "Annoying issue about not having a comment. The rare codebase has such comments", |
| 79 | + }, |
| 80 | +} |
| 81 | + |
| 82 | +type Issues struct { |
| 83 | + IncludeDefaultExcludes []string `mapstructure:"include"` |
| 84 | + ExcludeCaseSensitive bool `mapstructure:"exclude-case-sensitive"` |
| 85 | + ExcludePatterns []string `mapstructure:"exclude"` |
| 86 | + ExcludeRules []ExcludeRule `mapstructure:"exclude-rules"` |
| 87 | + UseDefaultExcludes bool `mapstructure:"exclude-use-default"` |
| 88 | + |
| 89 | + MaxIssuesPerLinter int `mapstructure:"max-issues-per-linter"` |
| 90 | + MaxSameIssues int `mapstructure:"max-same-issues"` |
| 91 | + |
| 92 | + DiffFromRevision string `mapstructure:"new-from-rev"` |
| 93 | + DiffPatchFilePath string `mapstructure:"new-from-patch"` |
| 94 | + Diff bool `mapstructure:"new"` |
| 95 | + |
| 96 | + NeedFix bool `mapstructure:"fix"` |
| 97 | +} |
| 98 | + |
| 99 | +type ExcludeRule struct { |
| 100 | + BaseRule `mapstructure:",squash"` |
| 101 | +} |
| 102 | + |
| 103 | +func (e ExcludeRule) Validate() error { |
| 104 | + return e.BaseRule.Validate(excludeRuleMinConditionsCount) |
| 105 | +} |
| 106 | + |
| 107 | +type BaseRule struct { |
| 108 | + Linters []string |
| 109 | + Path string |
| 110 | + Text string |
| 111 | + Source string |
| 112 | +} |
| 113 | + |
| 114 | +func (b BaseRule) Validate(minConditionsCount int) error { |
| 115 | + if err := validateOptionalRegex(b.Path); err != nil { |
| 116 | + return fmt.Errorf("invalid path regex: %v", err) |
| 117 | + } |
| 118 | + if err := validateOptionalRegex(b.Text); err != nil { |
| 119 | + return fmt.Errorf("invalid text regex: %v", err) |
| 120 | + } |
| 121 | + if err := validateOptionalRegex(b.Source); err != nil { |
| 122 | + return fmt.Errorf("invalid source regex: %v", err) |
| 123 | + } |
| 124 | + nonBlank := 0 |
| 125 | + if len(b.Linters) > 0 { |
| 126 | + nonBlank++ |
| 127 | + } |
| 128 | + if b.Path != "" { |
| 129 | + nonBlank++ |
| 130 | + } |
| 131 | + if b.Text != "" { |
| 132 | + nonBlank++ |
| 133 | + } |
| 134 | + if b.Source != "" { |
| 135 | + nonBlank++ |
| 136 | + } |
| 137 | + if nonBlank < minConditionsCount { |
| 138 | + return fmt.Errorf("at least %d of (text, source, path, linters) should be set", minConditionsCount) |
| 139 | + } |
| 140 | + return nil |
| 141 | +} |
| 142 | + |
| 143 | +func validateOptionalRegex(value string) error { |
| 144 | + if value == "" { |
| 145 | + return nil |
| 146 | + } |
| 147 | + _, err := regexp.Compile(value) |
| 148 | + return err |
| 149 | +} |
| 150 | + |
| 151 | +type ExcludePattern struct { |
| 152 | + ID string |
| 153 | + Pattern string |
| 154 | + Linter string |
| 155 | + Why string |
| 156 | +} |
| 157 | + |
| 158 | +func GetDefaultExcludePatternsStrings() []string { |
| 159 | + ret := make([]string, len(DefaultExcludePatterns)) |
| 160 | + for i, p := range DefaultExcludePatterns { |
| 161 | + ret[i] = p.Pattern |
| 162 | + } |
| 163 | + return ret |
| 164 | +} |
| 165 | + |
| 166 | +func GetExcludePatterns(include []string) []ExcludePattern { |
| 167 | + includeMap := make(map[string]bool, len(include)) |
| 168 | + for _, inc := range include { |
| 169 | + includeMap[inc] = true |
| 170 | + } |
| 171 | + |
| 172 | + var ret []ExcludePattern |
| 173 | + for _, p := range DefaultExcludePatterns { |
| 174 | + if !includeMap[p.ID] { |
| 175 | + ret = append(ret, p) |
| 176 | + } |
| 177 | + } |
| 178 | + |
| 179 | + return ret |
| 180 | +} |
0 commit comments