From 83fdc01df16315819dc35d1f4eec22718b604f8d Mon Sep 17 00:00:00 2001 From: Julien Neuhart Date: Fri, 24 Oct 2025 14:46:14 +0000 Subject: [PATCH] fix(chromium): accept case-insensitive values for the cookies' sameSite attribute - closes #1331 --- pkg/modules/chromium/routes.go | 32 +++++++++++++++++-- .../features/chromium_convert_url.feature | 16 ++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/pkg/modules/chromium/routes.go b/pkg/modules/chromium/routes.go index 56d6729..bec0688 100644 --- a/pkg/modules/chromium/routes.go +++ b/pkg/modules/chromium/routes.go @@ -24,6 +24,11 @@ import ( "github.com/gotenberg/gotenberg/v8/pkg/modules/pdfengines" ) +var sameSiteRegexp = regexp2.MustCompile( + `("sameSite"\s*:\s*")(?i:(lax|strict|none))(")`, + regexp2.None, +) + // FormDataChromiumOptions creates [Options] from the form data. Fallback to // the default value if the considered key is not present. func FormDataChromiumOptions(ctx *api.Context) (*api.FormData, Options) { @@ -84,7 +89,30 @@ func FormDataChromiumOptions(ctx *api.Context) (*api.FormData, Options) { return nil } - err := json.Unmarshal([]byte(value), &cookies) + // sameSite attribute from cookies must accept case-insensitive + // values. + // See https://github.com/gotenberg/gotenberg/issues/1331. + normalized, err := sameSiteRegexp.ReplaceFunc(value, func(m regexp2.Match) string { + groups := m.Groups() + provided := groups[2].String() + var canon string + switch strings.ToLower(provided) { + case "lax": + canon = "Lax" + case "strict": + canon = "Strict" + case "none": + canon = "None" + default: + canon = provided + } + return groups[1].String() + canon + groups[3].String() + }, -1, -1) + if err != nil { + return fmt.Errorf("normalize sameSite from cookies: %w") + } + + err = json.Unmarshal([]byte(normalized), &cookies) if err != nil { return fmt.Errorf("unmarshal cookies: %w", err) } @@ -141,7 +169,7 @@ func FormDataChromiumOptions(ctx *api.Context) (*api.FormData, Options) { var scopeRegexp *regexp2.Regexp if len(scope) > 0 { - p, errCompile := regexp2.Compile(scope, 0) + p, errCompile := regexp2.Compile(scope, regexp2.None) if errCompile != nil { err = multierr.Append(err, fmt.Errorf("invalid scope regex pattern for header '%s': %w", k, errCompile)) continue diff --git a/test/integration/features/chromium_convert_url.feature b/test/integration/features/chromium_convert_url.feature index 43afb4e..319f1aa 100644 --- a/test/integration/features/chromium_convert_url.feature +++ b/test/integration/features/chromium_convert_url.feature @@ -173,6 +173,22 @@ Feature: /forms/chromium/convert/url Then the server request cookie "cookie_1" should be "foo" Then the server request cookie "cookie_2" should be "" + # See https://github.com/gotenberg/gotenberg/issues/1130. + Scenario: POST /forms/chromium/convert/url (case-insensitive sameSite) + Given I have a default Gotenberg container + Given I have a static server + When I make a "POST" request to Gotenberg at the "/forms/chromium/convert/url" endpoint with the following form data and header(s): + | url | http://host.docker.internal:%d/html/testdata/page-1-html/index.html | field | + | cookies | [{"name":"cookie_1","value":"foo","domain":"host.docker.internal:%d"},{"name":"cookie_2","value":"bar","domain":"domain.com","sameSite":"lax"}] | field | + | Gotenberg-Output-Filename | foo | header | + Then the response status code should be 200 + Then the response header "Content-Type" should be "application/pdf" + Then there should be 1 PDF(s) in the response + Then there should be the following file(s) in the response: + | foo.pdf | + Then the server request cookie "cookie_1" should be "foo" + Then the server request cookie "cookie_2" should be "" + Scenario: POST /forms/chromium/convert/url (Wait Delay) Given I have a default Gotenberg container Given I have a static server