CVE-2024-21527: Insufficient Information (NVD-CWE-noinfo)
value: Versions of the package github.com/gotenberg/gotenberg/v8/pkg/gotenberg before 8.1.0; versions of the package github.com/gotenberg/gotenberg/v8/pkg/modules/chromium before 8.1.0; versions of the package github.com/gotenberg/gotenberg/v8/pkg/modules/webhook before 8.1.0 are vulnerable to Server-side Request Forgery (SSRF) via the /convert/html endpoint when a request is made to a file via localhost, such as <iframe src="\\localhost/etc/passwd">. By exploiting this vulnerability, an attacker can achieve local file inclusion, allowing of sensitive files read on the host system. WorkaroundAn alternative is using either or both --chromium-deny-list and --chromium-allow-list flags.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-07-19T05:15Z
Repository: https://github.com/gotenberg/gotenberg
Language: Go
Files changed: Makefile, pkg/gotenberg/flags.go, pkg/gotenberg/regexp.go, pkg/gotenberg/regexp_test.go, pkg/gotenberg/supervisor_test.go, pkg/modules/chromium/browser.go, pkg/modules/chromium/browser_test.go, pkg/modules/chromium/chromium.go, pkg/modules/chromium/events.go, pkg/modules/chromium/routes.go, pkg/modules/chromium/routes_test.go, pkg/modules/webhook/middleware.go, pkg/modules/webhook/middleware_test.go, pkg/modules/webhook/webhook.go
Commit date: 2024-02-18 17:18:40 +0100
Commit message: fix(chromium): better default deny list regexp

Diff: diff --git a/Makefile b/Makefile
index 21b316c..64ee426 100644
--- a/Makefile
+++ b/Makefile
@@ -44,11 +44,11 @@ CHROMIUM_IGNORE_CERTIFICATE_ERRORS=false
 CHROMIUM_DISABLE_WEB_SECURITY=false
 CHROMIUM_ALLOW_FILE_ACCESS_FROM_FILES=false
 CHROMIUM_HOST_RESOLVER_RULES=
 CHROMIUM_PROXY_SERVER=
 CHROMIUM_ALLOW_LIST=
-CHROMIUM_DENY_LIST="^file:///[^tmp].*"
+CHROMIUM_DENY_LIST=^file:(?!//\/tmp/).*
 CHROMIUM_CLEAR_CACHE=false
 CHROMIUM_CLEAR_COOKIES=false
 CHROMIUM_DISABLE_JAVASCRIPT=false
 CHROMIUM_DISABLE_ROUTES=false
 LIBREOFFICE_RESTART_AFTER=10
@@ -98,12 +98,12 @@ run: ## Start a Gotenberg container
 	--chromium-ignore-certificate-errors=$(CHROMIUM_IGNORE_CERTIFICATE_ERRORS) \
 	--chromium-disable-web-security=$(CHROMIUM_DISABLE_WEB_SECURITY) \
 	--chromium-allow-file-access-from-files=$(CHROMIUM_ALLOW_FILE_ACCESS_FROM_FILES) \
 	--chromium-host-resolver-rules=$(CHROMIUM_HOST_RESOLVER_RULES) \
 	--chromium-proxy-server=$(CHROMIUM_PROXY_SERVER) \
-	--chromium-allow-list=$(CHROMIUM_ALLOW_LIST) \
-	--chromium-deny-list=$(CHROMIUM_DENY_LIST) \
+	--chromium-allow-list="$(CHROMIUM_ALLOW_LIST)" \
+	--chromium-deny-list="$(CHROMIUM_DENY_LIST)" \
 	--chromium-clear-cache=$(CHROMIUM_CLEAR_CACHE) \
 	--chromium-clear-cookies=$(CHROMIUM_CLEAR_COOKIES) \
 	--chromium-disable-javascript=$(CHROMIUM_DISABLE_JAVASCRIPT) \
 	--chromium-disable-routes=$(CHROMIUM_DISABLE_ROUTES) \
 	--libreoffice-restart-after=$(LIBREOFFICE_RESTART_AFTER) \
@@ -118,12 +118,12 @@ run: ## Start a Gotenberg container
 	--pdfengines-disable-routes=$(PDFENGINES_DISABLE_ROUTES) \
 	--prometheus-namespace=$(PROMETHEUS_NAMESPACE) \
 	--prometheus-collect-interval=$(PROMETHEUS_COLLECT_INTERVAL) \
 	--prometheus-disable-route-logging=$(PROMETHEUS_DISABLE_ROUTE_LOGGING) \
 	--prometheus-disable-collect=$(PROMETHEUS_DISABLE_COLLECT) \
-	--webhook-allow-list=$(WEBHOOK_ALLOW_LIST) \
-	--webhook-deny-list=$(WEBHOOK_DENY_LIST) \
+	--webhook-allow-list="$(WEBHOOK_ALLOW_LIST)" \
+	--webhook-deny-list="$(WEBHOOK_DENY_LIST)" \
 	--webhook-error-allow-list=$(WEBHOOK_ERROR_ALLOW_LIST) \
 	--webhook-error-deny-list=$(WEBHOOK_ERROR_DENY_LIST) \
 	--webhook-max-retry=$(WEBHOOK_MAX_RETRY) \
 	--webhook-retry-min-wait=$(WEBHOOK_RETRY_MIN_WAIT) \
 	--webhook-retry-max-wait=$(WEBHOOK_RETRY_MAX_WAIT) \
diff --git a/pkg/gotenberg/flags.go b/pkg/gotenberg/flags.go
index 80d1704..3cc8366 100644
--- a/pkg/gotenberg/flags.go
+++ b/pkg/gotenberg/flags.go
@@ -1,11 +1,11 @@
 package gotenberg
 
 import (
-	"regexp"
 	"time"
 
+	"github.com/dlclark/regexp2"
 	"github.com/labstack/gommon/bytes"
 	flag "github.com/spf13/pflag"
 )
 
 // ParsedFlags wraps a [flag.FlagSet] so that retrieving the typed values is
@@ -197,23 +197,23 @@ func (f *ParsedFlags) MustDeprecatedHumanReadableBytesString(deprecated string,
 	return f.MustHumanReadableBytesString(newName)
 }
 
 // MustRegexp returns the regular expression of a flag given by name.
 // It panics if an error occurs.
-func (f *ParsedFlags) MustRegexp(name string) *regexp.Regexp {
+func (f *ParsedFlags) MustRegexp(name string) *regexp2.Regexp {
 	val, err := f.GetString(name)
 	if err != nil {
 		panic(err)
 	}
 
-	return regexp.MustCompile(val)
+	return regexp2.MustCompile(val, 0)
 }
 
 // MustDeprecatedRegexp returns the regular expression of a deprecated flag if
 // it was explicitly set or the regular expression of the new flag.
 // It panics if an error occurs.
-func (f *ParsedFlags) MustDeprecatedRegexp(deprecated string, newName string) *regexp.Regexp {
+func (f *ParsedFlags) MustDeprecatedRegexp(deprecated string, newName string) *regexp2.Regexp {
 	if f.Changed(deprecated) {
 		return f.MustRegexp(deprecated)
 	}
 
 	return f.MustRegexp(newName)
diff --git a/pkg/gotenberg/regexp.go b/pkg/gotenberg/regexp.go
new file mode 100644
index 0000000..c627c08
--- /dev/null
+++ b/pkg/gotenberg/regexp.go
@@ -0,0 +1,53 @@
+package gotenberg
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"time"
+
+	"github.com/dlclark/regexp2"
+)
+
+// ErrFiltered happens if a value is filtered by the [FilterDeadline] function.
+var ErrFiltered = errors.New("value filtered")
+
+// FilterDeadline checks if given value is allowed and not denied according to
+// regex patterns. It returns a [context.DeadlineExceeded] if it takes too long
+// to process.
+func FilterDeadline(allowed, denied *regexp2.Regexp, s string, deadline time.Time) error {
+	// FIXME: not ideal to compile everytime, but is there another way to create a clone?
+	if allowed.String() != "" {
+		allow := regexp2.MustCompile(allowed.String(), 0)
+		allow.MatchTimeout = time.Until(deadline)
+
+		ok, err := allow.MatchString(s)
+		if err != nil {
+			if time.Now().After(deadline) {
+				return context.DeadlineExceeded
+			}
+			return fmt.Errorf("'%s' cannot handle '%s': %w", allow.String(), s, err)
+		}
+		if !ok {
+			return fmt.Errorf("'%s' does not match the expression from the allowed list: %w", s, ErrFiltered)
+		}
+	}
+
+	if denied.String() != "" {
+		deny := regexp2.MustCompile(denied.String(), 0)
+		deny.MatchTimeout = time.Until(deadline)
+
+		ok, err := deny.MatchString(s)
+		if err != nil {
+			if time.Now().After(deadline) {
+				return context.DeadlineExceeded
+			}
+			return fmt.Errorf("'%s' cannot handle '%s': %w", deny.String(), s, err)
+		}
+		if ok {
+			return fmt.Errorf("'%s' matches the expression from the denied list: %w", s, ErrFiltered)
+		}
+	}
+
+	return nil
+}
diff --git a/pkg/gotenberg/regexp_test.go b/pkg/gotenberg/regexp_test.go
new file mode 100644
index 0000000..45c38ad
--- /dev/null
+++ b/pkg/gotenberg/regexp_test.go
@@ -0,0 +1,83 @@
+package gotenberg
+
+import (
+	"context"
+	"errors"
+	"testing"
+	"time"
+
+	"github.com/dlclark/regexp2"
+)
+
+func TestFilterDeadline(t *testing.T) {
+	for _, tc := range []struct {
+		scenario      string
+		allowed       *regexp2.Regexp
+		denied        *regexp2.Regexp
+		s             string
+		deadline      time.Time
+		expectError   bool
+		expectedError error
+	}{
+		{
+			scenario:      "DeadlineExceeded (allowed)",
+			allowed:       regexp2.MustCompile("foo", 0),
+			denied:        regexp2.MustCompile("", 0),
+			s:             "foo",
+			deadline:      time.Now().Add(time.Duration(-1) * time.Hour),
+			expectError:   true,
+			expectedError: context.DeadlineExceeded,
+		},
+		{
+			scenario:      "ErrFiltered (allowed)",
+			allowed:       regexp2.MustCompile("foo", 0),
+			denied:        regexp2.MustCompile("", 0),
+			s:             "bar",
+			deadline:      time.Now().Add(time.Duration(5) * time.Second),
+			expectError:   true,
+			expectedError: ErrFiltered,
+		},
+		{
+			scenario:      "DeadlineExceeded (denied)",
+			allowed:       regexp2.MustCompile("", 0),
+			denied:        regexp2.MustCompile("foo", 0),
+			s:             "foo",
+			deadline:      time.Now().Add(time.Duration(-1) * time.Hour),
+			expectError:   true,
+			expectedError: context.DeadlineExceeded,
+		},
+		{
+			scenario:      "ErrFiltered (denied)",
+			allowed:       regexp2.MustCompile("", 0),
+			denied:        regexp2.MustCompile("foo", 0),
+			s:             "foo",
+			deadline:      time.Now().Add(time.Duration(5) * time.Second),
+			expectError:   true,
+			expectedError: ErrFiltered,
+		},
+		{
+			scenario:    "success",
+			allowed:     regexp2.MustCompile("", 0),
+			denied:      regexp2.MustCompile("", 0),
+			s:           "foo",
+			deadline:    time.Now().Add(time.Duration(5) * time.Second),
+			expectError: false,
+		},
+	} {
+		t.Run(tc.scenario, func(t *testing.T) {
+			err := FilterDeadline(tc.allowed, tc.denied, tc.s, tc.deadline)
+
+			if tc.expectError && err == nil {
+				t.Fatal("expected an error but got none")
+			}
+
+			if !tc.expectError && err != nil {
+				t.Fatalf("expected no error but got: %v", err)
+			}
+
+			if tc.expectedError != nil && !errors.Is(err, tc.expectedError) {
+				t.Fatalf("expected error %v but got: %v", tc.expectedError, err)
+			}
+		})
+	}
+}
diff --git a/pkg/gotenberg/supervisor_test.go b/pkg/gotenberg/supervisor_test.go
index ef7eb4b..10f381b 100644
--- a/pkg/gotenberg/supervisor_test.go
+++ b/pkg/gotenberg/supervisor_test.go
@@ -485,14 +485,14 @@ func TestProcessSupervisor_runWithDeadline(t *testing.T) {
 	} {
 		t.Run(tc.scenario, func(t *testing.T) {
 			ps := NewProcessSupervisor(zap.NewNop(), new(ProcessMock), 0, 0).(*processSupervisor)
 
 			ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
-			defer cancel()
-
 			if tc.ctxDone {
 				cancel()
+			} else {
+				defer cancel()
 			}
 
 			err := ps.runWithDeadline(ctx, func() error {
 				return nil
 			})
diff --git a/pkg/modules/chromium/browser.go b/pkg/modules/chromium/browser.go
index fd2dd13..bb23ae6 100644
--- a/pkg/modules/chromium/browser.go
+++ b/pkg/modules/chromium/browser.go
@@ -3,20 +3,20 @@ package chromium
 import (
 	"context"
 	"errors"
 	"fmt"
 	"os"
-	"regexp"
 	"strings"
 	"sync"
 	"sync/atomic"
 	"time"
 
 	"github.com/chromedp/cdproto/fetch"
 	"github.com/chromedp/cdproto/network"
 	"github.com/chromedp/cdproto/runtime"
 	"github.com/chromedp/chromedp"
+	"github.com/dlclark/regexp2"
 	"go.uber.org/zap"
 
 	"github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
 )
 
@@ -37,12 +37,12 @@ type browserArguments struct {
 	hostResolverRules        string
 	proxyServer              string
 	wsUrlReadTimeout         time.Duration
 
 	// Tasks specific.
-	allowList         *regexp.Regexp
-	denyList          *regexp.Regexp
+	allowList         *regexp2.Regexp
+	denyList          *regexp2.Regexp
 	clearCache        bool
 	clearCookies      bool
 	disableJavaScript bool
 }
 
@@ -261,24 +261,21 @@ func (b *chromiumBrowser) screenshot(ctx context.Context, logger *zap.Logger, ur
 func (b *chromiumBrowser) do(ctx context.Context, logger *zap.Logger, url string, options Options, tasks chromedp.Tasks) error {
 	if !b.isStarted.Load() {
 		return errors.New("browser not started, cannot handle tasks")
 	}
 
-	// We validate the "main" URL against our allow / deny lists.
-	if !b.arguments.allowList.MatchString(url) {
-		return fmt.Errorf("'%s' does not match the expression from the allowed list: %w", url, ErrUrlNotAuthorized)
-	}
-
-	if b.arguments.denyList.String() != "" && b.arguments.denyList.MatchString(url) {
-		return fmt.Errorf("'%s' matches the expression from the denied list: %w", url, ErrUrlNotAuthorized)
-	}
-
 	deadline, ok := ctx.Deadline()
 	if !ok {
 		return errors.New("context has no deadline")
 	}
 
+	// We validate the "main" URL against our allow / deny lists.
+	err := gotenberg.FilterDeadline(b.arguments.allowList, b.arguments.denyList, url, deadline)
+	if err != nil {
+		return fmt.Errorf("filter URL: %w", err)
+	}
+
 	b.ctxMu.RLock()
 	defer b.ctxMu.RUnlock()
 
 	timeoutCtx, timeoutCancel := context.WithTimeout(b.ctx, time.Until(deadline))
 	defer timeoutCancel()
@@ -308,11 +305,11 @@ func (b *chromiumBrowser) do(ctx context.Context, logger *zap.Logger, url string
 	// See https://github.com/gotenberg/gotenberg/issues/262.
 	if options.FailOnConsoleExceptions && !b.arguments.disableJavaScript {
 		listenForEventExceptionThrown(taskCtx, logger, &consoleExceptions, &consoleExceptionsMu)
 	}
 
-	err := chromedp.Run(taskCtx, tasks...)
+	err = chromedp.Run(taskCtx, tasks...)
 	if err != nil {
 		errMessage := err.Error()
 
 		if strings.Contains(errMessage, "Show invalid printer settings error (-32000)") || strings.Contains(errMessage, "content area is empty (-32602)") {
 			return ErrInvalidPrinterSettings
diff --git a/pkg/modules/chromium/browser_test.go b/pkg/modules/chromium/browser_test.go
index db2cd18..4a4bc54 100644
--- a/pkg/modules/chromium/browser_test.go
+++ b/pkg/modules/chromium/browser_test.go
@@ -3,15 +3,15 @@ package chromium
 import (
 	"context"
 	"errors"
 	"fmt"
 	"os"
-	"regexp"
 	"strings"
 	"testing"
 	"time"
 
+	"github.com/dlclark/regexp2"
 	"github.com/google/uuid"
 	"go.uber.org/zap"
 	"go.uber.org/zap/zapcore"
 	"go.uber.org/zap/zaptest/observer"
 
@@ -266,66 +266,63 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			noDeadline:  false,
 			start:       false,
 			expectError: true,
 		},
 		{
-			scenario: "ErrUrlNotAuthorized: main URL does not match the allowed list",
+			scenario: "context has no deadline",
 			browser: func() browser {
 				b := new(chromiumBrowser)
-				b.arguments = browserArguments{
-					allowList: regexp.MustCompile("^file:///[^tmp].*"),
-				}
 				b.isStarted.Store(true)
 				return b
 			}(),
-			fs:            gotenberg.NewFileSystem(),
-			noDeadline:    false,
-			start:         false,
-			expectError:   true,
-			expectedError: ErrUrlNotAuthorized,
+			fs:          gotenberg.NewFileSystem(),
+			noDeadline:  true,
+			start:       false,
+			expectError: true,
 		},
 		{
-			scenario: "ErrUrlNotAuthorized: main URL does match the denied list",
+			scenario: "ErrFiltered: main URL does not match the allowed list",
 			browser: func() browser {
 				b := new(chromiumBrowser)
 				b.arguments = browserArguments{
-					allowList: regexp.MustCompile(""),
-					denyList:  regexp.MustCompile("^file:///tmp.*"),
+					allowList: regexp2.MustCompile(`^file:(?!//\/tmp/).*`, 0),
+					denyList:  regexp2.MustCompile("", 0),
 				}
 				b.isStarted.Store(true)
 				return b
 			}(),
 			fs:            gotenberg.NewFileSystem(),
 			noDeadline:    false,
 			start:         false,
 			expectError:   true,
-			expectedError: ErrUrlNotAuthorized,
+			expectedError: gotenberg.ErrFiltered,
 		},
 		{
-			scenario: "ErrUrlNotAuthorized: main URL does match the denied list",
+			scenario: "ErrFiltered: main URL does match the denied list",
 			browser: func() browser {
 				b := new(chromiumBrowser)
 				b.arguments = browserArguments{
-					allowList: regexp.MustCompile(""),
-					denyList:  regexp.MustCompile(""),
+					allowList: regexp2.MustCompile("", 0),
+					denyList:  regexp2.MustCompile("^file:///tmp.*", 0),
 				}
 				b.isStarted.Store(true)
 				return b
 			}(),
-			fs:          gotenberg.NewFileSystem(),
-			noDeadline:  true,
-			start:       false,
-			expectError: true,
+			fs:            gotenberg.NewFileSystem(),
+			noDeadline:    false,
+			start:         false,
+			expectError:   true,
+			expectedError: gotenberg.ErrFiltered,
 		},
 		{
 			scenario: "a request does not match the allowed list",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile("^file:///tmp.*"),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("^file:///tmp.*", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -352,12 +349,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "a request does match the denied list",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile("^file:///[^tmp].*"),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile(`^file:(?!//\/tmp/).*`, 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -384,12 +381,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "skip networkIdle event",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -419,12 +416,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "ErrInvalidHttpStatusCode",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -452,12 +449,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "ErrConsoleExceptions",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -485,12 +482,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "clear cache",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 					clearCache:       true,
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
@@ -518,12 +515,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "clear cookies",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 					clearCookies:     true,
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
@@ -551,12 +548,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "disable JavaScript",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:           os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout:  5 * time.Second,
-					allowList:         regexp.MustCompile(""),
-					denyList:          regexp.MustCompile(""),
+					allowList:         regexp2.MustCompile("", 0),
+					denyList:          regexp2.MustCompile("", 0),
 					disableJavaScript: true,
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
@@ -586,12 +583,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "extra HTTP headers",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -623,12 +620,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "ErrOmitBackgroundWithoutPrintBackground",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -656,12 +653,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "hide default white background",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -692,12 +689,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "ErrInvalidEmulatedMediaType",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -725,12 +722,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "emulate a media type",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -760,12 +757,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "wait delay: context done",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -795,12 +792,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "wait delay",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -830,12 +827,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "wait for expression: context done",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -876,12 +873,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "ErrInvalidEvaluationExpression",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -911,12 +908,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "wait for expression",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -957,12 +954,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "custom header and footer",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -993,12 +990,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "ErrInvalidPrinterSettings",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -1031,12 +1028,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "ErrPageRangesSyntaxError",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -1064,12 +1061,12 @@ func TestChromiumBrowser_pdf(t *testing.T) {
 			scenario: "success (default options)",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -1200,66 +1197,67 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			noDeadline:  false,
 			start:       false,
 			expectError: true,
 		},
 		{
-			scenario: "ErrUrlNotAuthorized: main URL does not match the allowed list",
+			scenario: "context has not deadline",
 			browser: func() browser {
 				b := new(chromiumBrowser)
 				b.arguments = browserArguments{
-					allowList: regexp.MustCompile("^file:///[^tmp].*"),
+					allowList: regexp2.MustCompile("", 0),
+					denyList:  regexp2.MustCompile("", 0),
 				}
 				b.isStarted.Store(true)
 				return b
 			}(),
-			fs:            gotenberg.NewFileSystem(),
-			noDeadline:    false,
-			start:         false,
-			expectError:   true,
-			expectedError: ErrUrlNotAuthorized,
+			fs:          gotenberg.NewFileSystem(),
+			noDeadline:  true,
+			start:       false,
+			expectError: true,
 		},
 		{
-			scenario: "ErrUrlNotAuthorized: main URL does match the denied list",
+			scenario: "ErrFiltered: main URL does not match the allowed list",
 			browser: func() browser {
 				b := new(chromiumBrowser)
 				b.arguments = browserArguments{
-					allowList: regexp.MustCompile(""),
-					denyList:  regexp.MustCompile("^file:///tmp.*"),
+					allowList: regexp2.MustCompile(`^file:(?!//\/tmp/).*`, 0),
+					denyList:  regexp2.MustCompile("", 0),
 				}
 				b.isStarted.Store(true)
 				return b
 			}(),
 			fs:            gotenberg.NewFileSystem(),
 			noDeadline:    false,
 			start:         false,
 			expectError:   true,
-			expectedError: ErrUrlNotAuthorized,
+			expectedError: gotenberg.ErrFiltered,
 		},
 		{
-			scenario: "ErrUrlNotAuthorized: main URL does match the denied list",
+			scenario: "ErrFiltered: main URL does match the denied list",
 			browser: func() browser {
 				b := new(chromiumBrowser)
 				b.arguments = browserArguments{
-					allowList: regexp.MustCompile(""),
-					denyList:  regexp.MustCompile(""),
+					allowList: regexp2.MustCompile("", 0),
+					denyList:  regexp2.MustCompile("^file:///tmp.*", 0),
 				}
 				b.isStarted.Store(true)
 				return b
 			}(),
-			fs:          gotenberg.NewFileSystem(),
-			noDeadline:  true,
-			start:       false,
-			expectError: true,
+			fs:            gotenberg.NewFileSystem(),
+			noDeadline:    false,
+			start:         false,
+			expectError:   true,
+			expectedError: gotenberg.ErrFiltered,
 		},
 		{
 			scenario: "a request does not match the allowed list",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile("^file:///tmp.*"),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("^file:///tmp.*", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -1286,12 +1284,12 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			scenario: "a request does match the denied list",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile("^file:///[^tmp].*"),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile(`^file:(?!//\/tmp/).*`, 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -1318,12 +1316,12 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			scenario: "skip networkIdle event",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -1353,12 +1351,12 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			scenario: "ErrInvalidHttpStatusCode",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -1386,12 +1384,12 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			scenario: "ErrConsoleExceptions",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -1419,12 +1417,12 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			scenario: "clear cache",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 					clearCache:       true,
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
@@ -1452,12 +1450,12 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			scenario: "clear cookies",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 					clearCookies:     true,
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
@@ -1485,12 +1483,12 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			scenario: "disable JavaScript",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:           os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout:  5 * time.Second,
-					allowList:         regexp.MustCompile(""),
-					denyList:          regexp.MustCompile(""),
+					allowList:         regexp2.MustCompile("", 0),
+					denyList:          regexp2.MustCompile("", 0),
 					disableJavaScript: true,
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
@@ -1520,12 +1518,12 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			scenario: "extra HTTP headers",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -1557,12 +1555,12 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			scenario: "ErrOmitBackgroundWithoutPrintBackground",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -1592,12 +1590,12 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			scenario: "ErrInvalidEmulatedMediaType",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -1625,12 +1623,12 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			scenario: "emulate a media type",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -1660,12 +1658,12 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			scenario: "wait delay: context done",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -1695,12 +1693,12 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			scenario: "wait delay",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -1730,12 +1728,12 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			scenario: "wait for expression: context done",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -1776,12 +1774,12 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			scenario: "ErrInvalidEvaluationExpression",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -1811,12 +1809,12 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			scenario: "wait for expression",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -1857,12 +1855,12 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			scenario: "success (default options)",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
@@ -1898,12 +1896,12 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
 			scenario: "success (jpeg)",
 			browser: newChromiumBrowser(
 				browserArguments{
 					binPath:          os.Getenv("CHROMIUM_BIN_PATH"),
 					wsUrlReadTimeout: 5 * time.Second,
-					allowList:        regexp.MustCompile(""),
-					denyList:         regexp.MustCompile(""),
+					allowList:        regexp2.MustCompile("", 0),
+					denyList:         regexp2.MustCompile("", 0),
 				},
 			),
 			fs: func() *gotenberg.FileSystem {
 				fs := gotenberg.NewFileSystem()
 
diff --git a/pkg/modules/chromium/chromium.go b/pkg/modules/chromium/chromium.go
index 4e73410..ea5706e 100644
--- a/pkg/modules/chromium/chromium.go
+++ b/pkg/modules/chromium/chromium.go
@@ -18,14 +18,10 @@ import (
 func init() {
 	gotenberg.MustRegisterModule(new(Chromium))
 }
 
 var (
-	// ErrUrlNotAuthorized happens if a URL is not acceptable according to the
-	// allowed/denied lists.
-	ErrUrlNotAuthorized = errors.New("URL not authorized")
-
 	// ErrInvalidEmulatedMediaType happens if the emulated media type is not
 	// "screen" nor "print". Empty value are allowed though.
 	ErrInvalidEmulatedMediaType = errors.New("invalid emulated media type")
 
 	// ErrInvalidEvaluationExpression happens if an evaluation expression
@@ -289,11 +285,11 @@ func (mod *Chromium) Descriptor() gotenberg.ModuleDescriptor {
 			fs.Bool("chromium-disable-web-security", false, "Don't enforce the same-origin policy")
 			fs.Bool("chromium-allow-file-access-from-files", false, "Allow file:// URIs to read other file:// URIs")
 			fs.String("chromium-host-resolver-rules", "", "Set custom mappings to the host resolver")
 			fs.String("chromium-proxy-server", "", "Set the outbound proxy server; this switch only affects HTTP and HTTPS requests")
 			fs.String("chromium-allow-list", "", "Set the allowed URLs for Chromium using a regular expression")
-			fs.String("chromium-deny-list", "^file:///[^tmp].*", "Set the denied URLs for Chromium using a regular expression")
+			fs.String("chromium-deny-list", `^file:(?!//\/tmp/).*`, "Set the denied URLs for Chromium using a regular expression")
 			fs.Bool("chromium-clear-cache", false, "Clear Chromium cache between each conversion")
 			fs.Bool("chromium-clear-cookies", false, "Clear Chromium cookies between each conversion")
 			fs.Bool("chromium-disable-javascript", false, "Disable JavaScript")
 			fs.Bool("chromium-disable-routes", false, "Disable the routes")
 
diff --git a/pkg/modules/chromium/events.go b/pkg/modules/chromium/events.go
index e5128c3..a329774 100644
--- a/pkg/modules/chromium/events.go
+++ b/pkg/modules/chromium/events.go
@@ -1,60 +1,63 @@
 package chromium
 
 import (
 	"context"
 	"fmt"
-	"regexp"
 	"slices"
 	"sync"
 
 	"github.com/chromedp/cdproto/cdp"
 	"github.com/chromedp/cdproto/fetch"
 	"github.com/chromedp/cdproto/network"
 	"github.com/chromedp/cdproto/page"
 	"github.com/chromedp/cdproto/runtime"
 	"github.com/chromedp/chromedp"
+	"github.com/dlclark/regexp2"
 	"go.uber.org/multierr"
 	"go.uber.org/zap"
 	"golang.org/x/sync/errgroup"
+
+	"github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
 )
 
 // listenForEventRequestPaused listens for requests to check if they are
 // allowed or not.
-func listenForEventRequestPaused(ctx context.Context, logger *zap.Logger, allowList *regexp.Regexp, denyList *regexp.Regexp) {
+func listenForEventRequestPaused(ctx context.Context, logger *zap.Logger, allowList *regexp2.Regexp, denyList *regexp2.Regexp) {
 	chromedp.ListenTarget(ctx, func(ev interface{}) {
 		switch e := ev.(type) {
 		case *fetch.EventRequestPaused:
 			go func() {
 				logger.Debug(fmt.Sprintf("event EventRequestPaused fired for '%s'", e.Request.URL))
 				allow := true
 
-				if !allowList.MatchString(e.Request.URL) {
-					logger.Warn(fmt.Sprintf("'%s' does not match the expression from the allowed list", e.Request.URL))
-					allow = false
+				deadline, ok := ctx.Deadline()
+				if !ok {
+					logger.Error("context has no deadline, cannot filter URL")
+					return
 				}
 
-				if denyList.String() != "" && denyList.MatchString(e.Request.URL) {
-					logger.Warn(fmt.Sprintf("'%s' matches the expression from the denied list", e.Request.URL))
+				err := gotenberg.FilterDeadline(allowList, denyList, e.Request.URL, deadline)
+				if err != nil {
+					logger.Warn(err.Error())
 					allow = false
 				}
 
 				cctx := chromedp.FromContext(ctx)
 				executorCtx := cdp.WithExecutor(ctx, cctx.Target)
 
 				if allow {
 					req := fetch.ContinueRequest(e.RequestID)
-					err := req.Do(executorCtx)
+					err = req.Do(executorCtx)
 					if err != nil {
 						logger.Error(fmt.Sprintf("continue request: %s", err))
 					}
-
 					return
 				}
 
 				req := fetch.FailRequest(e.RequestID, network.ErrorReasonAccessDenied)
-				err := req.Do(executorCtx)
+				err = req.Do(executorCtx)
 				if err != nil {
 					logger.Error(fmt.Sprintf("fail request: %s", err))
 				}
 			}()
 		}
diff --git a/pkg/modules/chromium/routes.go b/pkg/modules/chromium/routes.go
index e9d0934..8859b9a 100644
--- a/pkg/modules/chromium/routes.go
+++ b/pkg/modules/chromium/routes.go
@@ -619,11 +619,11 @@ func handleChromiumError(err error, url string, options Options) error {
 				"The maximum queue size has been reached",
 			),
 		)
 	}
 
-	if errors.Is(err, ErrUrlNotAuthorized) {
+	if errors.Is(err, gotenberg.ErrFiltered) {
 		return api.WrapError(
 			err,
 			api.NewSentinelHttpError(
 				http.StatusForbidden,
 				fmt.Sprintf("'%s' does not match the authorized URLs", url),
diff --git a/pkg/modules/chromium/routes_test.go b/pkg/modules/chromium/routes_test.go
index bb5d7fe..269944f 100644
--- a/pkg/modules/chromium/routes_test.go
+++ b/pkg/modules/chromium/routes_test.go
@@ -1241,14 +1241,14 @@ func TestConvertUrl(t *testing.T) {
 			expectHttpError:        true,
 			expectHttpStatus:       http.StatusTooManyRequests,
 			expectOutputPathsCount: 0,
 		},
 		{
-			scenario: "ErrUrlNotAuthorized",
+			scenario: "ErrFiltered",
 			ctx:      &api.ContextMock{Context: new(api.Context)},
 			api: &ApiMock{PdfMock: func(ctx context.Context, logger *zap.Logger, url, outputPath string, options PdfOptions) error {
-				return ErrUrlNotAuthorized
+				return gotenberg.ErrFiltered
 			}},
 			options:                DefaultPdfOptions(),
 			expectError:            true,
 			expectHttpError:        true,
 			expectHttpStatus:       http.StatusForbidden,
@@ -1501,14 +1501,14 @@ func TestScreenshotUrl(t *testing.T) {
 			expectHttpError:        true,
 			expectHttpStatus:       http.StatusTooManyRequests,
 			expectOutputPathsCount: 0,
 		},
 		{
-			scenario: "ErrUrlNotAuthorized",
+			scenario: "ErrFiltered",
 			ctx:      &api.ContextMock{Context: new(api.Context)},
 			api: &ApiMock{ScreenshotMock: func(ctx context.Context, logger *zap.Logger, url, outputPath string, options ScreenshotOptions) error {
-				return ErrUrlNotAuthorized
+				return gotenberg.ErrFiltered
 			}},
 			options:                DefaultScreenshotOptions(),
 			expectError:            true,
 			expectHttpError:        true,
 			expectHttpStatus:       http.StatusForbidden,
diff --git a/pkg/modules/webhook/middleware.go b/pkg/modules/webhook/middleware.go
index d0540bf..29ffc78 100644
--- a/pkg/modules/webhook/middleware.go
+++ b/pkg/modules/webhook/middleware.go
@@ -7,18 +7,19 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
 	"net/http"
 	"os"
-	"regexp"
 	"strconv"
 	"strings"
 	"time"
 
+	"github.com/dlclark/regexp2"
 	"github.com/hashicorp/go-retryablehttp"
 	"github.com/labstack/echo/v4"
 
+	"github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
 	"github.com/gotenberg/gotenberg/v8/pkg/modules/api"
 )
 
 func webhookMiddleware(w *Webhook) api.Middleware {
 	return api.Middleware{
@@ -42,42 +43,42 @@ func webhookMiddleware(w *Webhook) api.Middleware {
 							errors.New("empty webhook error URL"),
 							api.NewSentinelHttpError(http.StatusBadRequest, "Invalid 'Gotenberg-Webhook-Error-Url' header: empty value or header not provided"),
 						)
 					}
 
+					deadline, ok := ctx.Deadline()
+					if !ok {
+						return errors.New("context has no deadline")
+					}
+
 					// Let's check if the webhook URLs are acceptable according to our
 					// allowed/denied lists.
-					filter := func(URL, header string, allowList, denyList *regexp.Regexp) error {
-						if !allowList.MatchString(URL) {
-							return api.WrapError(
-								fmt.Errorf("'%s' does not match the expression from the allowed list", URL),
-								api.NewSentinelHttpError(
-									http.StatusForbidden,
-									fmt.Sprintf("Invalid '%s' header value: '%s' does not match the authorized URLs", header, URL),
-								),
-							)
+					filter := func(url, header string, allowList, denyList *regexp2.Regexp, deadline time.Time) error {
+						err := gotenberg.FilterDeadline(allowList, denyList, url, deadline)
+						if err == nil {
+							return nil
 						}
 
-						if denyList.String() != "" && denyList.MatchString(URL) {
+						if errors.Is(err, gotenberg.ErrFiltered) {
 							return api.WrapError(
-								fmt.Errorf("'%s' matches the expression from the denied list", URL),
+								err,
 								api.NewSentinelHttpError(
 									http.StatusForbidden,
-									fmt.Sprintf("Invalid '%s' header value: '%s' does not match the authorized URLs", header, URL),
+									fmt.Sprintf("Invalid '%s' header value: '%s' does not match the authorized URL", header, url),
 								),
 							)
 						}
 
-						return nil
+						return err
 					}
 
-					err := filter(webhookUrl, "Gotenberg-Webhook-Url", w.allowList, w.denyList)
+					err := filter(webhookUrl, "Gotenberg-Webhook-Url", w.allowList, w.denyList, deadline)
 					if err != nil {
 						return fmt.Errorf("filter webhook URL: %w", err)
 					}
 
-					err = filter(webhookErrorUrl, "Gotenberg-Webhook-Error-Url", w.errorAllowList, w.errorDenyList)
+					err = filter(webhookErrorUrl, "Gotenberg-Webhook-Error-Url", w.errorAllowList, w.errorDenyList, deadline)
 					if err != nil {
 						return fmt.Errorf("filter webhook error URL: %w", err)
 					}
 
 					// Let's check the HTTP methods for calling the webhook URLs.
diff --git a/pkg/modules/webhook/middleware_test.go b/pkg/modules/webhook/middleware_test.go
index 664a4d3..89e1373 100644
--- a/pkg/modules/webhook/middleware_test.go
+++ b/pkg/modules/webhook/middleware_test.go
@@ -9,15 +9,15 @@ import (
 	"io"
 	"math/rand"
 	"mime/multipart"
 	"net/http"
 	"net/http/httptest"
-	"regexp"
 	"strings"
 	"testing"
 	"time"
 
+	"github.com/dlclark/regexp2"
 	"github.com/labstack/echo/v4"
 	"go.uber.org/zap"
 
 	"github.com/gotenberg/gotenberg/v8/pkg/modules/api"
 )
@@ -45,14 +45,14 @@ func TestWebhookMiddlewareGuards(t *testing.T) {
 		return req
 	}
 
 	buildWebhookModule := func() *Webhook {
 		return &Webhook{
-			allowList:      regexp.MustCompile(""),
-			denyList:       regexp.MustCompile(""),
-			errorAllowList: regexp.MustCompile(""),
-			errorDenyList:  regexp.MustCompile(""),
+			allowList:      regexp2.MustCompile("", 0),
+			denyList:       regexp2.MustCompile("", 0),
+			errorAllowList: regexp2.MustCompile("", 0),
+			errorDenyList:  regexp2.MustCompile("", 0),
 			maxRetry:       0,
 			retryMinWait:   0,
 			retryMaxWait:   0,
 			disable:        false,
 		}
@@ -61,10 +61,11 @@ func TestWebhookMiddlewareGuards(t *testing.T) {
 	for _, tc := range []struct {
 		scenario         string
 		request          *http.Request
 		mod              *Webhook
 		next             echo.HandlerFunc
+		noDeadline       bool
 		expectError      bool
 		expectHttpError  bool
 		expectHttpStatus int
 	}{
 		{
@@ -74,10 +75,11 @@ func TestWebhookMiddlewareGuards(t *testing.T) {
 			next: func() echo.HandlerFunc {
 				return func(c echo.Context) error {
 					return nil
 				}
 			}(),
+			noDeadline:      false,
 			expectError:     false,
 			expectHttpError: false,
 		},
 		{
 			scenario: "no webhook error URL",
@@ -85,27 +87,41 @@ func TestWebhookMiddlewareGuards(t *testing.T) {
 				req := buildMultipartFormDataRequest()
 				req.Header.Set("Gotenberg-Webhook-Url", "foo")
 				return req
 			}(),
 			mod:              buildWebhookModule(),
+			noDeadline:       false,
 			expectError:      true,
 			expectHttpError:  true,
 			expectHttpStatus: http.StatusBadRequest,
 		},
+		{
+			scenario: "context has no deadline",
+			request: func() *http.Request {
+				req := buildMultipartFormDataRequest()
+				req.Header.Set("Gotenberg-Webhook-Url", "foo")
+				req.Header.Set("Gotenberg-Webhook-Error-Url", "bar")
+				return req
+			}(),
+			mod:         buildWebhookModule(),
+			noDeadline:  true,
+			expectError: true,
+		},
 		{
 			scenario: "webhook URL is not allowed",
 			request: func() *http.Request {
 				req := buildMultipartFormDataRequest()
 				req.Header.Set("Gotenberg-Webhook-Url", "foo")
 				req.Header.Set("Gotenberg-Webhook-Error-Url", "bar")
 				return req
 			}(),
 			mod: func() *Webhook {
 				mod := buildWebhookModule()
-				mod.allowList = regexp.MustCompile("bar")
+				mod.allowList = regexp2.MustCompile("bar", 0)
 				return mod
 			}(),
+			noDeadline:       false,
 			expectError:      true,
 			expectHttpError:  true,
 			expectHttpStatus: http.StatusForbidden,
 		},
 		{
@@ -116,13 +132,14 @@ func TestWebhookMiddlewareGuards(t *testing.T) {
 				req.Header.Set("Gotenberg-Webhook-Error-Url", "bar")
 				return req
 			}(),
 			mod: func() *Webhook {
 				mod := buildWebhookModule()
-				mod.denyList = regexp.MustCompile("foo")
+				mod.denyList = regexp2.MustCompile("foo", 0)
 				return mod
 			}(),
+			noDeadline:       false,
 			expectError:      true,
 			expectHttpError:  true,
 			expectHttpStatus: http.StatusForbidden,
 		},
 		{
@@ -133,13 +150,14 @@ func TestWebhookMiddlewareGuards(t *testing.T) {
 				req.Header.Set("Gotenberg-Webhook-Error-Url", "bar")
 				return req
 			}(),
 			mod: func() *Webhook {
 				mod := buildWebhookModule()
-				mod.errorAllowList = regexp.MustCompile("foo")
+				mod.errorAllowList = regexp2.MustCompile("foo", 0)
 				return mod
 			}(),
+			noDeadline:       false,
 			expectError:      true,
 			expectHttpError:  true,
 			expectHttpStatus: http.StatusForbidden,
 		},
 		{
@@ -150,13 +168,14 @@ func TestWebhookMiddlewareGuards(t *testing.T) {
 				req.Header.Set("Gotenberg-Webhook-Error-Url", "bar")
 				return req
 			}(),
 			mod: func() *Webhook {
 				mod := buildWebhookModule()
-				mod.errorDenyList = regexp.MustCompile("bar")
+				mod.errorDenyList = regexp2.MustCompile("bar", 0)
 				return mod
 			}(),
+			noDeadline:       false,
 			expectError:      true,
 			expectHttpError:  true,
 			expectHttpStatus: http.StatusForbidden,
 		},
 		{
@@ -167,10 +186,11 @@ func TestWebhookMiddlewareGuards(t *testing.T) {
 				req.Header.Set("Gotenberg-Webhook-Method", http.MethodGet)
 				req.Header.Set("Gotenberg-Webhook-Error-Url", "bar")
 				return req
 			}(),
 			mod:              buildWebhookModule(),
+			noDeadline:       false,
 			expectError:      true,
 			expectHttpError:  true,
 			expectHttpStatus: http.StatusBadRequest,
 		},
 		{
@@ -181,10 +201,11 @@ func TestWebhookMiddlewareGuards(t *testing.T) {
 				req.Header.Set("Gotenberg-Webhook-Error-Url", "bar")
 				req.Header.Set("Gotenberg-Webhook-Error-Method", http.MethodGet)
 				return req
 			}(),
 			mod:              buildWebhookModule(),
+			noDeadline:       false,
 			expectError:      true,
 			expectHttpError:  true,
 			expectHttpStatus: http.StatusBadRequest,
 		},
 		{
@@ -196,10 +217,11 @@ func TestWebhookMiddlewareGuards(t *testing.T) {
 				req.Header.Set("Gotenberg-Webhook-Error-Url", "bar")
 				req.Header.Set("Gotenberg-Webhook-Error-Method", http.MethodGet)
 				return req
 			}(),
 			mod:              buildWebhookModule(),
+			noDeadline:       false,
 			expectError:      true,
 			expectHttpError:  true,
 			expectHttpStatus: http.StatusBadRequest,
 		},
 		{
@@ -211,10 +233,11 @@ func TestWebhookMiddlewareGuards(t *testing.T) {
 				req.Header.Set("Gotenberg-Webhook-Error-Url", "bar")
 				req.Header.Set("Gotenberg-Webhook-Error-Method", http.MethodGet)
 				return req
 			}(),
 			mod:              buildWebhookModule(),
+			noDeadline:       false,
 			expectError:      true,
 			expectHttpError:  true,
 			expectHttpStatus: http.StatusBadRequest,
 		},
 		{
@@ -226,10 +249,11 @@ func TestWebhookMiddlewareGuards(t *testing.T) {
 				req.Header.Set("Gotenberg-Webhook-Error-Url", "bar")
 				req.Header.Set("Gotenberg-Webhook-Error-Method", http.MethodGet)
 				return req
 			}(),
 			mod:              buildWebhookModule(),
+			noDeadline:       false,
 			expectError:      true,
 			expectHttpError:  true,
 			expectHttpStatus: http.StatusBadRequest,
 		},
 		{
@@ -240,10 +264,11 @@ func TestWebhookMiddlewareGuards(t *testing.T) {
 				req.Header.Set("Gotenberg-Webhook-Error-Url", "bar")
 				req.Header.Set("Gotenberg-Webhook-Extra-Http-Headers", "foo")
 				return req
 			}(),
 			mod:              buildWebhookModule(),
+			noDeadline:       false,
 			expectError:      true,
 			expectHttpError:  true,
 			expectHttpStatus: http.StatusBadRequest,
 		},
 	} {
@@ -252,19 +277,24 @@ func TestWebhookMiddlewareGuards(t *testing.T) {
 			srv.HideBanner = true
 			srv.HidePort = true
 
 			c := srv.NewContext(tc.request, httptest.NewRecorder())
 
-			ctx := &api.ContextMock{Context: &api.Context{}}
-			ctx.SetEchoContext(c)
-
-			c.Set("context", ctx.Context)
-			c.Set("cancel", func() context.CancelFunc {
-				return func() {
-					return
-				}
-			}())
+			if tc.noDeadline {
+				ctx := &api.ContextMock{Context: &api.Context{Context: context.Background()}}
+				ctx.SetEchoContext(c)
+				c.Set("context", ctx.Context)
+				c.Set("cancel", func() context.CancelFunc {
+					return nil
+				}())
+			} else {
+				timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Duration(10)*time.Second)
+				ctx := &api.ContextMock{Context: &api.Context{Context: timeoutCtx}}
+				ctx.SetEchoContext(c)
+				c.Set("context", ctx.Context)
+				c.Set("cancel", cancel)
+			}
 
 			err := webhookMiddleware(tc.mod).Handler(tc.next)(c)
 
 			if tc.expectError && err == nil {
 				t.Fatal("expected error but got none", err)
@@ -318,14 +348,14 @@ func TestWebhookMiddlewareAsynchronousProcess(t *testing.T) {
 		return req
 	}
 
 	buildWebhookModule := func() *Webhook {
 		return &Webhook{
-			allowList:      regexp.MustCompile(""),
-			denyList:       regexp.MustCompile(""),
-			errorAllowList: regexp.MustCompile(""),
-			errorDenyList:  regexp.MustCompile(""),
+			allowList:      regexp2.MustCompile("", 0),
+			denyList:       regexp2.MustCompile("", 0),
+			errorAllowList: regexp2.MustCompile("", 0),
+			errorDenyList:  regexp2.MustCompile("", 0),
 			maxRetry:       0,
 			retryMinWait:   0,
 			retryMaxWait:   0,
 			clientTimeout:  time.Duration(30) * time.Second,
 			disable:        false,
@@ -424,26 +454,21 @@ func TestWebhookMiddlewareAsynchronousProcess(t *testing.T) {
 			c.Set("logger", zap.NewNop())
 			c.Set("traceHeader", "Gotenberg-Trace")
 			c.Set("trace", "foo")
 			c.Set("startTime", time.Now())
 
-			ctx := &api.ContextMock{Context: &api.Context{}}
+			timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Duration(10)*time.Second)
+			ctx := &api.ContextMock{Context: &api.Context{Context: timeoutCtx}}
 			ctx.SetLogger(zap.NewNop())
 			ctx.SetEchoContext(c)
 
 			c.Set("context", ctx.Context)
-			c.Set("cancel", func() context.CancelFunc {
-				return func() {
-					return
-				}
-			}())
+			c.Set("cancel", cancel)
 
 			webhook := echo.New()
 			webhook.HideBanner = true
 			webhook.HidePort = true
-
-			rand.Seed(time.Now().UnixNano())
 			webhookPort := rand.Intn(65535-1025+1) + 1025
 
 			c.Request().Header.Set("Gotenberg-Webhook-Url", fmt.Sprintf("http://localhost:%d/", webhookPort))
 			c.Request().Header.Set("Gotenberg-Webhook-Error-Url", fmt.Sprintf("http://localhost:%d/", webhookPort))
 
diff --git a/pkg/modules/webhook/webhook.go b/pkg/modules/webhook/webhook.go
index 7476bd5..8661220 100644
--- a/pkg/modules/webhook/webhook.go
+++ b/pkg/modules/webhook/webhook.go
@@ -1,11 +1,11 @@
 package webhook
 
 import (
-	"regexp"
 	"time"
 
+	"github.com/dlclark/regexp2"
 	flag "github.com/spf13/pflag"
 
 	"github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
 	"github.com/gotenberg/gotenberg/v8/pkg/modules/api"
 )
@@ -15,14 +15,14 @@ func init() {
 }
 
 // Webhook is a module which provides a middleware for uploading output files
 // to any destinations in an asynchronous fashion.
 type Webhook struct {
-	allowList      *regexp.Regexp
-	denyList       *regexp.Regexp
-	errorAllowList *regexp.Regexp
-	errorDenyList  *regexp.Regexp
+	allowList      *regexp2.Regexp
+	denyList       *regexp2.Regexp
+	errorAllowList *regexp2.Regexp
+	errorDenyList  *regexp2.Regexp
 	maxRetry       int
 	retryMinWait   time.Duration
 	retryMaxWait   time.Duration
 	clientTimeout  time.Duration
 	disable        bool

Vulnerable code: CHROMIUM_DENY_LIST="^file:///[^tmp].*"
	--chromium-allow-list=$(CHROMIUM_ALLOW_LIST) \
	--chromium-deny-list=$(CHROMIUM_DENY_LIST) \
	--webhook-allow-list=$(WEBHOOK_ALLOW_LIST) \
	--webhook-deny-list=$(WEBHOOK_DENY_LIST) \
	"regexp"
func (f *ParsedFlags) MustRegexp(name string) *regexp.Regexp {
	return regexp.MustCompile(val)
func (f *ParsedFlags) MustDeprecatedRegexp(deprecated string, newName string) *regexp.Regexp {
			defer cancel()

	"regexp"
	allowList         *regexp.Regexp
	denyList          *regexp.Regexp
	// We validate the "main" URL against our allow / deny lists.
	if !b.arguments.allowList.MatchString(url) {
		return fmt.Errorf("'%s' does not match the expression from the allowed list: %w", url, ErrUrlNotAuthorized)
	}

	if b.arguments.denyList.String() != "" && b.arguments.denyList.MatchString(url) {
		return fmt.Errorf("'%s' matches the expression from the denied list: %w", url, ErrUrlNotAuthorized)
	}

	err := chromedp.Run(taskCtx, tasks...)
	"regexp"
			scenario: "ErrUrlNotAuthorized: main URL does not match the allowed list",
				b.arguments = browserArguments{
					allowList: regexp.MustCompile("^file:///[^tmp].*"),
				}
			fs:            gotenberg.NewFileSystem(),
			noDeadline:    false,
			start:         false,
			expectError:   true,
			expectedError: ErrUrlNotAuthorized,
			scenario: "ErrUrlNotAuthorized: main URL does match the denied list",
					allowList: regexp.MustCompile(""),
					denyList:  regexp.MustCompile("^file:///tmp.*"),
			expectedError: ErrUrlNotAuthorized,
			scenario: "ErrUrlNotAuthorized: main URL does match the denied list",
					allowList: regexp.MustCompile(""),
					denyList:  regexp.MustCompile(""),
			fs:          gotenberg.NewFileSystem(),
			noDeadline:  true,
			start:       false,
			expectError: true,
					allowList:        regexp.MustCompile("^file:///tmp.*"),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile("^file:///[^tmp].*"),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:         regexp.MustCompile(""),
					denyList:          regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
			scenario: "ErrUrlNotAuthorized: main URL does not match the allowed list",
					allowList: regexp.MustCompile("^file:///[^tmp].*"),
			fs:            gotenberg.NewFileSystem(),
			noDeadline:    false,
			start:         false,
			expectError:   true,
			expectedError: ErrUrlNotAuthorized,
			scenario: "ErrUrlNotAuthorized: main URL does match the denied list",
					allowList: regexp.MustCompile(""),
					denyList:  regexp.MustCompile("^file:///tmp.*"),
			expectedError: ErrUrlNotAuthorized,
			scenario: "ErrUrlNotAuthorized: main URL does match the denied list",
					allowList: regexp.MustCompile(""),
					denyList:  regexp.MustCompile(""),
			fs:          gotenberg.NewFileSystem(),
			noDeadline:  true,
			start:       false,
			expectError: true,
					allowList:        regexp.MustCompile("^file:///tmp.*"),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile("^file:///[^tmp].*"),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:         regexp.MustCompile(""),
					denyList:          regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
					allowList:        regexp.MustCompile(""),
					denyList:         regexp.MustCompile(""),
	// ErrUrlNotAuthorized happens if a URL is not acceptable according to the
	// allowed/denied lists.
	ErrUrlNotAuthorized = errors.New("URL not authorized")

			fs.String("chromium-deny-list", "^file:///[^tmp].*", "Set the denied URLs for Chromium using a regular expression")
	"regexp"
func listenForEventRequestPaused(ctx context.Context, logger *zap.Logger, allowList *regexp.Regexp, denyList *regexp.Regexp) {
				if !allowList.MatchString(e.Request.URL) {
					logger.Warn(fmt.Sprintf("'%s' does not match the expression from the allowed list", e.Request.URL))
					allow = false
				if denyList.String() != "" && denyList.MatchString(e.Request.URL) {
					logger.Warn(fmt.Sprintf("'%s' matches the expression from the denied list", e.Request.URL))
					err := req.Do(executorCtx)

				err := req.Do(executorCtx)
	if errors.Is(err, ErrUrlNotAuthorized) {
			scenario: "ErrUrlNotAuthorized",
				return ErrUrlNotAuthorized
			scenario: "ErrUrlNotAuthorized",
				return ErrUrlNotAuthorized
	"regexp"
					filter := func(URL, header string, allowList, denyList *regexp.Regexp) error {
						if !allowList.MatchString(URL) {
							return api.WrapError(
								fmt.Errorf("'%s' does not match the expression from the allowed list", URL),
								api.NewSentinelHttpError(
									http.StatusForbidden,
									fmt.Sprintf("Invalid '%s' header value: '%s' does not match the authorized URLs", header, URL),
								),
							)
						if denyList.String() != "" && denyList.MatchString(URL) {
								fmt.Errorf("'%s' matches the expression from the denied list", URL),
									fmt.Sprintf("Invalid '%s' header value: '%s' does not match the authorized URLs", header, URL),
						return nil
					err := filter(webhookUrl, "Gotenberg-Webhook-Url", w.allowList, w.denyList)
					err = filter(webhookErrorUrl, "Gotenberg-Webhook-Error-Url", w.errorAllowList, w.errorDenyList)
	"regexp"
			allowList:      regexp.MustCompile(""),
			denyList:       regexp.MustCompile(""),
			errorAllowList: regexp.MustCompile(""),
			errorDenyList:  regexp.MustCompile(""),
				mod.allowList = regexp.MustCompile("bar")
				mod.denyList = regexp.MustCompile("foo")
				mod.errorAllowList = regexp.MustCompile("foo")
				mod.errorDenyList = regexp.MustCompile("bar")
			ctx := &api.ContextMock{Context: &api.Context{}}
			ctx.SetEchoContext(c)

			c.Set("context", ctx.Context)
			c.Set("cancel", func() context.CancelFunc {
				return func() {
					return
				}
			}())
			allowList:      regexp.MustCompile(""),
			denyList:       regexp.MustCompile(""),
			errorAllowList: regexp.MustCompile(""),
			errorDenyList:  regexp.MustCompile(""),
			ctx := &api.ContextMock{Context: &api.Context{}}
			c.Set("cancel", func() context.CancelFunc {
				return func() {
					return
				}
			}())

			rand.Seed(time.Now().UnixNano())
	"regexp"
	allowList      *regexp.Regexp
	denyList       *regexp.Regexp
	errorAllowList *regexp.Regexp
	errorDenyList  *regexp.Regexp
Fixed code: CHROMIUM_DENY_LIST=^file:(?!//\/tmp/).*
	--chromium-allow-list="$(CHROMIUM_ALLOW_LIST)" \
	--chromium-deny-list="$(CHROMIUM_DENY_LIST)" \
	--webhook-allow-list="$(WEBHOOK_ALLOW_LIST)" \
	--webhook-deny-list="$(WEBHOOK_DENY_LIST)" \
	"github.com/dlclark/regexp2"
func (f *ParsedFlags) MustRegexp(name string) *regexp2.Regexp {
	return regexp2.MustCompile(val, 0)
func (f *ParsedFlags) MustDeprecatedRegexp(deprecated string, newName string) *regexp2.Regexp {
package gotenberg

import (
	"context"
	"errors"
	"fmt"
	"time"

	"github.com/dlclark/regexp2"
)

// ErrFiltered happens if a value is filtered by the [FilterDeadline] function.
var ErrFiltered = errors.New("value filtered")

// FilterDeadline checks if given value is allowed and not denied according to
// regex patterns. It returns a [context.DeadlineExceeded] if it takes too long
// to process.
func FilterDeadline(allowed, denied *regexp2.Regexp, s string, deadline time.Time) error {
	// FIXME: not ideal to compile everytime, but is there another way to create a clone?
	if allowed.String() != "" {
		allow := regexp2.MustCompile(allowed.String(), 0)
		allow.MatchTimeout = time.Until(deadline)

		ok, err := allow.MatchString(s)
		if err != nil {
			if time.Now().After(deadline) {
				return context.DeadlineExceeded
			}
			return fmt.Errorf("'%s' cannot handle '%s': %w", allow.String(), s, err)
		}
		if !ok {
			return fmt.Errorf("'%s' does not match the expression from the allowed list: %w", s, ErrFiltered)
		}
	}

	if denied.String() != "" {
		deny := regexp2.MustCompile(denied.String(), 0)
		deny.MatchTimeout = time.Until(deadline)

		ok, err := deny.MatchString(s)
		if err != nil {
			if time.Now().After(deadline) {
				return context.DeadlineExceeded
			}
			return fmt.Errorf("'%s' cannot handle '%s': %w", deny.String(), s, err)
		}
		if ok {
			return fmt.Errorf("'%s' matches the expression from the denied list: %w", s, ErrFiltered)
		}
	}

	return nil
}
package gotenberg

import (
	"context"
	"errors"
	"testing"
	"time"

	"github.com/dlclark/regexp2"
)

func TestFilterDeadline(t *testing.T) {
	for _, tc := range []struct {
		scenario      string
		allowed       *regexp2.Regexp
		denied        *regexp2.Regexp
		s             string
		deadline      time.Time
		expectError   bool
		expectedError error
	}{
		{
			scenario:      "DeadlineExceeded (allowed)",
			allowed:       regexp2.MustCompile("foo", 0),
			denied:        regexp2.MustCompile("", 0),
			s:             "foo",
			deadline:      time.Now().Add(time.Duration(-1) * time.Hour),
			expectError:   true,
			expectedError: context.DeadlineExceeded,
		},
		{
			scenario:      "ErrFiltered (allowed)",
			allowed:       regexp2.MustCompile("foo", 0),
			denied:        regexp2.MustCompile("", 0),
			s:             "bar",
			deadline:      time.Now().Add(time.Duration(5) * time.Second),
			expectError:   true,
			expectedError: ErrFiltered,
		},
		{
			scenario:      "DeadlineExceeded (denied)",
			allowed:       regexp2.MustCompile("", 0),
			denied:        regexp2.MustCompile("foo", 0),
			s:             "foo",
			deadline:      time.Now().Add(time.Duration(-1) * time.Hour),
			expectError:   true,
			expectedError: context.DeadlineExceeded,
		},
		{
			scenario:      "ErrFiltered (denied)",
			allowed:       regexp2.MustCompile("", 0),
			denied:        regexp2.MustCompile("foo", 0),
			s:             "foo",
			deadline:      time.Now().Add(time.Duration(5) * time.Second),
			expectError:   true,
			expectedError: ErrFiltered,
		},
		{
			scenario:    "success",
			allowed:     regexp2.MustCompile("", 0),
			denied:      regexp2.MustCompile("", 0),
			s:           "foo",
			deadline:    time.Now().Add(time.Duration(5) * time.Second),
			expectError: false,
		},
	} {
		t.Run(tc.scenario, func(t *testing.T) {
			err := FilterDeadline(tc.allowed, tc.denied, tc.s, tc.deadline)

			if tc.expectError && err == nil {
				t.Fatal("expected an error but got none")
			}

			if !tc.expectError && err != nil {
				t.Fatalf("expected no error but got: %v", err)
			}

			if tc.expectedError != nil && !errors.Is(err, tc.expectedError) {
				t.Fatalf("expected error %v but got: %v", tc.expectedError, err)
			}
		})
	}
}
			} else {
				defer cancel()
	"github.com/dlclark/regexp2"
	allowList         *regexp2.Regexp
	denyList          *regexp2.Regexp
	// We validate the "main" URL against our allow / deny lists.
	err := gotenberg.FilterDeadline(b.arguments.allowList, b.arguments.denyList, url, deadline)
	if err != nil {
		return fmt.Errorf("filter URL: %w", err)
	}

	err = chromedp.Run(taskCtx, tasks...)
	"github.com/dlclark/regexp2"
			scenario: "context has no deadline",
			fs:          gotenberg.NewFileSystem(),
			noDeadline:  true,
			start:       false,
			expectError: true,
			scenario: "ErrFiltered: main URL does not match the allowed list",
					allowList: regexp2.MustCompile(`^file:(?!//\/tmp/).*`, 0),
					denyList:  regexp2.MustCompile("", 0),
			expectedError: gotenberg.ErrFiltered,
			scenario: "ErrFiltered: main URL does match the denied list",
					allowList: regexp2.MustCompile("", 0),
					denyList:  regexp2.MustCompile("^file:///tmp.*", 0),
			fs:            gotenberg.NewFileSystem(),
			noDeadline:    false,
			start:         false,
			expectError:   true,
			expectedError: gotenberg.ErrFiltered,
					allowList:        regexp2.MustCompile("^file:///tmp.*", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile(`^file:(?!//\/tmp/).*`, 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:         regexp2.MustCompile("", 0),
					denyList:          regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
			scenario: "context has not deadline",
					allowList: regexp2.MustCompile("", 0),
					denyList:  regexp2.MustCompile("", 0),
			fs:          gotenberg.NewFileSystem(),
			noDeadline:  true,
			start:       false,
			expectError: true,
			scenario: "ErrFiltered: main URL does not match the allowed list",
					allowList: regexp2.MustCompile(`^file:(?!//\/tmp/).*`, 0),
					denyList:  regexp2.MustCompile("", 0),
			expectedError: gotenberg.ErrFiltered,
			scenario: "ErrFiltered: main URL does match the denied list",
					allowList: regexp2.MustCompile("", 0),
					denyList:  regexp2.MustCompile("^file:///tmp.*", 0),
			fs:            gotenberg.NewFileSystem(),
			noDeadline:    false,
			start:         false,
			expectError:   true,
			expectedError: gotenberg.ErrFiltered,
					allowList:        regexp2.MustCompile("^file:///tmp.*", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile(`^file:(?!//\/tmp/).*`, 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:         regexp2.MustCompile("", 0),
					denyList:          regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
					allowList:        regexp2.MustCompile("", 0),
					denyList:         regexp2.MustCompile("", 0),
			fs.String("chromium-deny-list", `^file:(?!//\/tmp/).*`, "Set the denied URLs for Chromium using a regular expression")
	"github.com/dlclark/regexp2"

	"github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
func listenForEventRequestPaused(ctx context.Context, logger *zap.Logger, allowList *regexp2.Regexp, denyList *regexp2.Regexp) {
				deadline, ok := ctx.Deadline()
				if !ok {
					logger.Error("context has no deadline, cannot filter URL")
					return
				err := gotenberg.FilterDeadline(allowList, denyList, e.Request.URL, deadline)
				if err != nil {
					logger.Warn(err.Error())
					err = req.Do(executorCtx)
				err = req.Do(executorCtx)
	if errors.Is(err, gotenberg.ErrFiltered) {
			scenario: "ErrFiltered",
				return gotenberg.ErrFiltered
			scenario: "ErrFiltered",
				return gotenberg.ErrFiltered
	"github.com/dlclark/regexp2"
	"github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
					deadline, ok := ctx.Deadline()
					if !ok {
						return errors.New("context has no deadline")
					}

					filter := func(url, header string, allowList, denyList *regexp2.Regexp, deadline time.Time) error {
						err := gotenberg.FilterDeadline(allowList, denyList, url, deadline)
						if err == nil {
							return nil
						if errors.Is(err, gotenberg.ErrFiltered) {
								err,
									fmt.Sprintf("Invalid '%s' header value: '%s' does not match the authorized URL", header, url),
						return err
					err := filter(webhookUrl, "Gotenberg-Webhook-Url", w.allowList, w.denyList, deadline)
					err = filter(webhookErrorUrl, "Gotenberg-Webhook-Error-Url", w.errorAllowList, w.errorDenyList, deadline)
	"github.com/dlclark/regexp2"
			allowList:      regexp2.MustCompile("", 0),
			denyList:       regexp2.MustCompile("", 0),
			errorAllowList: regexp2.MustCompile("", 0),
			errorDenyList:  regexp2.MustCompile("", 0),
		noDeadline       bool
			noDeadline:      false,
			noDeadline:       false,
		{
			scenario: "context has no deadline",
			request: func() *http.Request {
				req := buildMultipartFormDataRequest()
				req.Header.Set("Gotenberg-Webhook-Url", "foo")
				req.Header.Set("Gotenberg-Webhook-Error-Url", "bar")
				return req
			}(),
			mod:         buildWebhookModule(),
			noDeadline:  true,
			expectError: true,
		},
				mod.allowList = regexp2.MustCompile("bar", 0)
			noDeadline:       false,
				mod.denyList = regexp2.MustCompile("foo", 0)
			noDeadline:       false,
				mod.errorAllowList = regexp2.MustCompile("foo", 0)
			noDeadline:       false,
				mod.errorDenyList = regexp2.MustCompile("bar", 0)
			noDeadline:       false,
			noDeadline:       false,
			noDeadline:       false,
			noDeadline:       false,
			noDeadline:       false,
			noDeadline:       false,
			noDeadline:       false,
			if tc.noDeadline {
				ctx := &api.ContextMock{Context: &api.Context{Context: context.Background()}}
				ctx.SetEchoContext(c)
				c.Set("context", ctx.Context)
				c.Set("cancel", func() context.CancelFunc {
					return nil
				}())
			} else {
				timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Duration(10)*time.Second)
				ctx := &api.ContextMock{Context: &api.Context{Context: timeoutCtx}}
				ctx.SetEchoContext(c)
				c.Set("context", ctx.Context)
				c.Set("cancel", cancel)
			}
			allowList:      regexp2.MustCompile("", 0),
			denyList:       regexp2.MustCompile("", 0),
			errorAllowList: regexp2.MustCompile("", 0),
			errorDenyList:  regexp2.MustCompile("", 0),
			timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Duration(10)*time.Second)
			ctx := &api.ContextMock{Context: &api.Context{Context: timeoutCtx}}
			c.Set("cancel", cancel)
	"github.com/dlclark/regexp2"
	allowList      *regexp2.Regexp
	denyList       *regexp2.Regexp
	errorAllowList *regexp2.Regexp
	errorDenyList  *regexp2.Regexp