1package checkers
2
3import (
4	"go/ast"
5	"go/constant"
6	"regexp"
7	"strings"
8
9	"github.com/go-critic/go-critic/checkers/internal/astwalk"
10	"github.com/go-critic/go-critic/framework/linter"
11)
12
13func init() {
14	var info linter.CheckerInfo
15	info.Name = "regexpPattern"
16	info.Tags = []string{"diagnostic", "experimental"}
17	info.Summary = "Detects suspicious regexp patterns"
18	info.Before = "regexp.MustCompile(`google.com|yandex.ru`)"
19	info.After = "regexp.MustCompile(`google\\.com|yandex\\.ru`)"
20
21	collection.AddChecker(&info, func(ctx *linter.CheckerContext) (linter.FileWalker, error) {
22		domains := []string{
23			"com",
24			"org",
25			"info",
26			"net",
27			"ru",
28			"de",
29		}
30
31		allDomains := strings.Join(domains, "|")
32		domainRE := regexp.MustCompile(`[^\\]\.(` + allDomains + `)\b`)
33		return astwalk.WalkerForExpr(&regexpPatternChecker{
34			ctx:      ctx,
35			domainRE: domainRE,
36		}), nil
37	})
38}
39
40type regexpPatternChecker struct {
41	astwalk.WalkHandler
42	ctx *linter.CheckerContext
43
44	domainRE *regexp.Regexp
45}
46
47func (c *regexpPatternChecker) VisitExpr(x ast.Expr) {
48	call, ok := x.(*ast.CallExpr)
49	if !ok {
50		return
51	}
52
53	switch qualifiedName(call.Fun) {
54	case "regexp.Compile", "regexp.CompilePOSIX", "regexp.MustCompile", "regexp.MustCompilePosix":
55		cv := c.ctx.TypesInfo.Types[call.Args[0]].Value
56		if cv == nil || cv.Kind() != constant.String {
57			return
58		}
59		s := constant.StringVal(cv)
60		if m := c.domainRE.FindStringSubmatch(s); m != nil {
61			c.warnDomain(call.Args[0], m[1])
62		}
63	}
64}
65
66func (c *regexpPatternChecker) warnDomain(cause ast.Expr, domain string) {
67	c.ctx.Warn(cause, "'.%s' should probably be '\\.%s'", domain, domain)
68}
69