fix(plugins): add regex length limit (#7802)

This commit is contained in:
Ville Vesilehto
2026-01-05 19:48:48 +02:00
committed by GitHub
parent adba778626
commit b723bd94d4
15 changed files with 117 additions and 3 deletions

View File

@@ -74,7 +74,8 @@ The match type, e.g., `exact`, `substring`, etc., triggers rewrite:
* **substring**: on a partial match of the name in the question section of a request
* **prefix**: when the name begins with the matching string
* **suffix**: when the name ends with the matching string
* **regex**: when the name in the question section of a request matches a regular expression
* **regex**: when the name in the question section of a request matches a regular expression.
Regex patterns must not exceed 10000 characters.
If the match type is omitted, the `exact` match type is assumed. If OPTIONS are
given, the type must be specified.

View File

@@ -144,6 +144,9 @@ func newCNAMERule(nextAction string, args ...string) (Rule, error) {
Upstream: upstream.New(),
}
if rewriteType == RegexMatch {
if len(paramFromTarget) > maxRegexpLen {
return nil, fmt.Errorf("regex pattern too long in a cname rule: %d > %d", len(paramFromTarget), maxRegexpLen)
}
re, err := regexp.Compile(paramFromTarget)
if err != nil {
return nil, fmt.Errorf("invalid cname rewrite regex pattern: %w", err)

View File

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"reflect"
"strings"
"testing"
"github.com/coredns/coredns/plugin"
@@ -263,3 +264,14 @@ func TestCNAMETargetRewrite_upstreamFailurePaths(t *testing.T) {
})
}
}
func TestNewCNAMERuleLargeRegex(t *testing.T) {
largeRegex := strings.Repeat("a", maxRegexpLen+1)
_, err := newCNAMERule("stop", "regex", largeRegex, "replacement")
if err == nil {
t.Fatal("Expected error for large regex, got nil")
}
if !strings.Contains(err.Error(), "too long") {
t.Errorf("Expected 'too long' error, got: %v", err)
}
}

View File

@@ -13,6 +13,10 @@ import (
"github.com/miekg/dns"
)
// maxRegexpLen is a hard limit on the length of a regex pattern to prevent
// OOM during regex compilation with malicious input.
const maxRegexpLen = 10000
// stringRewriter rewrites a string
type stringRewriter interface {
rewriteString(src string) string
@@ -438,6 +442,9 @@ func getSubExprUsage(s string) int {
// isValidRegexPattern returns a regular expression for pattern matching or errors, if any.
func isValidRegexPattern(rewriteFrom, rewriteTo string) (*regexp.Regexp, error) {
if len(rewriteFrom) > maxRegexpLen {
return nil, fmt.Errorf("regex pattern too long: %d > %d", len(rewriteFrom), maxRegexpLen)
}
rewriteFromPattern, err := regexp.Compile(rewriteFrom)
if err != nil {
return nil, fmt.Errorf("invalid regex matching pattern: %s", rewriteFrom)

View File

@@ -368,3 +368,14 @@ func TestNewNameRule(t *testing.T) {
t.Fatalf("Test %d: FAIL, expected fail=%t, but received fail=%t: (%s) %s, rule=%v", i, tc.expectedFail, failed, tc.next, tc.args, rule)
}
}
func TestNewNameRuleLargeRegex(t *testing.T) {
largeRegex := strings.Repeat("a", maxRegexpLen+1)
_, err := newNameRule("stop", "regex", largeRegex, "replacement")
if err == nil {
t.Fatal("Expected error for large regex, got nil")
}
if !strings.Contains(err.Error(), "too long") {
t.Errorf("Expected 'too long' error, got: %v", err)
}
}

View File

@@ -142,6 +142,9 @@ func newRCodeRule(nextAction string, args ...string) (Rule, error) {
plugin.Name(args[1]).Normalize(),
}, nil
case RegexMatch:
if len(args[1]) > maxRegexpLen {
return nil, fmt.Errorf("regex pattern too long in a rcode rule: %d > %d", len(args[1]), maxRegexpLen)
}
regexPattern, err := regexp.Compile(args[1])
if err != nil {
return nil, fmt.Errorf("invalid regex pattern in a rcode rule: %s", args[1])

View File

@@ -1,6 +1,7 @@
package rewrite
import (
"strings"
"testing"
"github.com/coredns/coredns/plugin/test"
@@ -70,3 +71,14 @@ func TestRCodeRewrite(t *testing.T) {
t.Fatalf("RCode rewrite did not apply changes, request=%#v, err=%v", request.Req, err)
}
}
func TestNewRCodeRuleLargeRegex(t *testing.T) {
largeRegex := strings.Repeat("a", maxRegexpLen+1)
_, err := newRCodeRule("stop", "regex", largeRegex, "SERVFAIL", "NXDOMAIN")
if err == nil {
t.Fatal("Expected error for large regex, got nil")
}
if !strings.Contains(err.Error(), "too long") {
t.Errorf("Expected 'too long' error, got: %v", err)
}
}

View File

@@ -140,6 +140,9 @@ func newTTLRule(nextAction string, args ...string) (Rule, error) {
plugin.Name(args[1]).Normalize(),
}, nil
case RegexMatch:
if len(args[1]) > maxRegexpLen {
return nil, fmt.Errorf("regex pattern too long in a ttl rule: %d > %d", len(args[1]), maxRegexpLen)
}
regexPattern, err := regexp.Compile(args[1])
if err != nil {
return nil, fmt.Errorf("invalid regex pattern in a ttl rule: %s", args[1])

View File

@@ -3,6 +3,7 @@ package rewrite
import (
"context"
"reflect"
"strings"
"testing"
"github.com/coredns/coredns/plugin"
@@ -156,3 +157,14 @@ func doTTLTests(t *testing.T, rules []Rule) {
}
}
}
func TestNewTTLRuleLargeRegex(t *testing.T) {
largeRegex := strings.Repeat("a", maxRegexpLen+1)
_, err := newTTLRule("stop", "regex", largeRegex, "300")
if err == nil {
t.Fatal("Expected error for large regex, got nil")
}
if !strings.Contains(err.Error(), "too long") {
t.Errorf("Expected 'too long' error, got: %v", err)
}
}