From 5ef1e9979b6c975632ca45dcb434a966988d1c90 Mon Sep 17 00:00:00 2001 From: Tom Limoncelli Date: Mon, 1 Jul 2024 20:21:44 -0400 Subject: [PATCH] CLOUDFLARE: Fix bugs with the new "single redirect" feature (#3031) Co-authored-by: Josh Zhang --- documentation/providers.md | 2 +- pkg/cloudflare-go/internal/tools/go.sum | 12 ++++ providers/cloudflare/rest.go | 15 +++-- providers/cloudflare/singleredirect.go | 24 ++++++- providers/cloudflare/singleredirect_test.go | 74 ++++++++++++++++----- 5 files changed, 101 insertions(+), 26 deletions(-) diff --git a/documentation/providers.md b/documentation/providers.md index 840a3b8745..d0c569911a 100644 --- a/documentation/providers.md +++ b/documentation/providers.md @@ -24,7 +24,7 @@ If a feature is definitively not supported for whatever reason, we would also li | [`CLOUDFLAREAPI`](provider/cloudflareapi.md) | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❔ | ✅ | ❌ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | ✅ | | [`CLOUDNS`](provider/cloudns.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❔ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ✅ | ❔ | ✅ | ❔ | ❔ | ✅ | ❔ | ❔ | ✅ | ✅ | | [`CSCGLOBAL`](provider/cscglobal.md) | ✅ | ✅ | ✅ | ✅ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ✅ | -| [`DESEC`](provider/desec.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ✅ | ✅ | ✅ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ✅ | ✅ | ❔ | ❔ | ✅ | ❔ | ✅ | ✅ | +| [`DESEC`](provider/desec.md) | ❌ | ✅ | ❌ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ✅ | ✅ | ❔ | ❔ | ✅ | ❔ | ✅ | ✅ | | [`DIGITALOCEAN`](provider/digitalocean.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ✅ | ❔ | ❔ | ❌ | ❔ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ✅ | | [`DNSIMPLE`](provider/dnsimple.md) | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❔ | ❌ | ✅ | ✅ | ❔ | ✅ | ✅ | ❔ | ❌ | ❌ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | | [`DNSMADEEASY`](provider/dnsmadeeasy.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❔ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ❌ | ❔ | ❌ | ❌ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | diff --git a/pkg/cloudflare-go/internal/tools/go.sum b/pkg/cloudflare-go/internal/tools/go.sum index ab0c7aadff..003cb032f9 100644 --- a/pkg/cloudflare-go/internal/tools/go.sum +++ b/pkg/cloudflare-go/internal/tools/go.sum @@ -214,6 +214,8 @@ github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcH github.com/ettle/strcase v0.1.1 h1:htFueZyVeE1XNnMEfbqp5r67qAN/4r6ya1ysq8Q+Zcw= github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= @@ -434,6 +436,7 @@ github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9 github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.15.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= @@ -442,6 +445,8 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-plugin v1.3.0/go.mod h1:F9eH4LrE/ZsRdbwhfjs9k9HoDUwAHnYtXdgmf1AVNs0= github.com/hashicorp/go-plugin v1.4.0/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= +github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= +github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= @@ -594,6 +599,8 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -602,6 +609,8 @@ github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -1137,6 +1146,7 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1153,6 +1163,8 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/providers/cloudflare/rest.go b/providers/cloudflare/rest.go index 5d650f4508..948d85b7c7 100644 --- a/providers/cloudflare/rest.go +++ b/providers/cloudflare/rest.go @@ -2,6 +2,7 @@ package cloudflare import ( "context" + "errors" "fmt" "strings" @@ -278,7 +279,11 @@ func (c *cloudflareProvider) getUniversalSSL(domainID string) (bool, error) { func (c *cloudflareProvider) getSingleRedirects(id string, domain string) ([]*models.RecordConfig, error) { rules, err := c.cfClient.GetEntrypointRuleset(context.Background(), cloudflare.ZoneIdentifier(id), "http_request_dynamic_redirect") if err != nil { - return nil, fmt.Errorf("failed fetching redirect rule list cloudflare: %s", err) + var e *cloudflare.NotFoundError + if errors.As(err, &e) { + return []*models.RecordConfig{}, nil + } + return nil, fmt.Errorf("failed fetching redirect rule list cloudflare: %s (%T)", err, err) } //var rulelist []cloudflare.RulesetRule //rulelist = rules.Rules @@ -330,8 +335,9 @@ func (c *cloudflareProvider) createSingleRedirect(domainID string, cfr models.Cl newSingleRedirectRules = append(newSingleRedirectRules, newSingleRedirectRule) newSingleRedirect := cloudflare.UpdateEntrypointRulesetParams{} - // Preserve query string - preserveQueryString := true + // Preserve query string if there isn't one in the replacement. + preserveQueryString := !strings.Contains(cfr.SRReplacement, "?") + newSingleRedirectRulesActionParameters.FromValue = &cloudflare.RulesetRuleActionParametersFromValue{} // Redirect status code newSingleRedirectRulesActionParameters.FromValue.StatusCode = uint16(cfr.Code) @@ -352,7 +358,8 @@ func (c *cloudflareProvider) createSingleRedirect(domainID string, cfr models.Cl // Get a list of current redirects so that the new redirect get appended to it rules, err := c.cfClient.GetEntrypointRuleset(context.Background(), cloudflare.ZoneIdentifier(domainID), "http_request_dynamic_redirect") - if err != nil { + var e *cloudflare.NotFoundError + if err != nil && !errors.As(err, &e) { return fmt.Errorf("failed fetching redirect rule list cloudflare: %s", err) } newSingleRedirect.Rules = newSingleRedirectRules diff --git a/providers/cloudflare/singleredirect.go b/providers/cloudflare/singleredirect.go index 033ac1a9d1..4c98fc217b 100644 --- a/providers/cloudflare/singleredirect.go +++ b/providers/cloudflare/singleredirect.go @@ -122,22 +122,40 @@ func makeRuleFromPattern(pattern, replacement string, temporary bool) (string, s // meta.*yodeya.com/* (wildcard in host) h := simpleGlobToRegex(host) matcher = fmt.Sprintf(`http.host matches r###"%s"###`, h) + + } else if !strings.Contains(host, `*`) && strings.Count(path, `*`) == 1 && strings.HasSuffix(path, "*") { + // domain.tld/.well-known* (wildcard in path) + matcher = fmt.Sprintf(`(starts_with(http.request.uri.path, "%s") and http.host eq "%s")`, + path[0:len(path)-1], + host) + } // replacement if !strings.Contains(replacement, `$`) { // https://stackexchange.com/ (no substitutions) - expr = fmt.Sprintf(`"%s"`, replacement) + expr = fmt.Sprintf(`concat("%s", "")`, replacement) + + } else if host[0] == '*' && strings.Count(host, `*`) == 1 && strings.Count(replacement, `$`) == 1 && len(rpath) > 3 && strings.HasSuffix(rpath, "/$2") { + // *stackoverflowenterprise.com/* -> https://www.stackoverflowbusiness.com/enterprise/$2 + expr = fmt.Sprintf(`concat("https://%s", "%s", http.request.uri.path)`, + rhost, + rpath[0:len(rpath)-3], + ) } else if strings.Count(replacement, `$`) == 1 && rpath == `/$1` { // https://i.sstatic.net/$1 ($1 at end) - expr = fmt.Sprintf(`concat("https://%s/", http.request.uri.path)`, rhost) + expr = fmt.Sprintf(`concat("https://%s", http.request.uri.path)`, rhost) } else if strings.Count(host, `*`) == 1 && strings.Count(path, `*`) == 1 && strings.Count(replacement, `$`) == 1 && rpath == `/$2` { // https://careers.stackoverflow.com/$2 - expr = fmt.Sprintf(`concat("https://%s/", http.request.uri.path)`, rhost) + expr = fmt.Sprintf(`concat("https://%s", http.request.uri.path)`, rhost) + + } else if strings.Count(replacement, `$`) == 1 && strings.HasSuffix(replacement, `$1`) { + // https://social.domain.tld/.well-known$1 + expr = fmt.Sprintf(`concat("https://%s", http.request.uri.path)`, rhost) } diff --git a/providers/cloudflare/singleredirect_test.go b/providers/cloudflare/singleredirect_test.go index 6c1ac23554..3ca1941c49 100644 --- a/providers/cloudflare/singleredirect_test.go +++ b/providers/cloudflare/singleredirect_test.go @@ -23,7 +23,7 @@ func Test_makeSingleDirectRule(t *testing.T) { pattern: "example.com/", replace: "foo.com", wantMatch: `http.host eq "example.com" and http.request.uri.path eq "/"`, - wantExpr: `"https://foo.com"`, + wantExpr: `concat("https://foo.com", "")`, wantErr: false, }, @@ -43,7 +43,7 @@ func Test_makeSingleDirectRule(t *testing.T) { pattern: "https://i-dev.sstatic.net/", replace: "https://stackexchange.com/", wantMatch: `http.host eq "i-dev.sstatic.net" and http.request.uri.path eq "/"`, - wantExpr: `"https://stackexchange.com/"`, + wantExpr: `concat("https://stackexchange.com/", "")`, wantErr: false, }, { @@ -51,7 +51,7 @@ func Test_makeSingleDirectRule(t *testing.T) { pattern: "https://i.stack.imgur.com/*", replace: "https://i.sstatic.net/$1", wantMatch: `http.host eq "i.stack.imgur.com"`, - wantExpr: `concat("https://i.sstatic.net/", http.request.uri.path)`, + wantExpr: `concat("https://i.sstatic.net", http.request.uri.path)`, wantErr: false, }, { @@ -59,7 +59,7 @@ func Test_makeSingleDirectRule(t *testing.T) { pattern: "https://img.stack.imgur.com/*", replace: "https://i.sstatic.net/$1", wantMatch: `http.host eq "img.stack.imgur.com"`, - wantExpr: `concat("https://i.sstatic.net/", http.request.uri.path)`, + wantExpr: `concat("https://i.sstatic.net", http.request.uri.path)`, wantErr: false, }, { @@ -67,7 +67,7 @@ func Test_makeSingleDirectRule(t *testing.T) { pattern: "https://insights.stackoverflow.com/", replace: "https://survey.stackoverflow.co", wantMatch: `http.host eq "insights.stackoverflow.com" and http.request.uri.path eq "/"`, - wantExpr: `"https://survey.stackoverflow.co"`, + wantExpr: `concat("https://survey.stackoverflow.co", "")`, wantErr: false, }, { @@ -75,7 +75,7 @@ func Test_makeSingleDirectRule(t *testing.T) { pattern: "https://insights.stackoverflow.com/trends", replace: "https://trends.stackoverflow.co", wantMatch: `http.host eq "insights.stackoverflow.com" and http.request.uri.path eq "/trends"`, - wantExpr: `"https://trends.stackoverflow.co"`, + wantExpr: `concat("https://trends.stackoverflow.co", "")`, wantErr: false, }, { @@ -83,7 +83,7 @@ func Test_makeSingleDirectRule(t *testing.T) { pattern: "https://insights.stackoverflow.com/trends/", replace: "https://trends.stackoverflow.co", wantMatch: `http.host eq "insights.stackoverflow.com" and http.request.uri.path eq "/trends/"`, - wantExpr: `"https://trends.stackoverflow.co"`, + wantExpr: `concat("https://trends.stackoverflow.co", "")`, wantErr: false, }, { @@ -91,7 +91,7 @@ func Test_makeSingleDirectRule(t *testing.T) { pattern: "https://insights.stackoverflow.com/survey/2021", replace: "https://survey.stackoverflow.co/2021", wantMatch: `http.host eq "insights.stackoverflow.com" and http.request.uri.path eq "/survey/2021"`, - wantExpr: `"https://survey.stackoverflow.co/2021"`, + wantExpr: `concat("https://survey.stackoverflow.co/2021", "")`, wantErr: false, }, // { @@ -108,7 +108,7 @@ func Test_makeSingleDirectRule(t *testing.T) { pattern: "*stackoverflow.help/support/solutions/articles/36000241656-write-an-article", replace: "https://stackoverflow.help/en/articles/4397209-write-an-article", wantMatch: `( http.host eq "stackoverflow.help" or ends_with(http.host, ".stackoverflow.help") ) and http.request.uri.path eq "/support/solutions/articles/36000241656-write-an-article"`, - wantExpr: `"https://stackoverflow.help/en/articles/4397209-write-an-article"`, + wantExpr: `concat("https://stackoverflow.help/en/articles/4397209-write-an-article", "")`, wantErr: false, }, { @@ -116,7 +116,7 @@ func Test_makeSingleDirectRule(t *testing.T) { pattern: "*stackoverflow.careers/*", replace: "https://careers.stackoverflow.com/$2", wantMatch: `http.host eq "stackoverflow.careers" or ends_with(http.host, ".stackoverflow.careers")`, - wantExpr: `concat("https://careers.stackoverflow.com/", http.request.uri.path)`, + wantExpr: `concat("https://careers.stackoverflow.com", http.request.uri.path)`, wantErr: false, }, { @@ -124,7 +124,7 @@ func Test_makeSingleDirectRule(t *testing.T) { pattern: "stackenterprise.com/*", replace: "https://stackoverflow.co/teams/", wantMatch: `http.host eq "stackenterprise.com"`, - wantExpr: `"https://stackoverflow.co/teams/"`, + wantExpr: `concat("https://stackoverflow.co/teams/", "")`, wantErr: false, }, { @@ -132,7 +132,7 @@ func Test_makeSingleDirectRule(t *testing.T) { pattern: "meta.*yodeya.com/*", replace: "https://judaism.meta.stackexchange.com/$2", wantMatch: `http.host matches r###"^meta\..*yodeya\.com$"###`, - wantExpr: `concat("https://judaism.meta.stackexchange.com/", http.request.uri.path)`, + wantExpr: `concat("https://judaism.meta.stackexchange.com", http.request.uri.path)`, wantErr: false, }, { @@ -140,7 +140,7 @@ func Test_makeSingleDirectRule(t *testing.T) { pattern: "chat.*yodeya.com/*", replace: "https://chat.stackexchange.com/?tab=site\u0026host=judaism.stackexchange.com", wantMatch: `http.host matches r###"^chat\..*yodeya\.com$"###`, - wantExpr: `"https://chat.stackexchange.com/?tab=site&host=judaism.stackexchange.com"`, + wantExpr: `concat("https://chat.stackexchange.com/?tab=site&host=judaism.stackexchange.com", "")`, wantErr: false, }, { @@ -148,7 +148,7 @@ func Test_makeSingleDirectRule(t *testing.T) { pattern: "*yodeya.com/*", replace: "https://judaism.stackexchange.com/$2", wantMatch: `http.host eq "yodeya.com" or ends_with(http.host, ".yodeya.com")`, - wantExpr: `concat("https://judaism.stackexchange.com/", http.request.uri.path)`, + wantExpr: `concat("https://judaism.stackexchange.com", http.request.uri.path)`, wantErr: false, }, { @@ -156,7 +156,7 @@ func Test_makeSingleDirectRule(t *testing.T) { pattern: "meta.*seasonedadvice.com/*", replace: "https://cooking.meta.stackexchange.com/$2", wantMatch: `http.host matches r###"^meta\..*seasonedadvice\.com$"###`, - wantExpr: `concat("https://cooking.meta.stackexchange.com/", http.request.uri.path)`, + wantExpr: `concat("https://cooking.meta.stackexchange.com", http.request.uri.path)`, wantErr: false, }, { @@ -164,7 +164,7 @@ func Test_makeSingleDirectRule(t *testing.T) { pattern: "collectivesonstackoverflow.co/*", replace: "https://stackoverflow.com/collectives-on-stack-overflow", wantMatch: `http.host eq "collectivesonstackoverflow.co"`, - wantExpr: `"https://stackoverflow.com/collectives-on-stack-overflow"`, + wantExpr: `concat("https://stackoverflow.com/collectives-on-stack-overflow", "")`, wantErr: false, }, { @@ -172,7 +172,7 @@ func Test_makeSingleDirectRule(t *testing.T) { pattern: "*collectivesonstackoverflow.co/*", replace: "https://stackoverflow.com/collectives-on-stack-overflow", wantMatch: `http.host eq "collectivesonstackoverflow.co" or ends_with(http.host, ".collectivesonstackoverflow.co")`, - wantExpr: `"https://stackoverflow.com/collectives-on-stack-overflow"`, + wantExpr: `concat("https://stackoverflow.com/collectives-on-stack-overflow", "")`, wantErr: false, }, { @@ -180,10 +180,48 @@ func Test_makeSingleDirectRule(t *testing.T) { pattern: "*stackexchange.ca/*", replace: "https://stackexchange.com/$2", wantMatch: `http.host eq "stackexchange.ca" or ends_with(http.host, ".stackexchange.ca")`, - wantExpr: `concat("https://stackexchange.com/", http.request.uri.path)`, + wantExpr: `concat("https://stackexchange.com", http.request.uri.path)`, wantErr: false, }, + + // https://github.com/StackExchange/dnscontrol/issues/2313#issuecomment-2197296025 + { + name: "pro-sumer1", + pattern: "domain.tld/.well-known*", + replace: "https://social.domain.tld/.well-known$1", + wantMatch: `(starts_with(http.request.uri.path, "/.well-known") and http.host eq "domain.tld")`, + wantExpr: `concat("https://social.domain.tld", http.request.uri.path)`, + wantErr: false, + }, + { + name: "pro-sumer2", + pattern: "domain.tld/users*", + replace: "https://social.domain.tld/users$1", + wantMatch: `(starts_with(http.request.uri.path, "/users") and http.host eq "domain.tld")`, + wantExpr: `concat("https://social.domain.tld", http.request.uri.path)`, + wantErr: false, + }, + { + name: "pro-sumer3", + pattern: "domain.tld/@*", + replace: `https://social.domain.tld/@$1`, + wantMatch: `(starts_with(http.request.uri.path, "/@") and http.host eq "domain.tld")`, + wantExpr: `concat("https://social.domain.tld", http.request.uri.path)`, + wantErr: false, + }, + + { + name: "stackentwild", + pattern: "*stackoverflowenterprise.com/*", + replace: "https://www.stackoverflowbusiness.com/enterprise/$2", + wantMatch: `http.host eq "stackoverflowenterprise.com" or ends_with(http.host, ".stackoverflowenterprise.com")`, + wantExpr: `concat("https://www.stackoverflowbusiness.com", "/enterprise", http.request.uri.path)`, + wantErr: false, + }, + + // } + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotMatch, gotExpr, err := makeRuleFromPattern(tt.pattern, tt.replace, true)