Repository: ysugimoto/falco
Pull request #90 (2022-07-21 16:26:26)

Add support for PCRE
Why?
Falco ([the go regexp pkg](https://pkg.go.dev/regexp) to be more accurate!) right now doesn't know how to parse [PCRE](https://www.pcre.org/) regular expressions that Varnish uses.
An example of that would be this file:
https://github.com/varnishcache/varnish-devicedetect/blob/master/devicedetect.vcl

**Edit1:** 
I created a small repo that uses [ccgo](https://modernc.org/ccgo/v3) compiler to translate the PCRE2 source code to Go.
https://github.com/shadialtarsha/go-pcre

Would love to hear your opinion about this

**Edit2:** 
CI is green. I added a build step for `pullrequest.yaml` and it passed, then I removed it.


Patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 24ef6fd..7ec271c 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -6,7 +6,6 @@ on:
     tags-ignore:
       - 'v*.*.*'
 
-
 jobs:
   build:
     runs-on: ubuntu-latest
@@ -25,4 +24,3 @@ jobs:
         skip-pkg-cache: true
         skip-build-cache: true
         skip-go-installation: true
-
diff --git a/dist/.keep b/dist/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/go.mod b/go.mod
index b805bd2..6841252 100644
--- a/go.mod
+++ b/go.mod
@@ -11,5 +11,5 @@ require (
 	github.com/kyokomi/emoji v1.5.1
 	github.com/mattn/go-colorable v0.1.8
 	github.com/pkg/errors v0.9.1
-	golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect
+	github.com/shadialtarsha/go-pcre v0.0.0-20220904164957-b4f1834ceecc
 )
diff --git a/go.sum b/go.sum
index a19e93e..39f20b7 100644
--- a/go.sum
+++ b/go.sum
@@ -13,6 +13,8 @@ github.com/goccy/go-yaml v1.8.9 h1:4AEXg2qx+/w29jXnXpMY6mTckmYu1TMoHteKuMf0HFg=
 github.com/goccy/go-yaml v1.8.9/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
 github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
 github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
 github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
 github.com/k0kubun/pp v2.4.0+incompatible h1:M9iQzcejGfiBjDa7+Tc0rJgR7WFKP6rim/Q0DDrAT3g=
@@ -28,6 +30,10 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
+github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
+github.com/shadialtarsha/go-pcre v0.0.0-20220904164957-b4f1834ceecc h1:60WCBKx6gbobqUIJKDCg+10GQqw1pHmJRFz+X9hf4VQ=
+github.com/shadialtarsha/go-pcre v0.0.0-20220904164957-b4f1834ceecc/go.mod h1:pjdUlPAShYIHpsq4u+qC1cUELMUk7ZGz1xnrYu7Dlag=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -38,8 +44,8 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
-golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac h1:oN6lz7iLW/YC7un8pq+9bOLyXrprv2+DKfkJY+2LJJw=
+golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -48,3 +54,9 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+modernc.org/libc v1.17.0 h1:nbL2Lv0I323wLc1GmTh/AqVtI9JeBVc7Nhapdg9EONs=
+modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0=
+modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
+modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
+modernc.org/memory v1.2.0 h1:zXehBrt9n+Pjn+4RoRCZ0KqRA/0ePFqcecxZ/hXCIVw=
+modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
diff --git a/linter/linter.go b/linter/linter.go
index 63276c9..41d4633 100644
--- a/linter/linter.go
+++ b/linter/linter.go
@@ -3,9 +3,9 @@ package linter
 import (
 	"fmt"
 	"net"
-	"regexp"
 	"strings"
 
+	regexp "github.com/shadialtarsha/go-pcre"
 	"github.com/ysugimoto/falco/ast"
 	"github.com/ysugimoto/falco/context"
 	"github.com/ysugimoto/falco/token"
@@ -1357,7 +1357,7 @@ func (l *Linter) lintInfixExpression(exp *ast.InfixExpression, ctx *context.Cont
 		}
 		// And, if right expression is STRING, regex must be valid
 		if v, ok := exp.Right.(*ast.String); ok {
-			if _, err := regexp.Compile(strings.ReplaceAll(v.Value, "\\", "\\\\")); err != nil {
+			if _, err := regexp.Compile(v.Value); err != nil {
 				err := &LintError{
 					Severity: ERROR,
 					Token:    exp.Right.GetMeta().Token,


Test patch:
diff --git a/linter/linter_test.go b/linter/linter_test.go
index 2e4e2a3..0ed8d00 100644
--- a/linter/linter_test.go
+++ b/linter/linter_test.go
@@ -1070,6 +1070,36 @@ sub foo {
 }`
 		assertError(t, input)
 	})
+
+	t.Run("pass with PCRE expression", func(t *testing.T) {
+		input := `
+sub foo {
+	if (req.http.Host ~ "(?i)^word") {
+		restart;
+	}
+}`
+		assertNoError(t, input)
+	})
+
+	t.Run("pass with expression that has backslash", func(t *testing.T) {
+		input := `
+sub foo {
+	if (req.http.User-Agent ~ "\(compatible.?; Googlebot/2.1.?; \+http://www.google.com/bot.html") {
+		restart;
+	}
+}`
+		assertNoError(t, input)
+	})
+
+	t.Run("pass with PCRE expression that has backslash", func(t *testing.T) {
+		input := `
+sub foo {
+	if (req.http.User-Agent ~ "(?i)windows\ ?ce") {
+		restart;
+	}
+}`
+		assertNoError(t, input)
+	})
 }
 
 func TestLintRegexNotOperator(t *testing.T) {