mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-08 08:32:16 +01:00
feat: Chromium underlying requests are not tested againt the allow/deny lists
This commit is contained in:
2
Makefile
2
Makefile
@@ -45,7 +45,7 @@ CHROMIUM_USER_AGENT=
|
|||||||
CHROMIUM_INCOGNITO=false
|
CHROMIUM_INCOGNITO=false
|
||||||
CHROMIUM_IGNORE_CERTIFICATE_ERRORS=false
|
CHROMIUM_IGNORE_CERTIFICATE_ERRORS=false
|
||||||
CHROMIUM_ALLOW_LIST=
|
CHROMIUM_ALLOW_LIST=
|
||||||
CHROMIUM_DENY_LIST=
|
CHROMIUM_DENY_LIST="^file:///[^tmp].*"
|
||||||
CHROMIUM_DISABLE_ROUTES=false
|
CHROMIUM_DISABLE_ROUTES=false
|
||||||
LIBREOFFICE_DISABLES_ROUTES=false
|
LIBREOFFICE_DISABLES_ROUTES=false
|
||||||
LOG_LEVEL=info
|
LOG_LEVEL=info
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/chromedp/cdproto/fetch"
|
||||||
"github.com/chromedp/cdproto/network"
|
"github.com/chromedp/cdproto/network"
|
||||||
"github.com/chromedp/cdproto/page"
|
"github.com/chromedp/cdproto/page"
|
||||||
"github.com/chromedp/chromedp"
|
"github.com/chromedp/chromedp"
|
||||||
@@ -183,7 +184,7 @@ func (mod Chromium) Descriptor() gotenberg.ModuleDescriptor {
|
|||||||
fs.Bool("chromium-incognito", false, "Start Chromium with incognito mode")
|
fs.Bool("chromium-incognito", false, "Start Chromium with incognito mode")
|
||||||
fs.Bool("chromium-ignore-certificate-errors", false, "Ignore the certificate errors")
|
fs.Bool("chromium-ignore-certificate-errors", false, "Ignore the certificate errors")
|
||||||
fs.String("chromium-allow-list", "", "Set the allowed URLs for Chromium using a regular expression")
|
fs.String("chromium-allow-list", "", "Set the allowed URLs for Chromium using a regular expression")
|
||||||
fs.String("chromium-deny-list", "", "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-disable-routes", false, "Disable the routes")
|
fs.Bool("chromium-disable-routes", false, "Disable the routes")
|
||||||
|
|
||||||
return fs
|
return fs
|
||||||
@@ -286,6 +287,7 @@ func (mod Chromium) PDF(ctx context.Context, logger *zap.Logger, URL, outputPath
|
|||||||
taskCtx, cancel := chromedp.NewContext(allocatorCtx)
|
taskCtx, cancel := chromedp.NewContext(allocatorCtx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
// We validate the "main" URL against our allow / deny lists.
|
||||||
if !mod.allowList.MatchString(URL) {
|
if !mod.allowList.MatchString(URL) {
|
||||||
return fmt.Errorf("'%s' does not match the expression from the allowed list: %w", URL, ErrURLNotAuthorized)
|
return fmt.Errorf("'%s' does not match the expression from the allowed list: %w", URL, ErrURLNotAuthorized)
|
||||||
}
|
}
|
||||||
@@ -295,8 +297,13 @@ func (mod Chromium) PDF(ctx context.Context, logger *zap.Logger, URL, outputPath
|
|||||||
}
|
}
|
||||||
|
|
||||||
printToPDF := func(URL string, options Options, result *[]byte) chromedp.Tasks {
|
printToPDF := func(URL string, options Options, result *[]byte) chromedp.Tasks {
|
||||||
|
// We validate the underlying requests against our allow / deny lists.
|
||||||
|
// If a request does not pass the validation, we make it fail.
|
||||||
|
listenForEventRequestPaused(taskCtx, logger, mod.allowList, mod.denyList)
|
||||||
|
|
||||||
return chromedp.Tasks{
|
return chromedp.Tasks{
|
||||||
network.Enable(),
|
network.Enable(),
|
||||||
|
fetch.Enable(),
|
||||||
chromedp.ActionFunc(func(ctx context.Context) error {
|
chromedp.ActionFunc(func(ctx context.Context) error {
|
||||||
if len(options.ExtraHTTPHeaders) == 0 {
|
if len(options.ExtraHTTPHeaders) == 0 {
|
||||||
logger.Debug("no extra HTTP headers")
|
logger.Debug("no extra HTTP headers")
|
||||||
|
|||||||
@@ -222,26 +222,25 @@ func TestChromium_PDF(t *testing.T) {
|
|||||||
expectErr bool
|
expectErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
URL: "https://google.com",
|
URL: "file:///tests/test/testdata/chromium/html/sample4/index.html",
|
||||||
allowList: regexp.MustCompile("https://google.fr"),
|
allowList: regexp.MustCompile("file:///tmp/*"),
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
URL: "https://google.com",
|
URL: "file:///tests/test/testdata/chromium/html/sample4/index.html",
|
||||||
denyList: regexp.MustCompile("https://google.com"),
|
denyList: regexp.MustCompile("file:///tests/*"),
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
URL: "",
|
URL: "file:///tests/test/testdata/chromium/html/sample4/index.html",
|
||||||
options: Options{
|
options: Options{
|
||||||
ExtraHTTPHeaders: map[string]string{
|
ExtraHTTPHeaders: map[string]string{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectErr: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
URL: "https://google.com",
|
URL: "file:///tests/test/testdata/chromium/html/sample4/index.html",
|
||||||
options: Options{
|
options: Options{
|
||||||
WaitDelay: time.Duration(1) * time.Nanosecond,
|
WaitDelay: time.Duration(1) * time.Nanosecond,
|
||||||
},
|
},
|
||||||
@@ -262,21 +261,21 @@ func TestChromium_PDF(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
URL: "https://google.com",
|
URL: "file:///tests/test/testdata/chromium/html/sample4/index.html",
|
||||||
options: Options{
|
options: Options{
|
||||||
MarginBottom: 100,
|
MarginBottom: 100,
|
||||||
},
|
},
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
URL: "https://google.com",
|
URL: "file:///tests/test/testdata/chromium/html/sample4/index.html",
|
||||||
options: Options{
|
options: Options{
|
||||||
PageRanges: "foo",
|
PageRanges: "foo",
|
||||||
},
|
},
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
URL: "https://google.com",
|
URL: "file:///tests/test/testdata/chromium/html/sample4/index.html",
|
||||||
userAgent: "foo",
|
userAgent: "foo",
|
||||||
incognito: true,
|
incognito: true,
|
||||||
ignoreCertificateErrors: true,
|
ignoreCertificateErrors: true,
|
||||||
@@ -285,7 +284,15 @@ func TestChromium_PDF(t *testing.T) {
|
|||||||
URL: "file:///tests/test/testdata/chromium/html/sample1/index.html",
|
URL: "file:///tests/test/testdata/chromium/html/sample1/index.html",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
URL: "https://google.com",
|
URL: "file:///tests/test/testdata/chromium/html/sample3/index.html",
|
||||||
|
allowList: regexp.MustCompile("file:///tests/*"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "file:///tests/test/testdata/chromium/html/sample3/index.html",
|
||||||
|
denyList: regexp.MustCompile("file:///etc/*"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "file:///tests/test/testdata/chromium/html/sample4/index.html",
|
||||||
options: Options{
|
options: Options{
|
||||||
HeaderTemplate: func() string {
|
HeaderTemplate: func() string {
|
||||||
b, err := ioutil.ReadFile("/tests/test/testdata/chromium/url/sample2/header.html")
|
b, err := ioutil.ReadFile("/tests/test/testdata/chromium/url/sample2/header.html")
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ package chromium
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/chromedp/cdproto/cdp"
|
||||||
|
"github.com/chromedp/cdproto/fetch"
|
||||||
"github.com/chromedp/cdproto/network"
|
"github.com/chromedp/cdproto/network"
|
||||||
"github.com/chromedp/cdproto/page"
|
"github.com/chromedp/cdproto/page"
|
||||||
"github.com/chromedp/chromedp"
|
"github.com/chromedp/chromedp"
|
||||||
@@ -11,6 +14,51 @@ import (
|
|||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
if denyList.String() != "" && denyList.MatchString(e.Request.URL) {
|
||||||
|
logger.Warn(fmt.Sprintf("'%s' matches the expression from the denied list", e.Request.URL))
|
||||||
|
allow = false
|
||||||
|
}
|
||||||
|
|
||||||
|
cctx := chromedp.FromContext(ctx)
|
||||||
|
executorCtx := cdp.WithExecutor(ctx, cctx.Target)
|
||||||
|
|
||||||
|
if allow {
|
||||||
|
req := fetch.ContinueRequest(e.RequestID)
|
||||||
|
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)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(fmt.Sprintf("fail request: %s", err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// waitForEventDomContentEventFired waits until the event DomContentEventFired
|
// waitForEventDomContentEventFired waits until the event DomContentEventFired
|
||||||
// is fired or the context timeout.
|
// is fired or the context timeout.
|
||||||
func waitForEventDomContentEventFired(ctx context.Context, logger *zap.Logger) func() error {
|
func waitForEventDomContentEventFired(ctx context.Context, logger *zap.Logger) func() error {
|
||||||
|
|||||||
10
test/testdata/chromium/html/sample3/index.html
vendored
Normal file
10
test/testdata/chromium/html/sample3/index.html
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Gutenberg</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<iframe src='file:///etc/passwd'></iframe>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
10
test/testdata/chromium/html/sample4/index.html
vendored
Normal file
10
test/testdata/chromium/html/sample4/index.html
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Gutenberg</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Hello, world!</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user