From e308bfd8bdcba01bc953589c6da7e5fd24fabda7 Mon Sep 17 00:00:00 2001 From: Joe <875022+moloch--@users.noreply.github.com> Date: Wed, 17 Apr 2024 16:06:43 -0700 Subject: [PATCH 01/12] Fix path traversal (#274) ## What type of PR is this? (check all applicable) - [ ] Refactor - [ ] Feature - [x] Bug Fix - [ ] Optimization - [ ] Documentation Update - [ ] Go Version Update - [ ] Dependency Update ## Added/updated tests? - [ ] Yes - [x] No, and this is why: _no additional tests needed, small fix_ - [ ] I need help with writing tests ## Run verifications and test - [ ] `make verify` is passing - [x] `make test` is passing ``` PASS coverage: 78.1% of statements ok github.com/gorilla/sessions 1.155s coverage: 78.1% of statements ``` --- store.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/store.go b/store.go index aea37e4..68d4ce5 100644 --- a/store.go +++ b/store.go @@ -14,6 +14,11 @@ import ( "github.com/gorilla/securecookie" ) +const ( + // File name prefix for session files. + sessionFilePrefix = "session_" +) + // Store is an interface for custom session stores. // // See CookieStore and FilesystemStore for examples. @@ -257,7 +262,7 @@ func (s *FilesystemStore) save(session *Session) error { if err != nil { return err } - filename := filepath.Join(s.path, "session_"+session.ID) + filename := filepath.Join(s.path, sessionFilePrefix+filepath.Base(session.ID)) fileMutex.Lock() defer fileMutex.Unlock() return os.WriteFile(filename, []byte(encoded), 0600) @@ -265,7 +270,7 @@ func (s *FilesystemStore) save(session *Session) error { // load reads a file and decodes its content into session.Values. func (s *FilesystemStore) load(session *Session) error { - filename := filepath.Join(s.path, "session_"+session.ID) + filename := filepath.Join(s.path, sessionFilePrefix+filepath.Base(session.ID)) fileMutex.RLock() defer fileMutex.RUnlock() fdata, err := os.ReadFile(filepath.Clean(filename)) @@ -281,7 +286,7 @@ func (s *FilesystemStore) load(session *Session) error { // delete session file func (s *FilesystemStore) erase(session *Session) error { - filename := filepath.Join(s.path, "session_"+session.ID) + filename := filepath.Join(s.path, sessionFilePrefix+filepath.Base(session.ID)) fileMutex.RLock() defer fileMutex.RUnlock() From bdabf0ac29ab2354c0674cb0dcd3a4f31f53589d Mon Sep 17 00:00:00 2001 From: kashishbehl <104421875+kashishbehl@users.noreply.github.com> Date: Sat, 4 May 2024 21:31:06 +0530 Subject: [PATCH 02/12] #272: feat: Add support for paritioned attribute in cookies as per chrome 3rd party cookie phaseout (#273) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [ ] Refactor - [x] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update - [ ] Go Version Update - [ ] Dependency Update ## Description The PR contains the change to add Partitioned attribute in the cookies. As chrome will be deprecating support for 3rd Party cookies, we need to add support for CHIPS to make cookies partitioned to the website. ## Related Tickets & Documents - Related Issue # - Closes #272 ## Added/updated tests? - [x] Yes - [ ] No, and this is why: _please replace this line with details on why tests have not been included_ - [ ] I need help with writing tests ## Run verifications and test - [ ] `make verify` is passing - [ ] `make test` is passing --- cookie.go | 1 + cookie_go111.go | 1 + cookie_test.go | 20 ++++++++++++------- options.go | 1 + options_go111.go | 1 + .../github.com/gorilla/securecookie/README.md | 1 + 6 files changed, 18 insertions(+), 7 deletions(-) diff --git a/cookie.go b/cookie.go index 6612662..fa70e7c 100644 --- a/cookie.go +++ b/cookie.go @@ -15,6 +15,7 @@ func newCookieFromOptions(name, value string, options *Options) *http.Cookie { MaxAge: options.MaxAge, Secure: options.Secure, HttpOnly: options.HttpOnly, + Partitioned: options.Partitioned, } } diff --git a/cookie_go111.go b/cookie_go111.go index 9b58828..d5e9e62 100644 --- a/cookie_go111.go +++ b/cookie_go111.go @@ -16,6 +16,7 @@ func newCookieFromOptions(name, value string, options *Options) *http.Cookie { Secure: options.Secure, HttpOnly: options.HttpOnly, SameSite: options.SameSite, + Partitioned: options.Partitioned, } } diff --git a/cookie_test.go b/cookie_test.go index acb4efb..8e02fbc 100644 --- a/cookie_test.go +++ b/cookie_test.go @@ -14,14 +14,16 @@ func TestNewCookieFromOptions(t *testing.T) { maxAge int secure bool httpOnly bool + partitioned bool }{ - {"", "bar", "/foo/bar", "foo.example.com", 3600, true, true}, - {"foo", "", "/foo/bar", "foo.example.com", 3600, true, true}, - {"foo", "bar", "", "foo.example.com", 3600, true, true}, - {"foo", "bar", "/foo/bar", "", 3600, true, true}, - {"foo", "bar", "/foo/bar", "foo.example.com", 0, true, true}, - {"foo", "bar", "/foo/bar", "foo.example.com", 3600, false, true}, - {"foo", "bar", "/foo/bar", "foo.example.com", 3600, true, false}, + {"", "bar", "/foo/bar", "foo.example.com", 3600, true, true, true}, + {"foo", "", "/foo/bar", "foo.example.com", 3600, true, true, true}, + {"foo", "bar", "", "foo.example.com", 3600, true, true, true}, + {"foo", "bar", "/foo/bar", "", 3600, true, true, true}, + {"foo", "bar", "/foo/bar", "foo.example.com", 0, true, true, true}, + {"foo", "bar", "/foo/bar", "foo.example.com", 3600, false, true, true}, + {"foo", "bar", "/foo/bar", "foo.example.com", 3600, true, false, true}, + {"foo", "bar", "/foo/bar", "foo.example.com", 3600, true, true, false}, } for i, v := range tests { options := &Options{ @@ -30,6 +32,7 @@ func TestNewCookieFromOptions(t *testing.T) { MaxAge: v.maxAge, Secure: v.secure, HttpOnly: v.httpOnly, + Partitioned: v.partitioned, } cookie := newCookieFromOptions(v.name, v.value, options) if cookie.Name != v.name { @@ -53,5 +56,8 @@ func TestNewCookieFromOptions(t *testing.T) { if cookie.HttpOnly != v.httpOnly { t.Fatalf("%v: bad cookie httpOnly: got %v, want %v", i+1, cookie.HttpOnly, v.httpOnly) } + if cookie.Partitioned != v.partitioned { + t.Fatalf("%v: bad cookie partitioned: got %v, want %v", i+1, cookie.Partitioned, v.partitioned) + } } } diff --git a/options.go b/options.go index d33d076..ec07068 100644 --- a/options.go +++ b/options.go @@ -16,4 +16,5 @@ type Options struct { MaxAge int Secure bool HttpOnly bool + Partitioned bool } diff --git a/options_go111.go b/options_go111.go index af9cdf0..3214990 100644 --- a/options_go111.go +++ b/options_go111.go @@ -18,6 +18,7 @@ type Options struct { MaxAge int Secure bool HttpOnly bool + Partitioned bool // Defaults to http.SameSiteDefaultMode SameSite http.SameSite } diff --git a/vendor/github.com/gorilla/securecookie/README.md b/vendor/github.com/gorilla/securecookie/README.md index c3b9815..62e4ec7 100644 --- a/vendor/github.com/gorilla/securecookie/README.md +++ b/vendor/github.com/gorilla/securecookie/README.md @@ -55,6 +55,7 @@ func SetCookieHandler(w http.ResponseWriter, r *http.Request) { Path: "/", Secure: true, HttpOnly: true, + Partitioned: true, } http.SetCookie(w, cookie) } From ef99c782e9aae430b326a614151c983dcd7c2c1d Mon Sep 17 00:00:00 2001 From: Bharat Rajani Date: Sat, 15 Jun 2024 07:59:33 +0530 Subject: [PATCH 03/12] fix(cookie): Add default samesite (#276) Sets the SameSite cookie attribute to None in the Set-Cookie header. The SameSite=None value provides a reasonable balance between security and usability for websites. This also requires setting Secure=True by default. Reference: https://owasp.org/www-community/SameSite Related Tickets & Documents - Related Issue # https://github.com/gorilla/sessions/issues/256 - Closes https://github.com/gorilla/sessions/issues/256 --- cookie_go111_test.go | 1 + sessions_test.go | 9 +++++++++ store.go | 6 ++++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cookie_go111_test.go b/cookie_go111_test.go index e6137c7..2fad2e7 100644 --- a/cookie_go111_test.go +++ b/cookie_go111_test.go @@ -17,6 +17,7 @@ func TestNewCookieFromOptionsSameSite(t *testing.T) { {http.SameSiteDefaultMode}, {http.SameSiteLaxMode}, {http.SameSiteStrictMode}, + {http.SameSiteNoneMode}, } for i, v := range tests { options := &Options{ diff --git a/sessions_test.go b/sessions_test.go index ddba006..9476c22 100644 --- a/sessions_test.go +++ b/sessions_test.go @@ -9,6 +9,7 @@ import ( "encoding/gob" "net/http" "net/http/httptest" + "strings" "testing" ) @@ -39,6 +40,10 @@ func TestFlashes(t *testing.T) { store := NewCookieStore([]byte("secret-key")) + if store.Options.SameSite != http.SameSiteNoneMode { + t.Fatalf("cookie store error: default same site is not set to None") + } + // Round 1 ---------------------------------------------------------------- req, _ = http.NewRequest("GET", "http://localhost:8080/", nil) @@ -67,6 +72,10 @@ func TestFlashes(t *testing.T) { t.Fatal("No cookies. Header:", hdr) } + if !strings.Contains(cookies[0], "SameSite=None") || !strings.Contains(cookies[0], "Secure") { + t.Fatal("Set-Cookie does not contains SameSite=None with Secure, cookie string:", cookies[0]) + } + if _, err = store.Get(req, "session:key"); err.Error() != "sessions: invalid character in cookie name: session:key" { t.Fatalf("Expected error due to invalid cookie name") } diff --git a/store.go b/store.go index 68d4ce5..24db822 100644 --- a/store.go +++ b/store.go @@ -54,8 +54,10 @@ func NewCookieStore(keyPairs ...[]byte) *CookieStore { cs := &CookieStore{ Codecs: securecookie.CodecsFromPairs(keyPairs...), Options: &Options{ - Path: "/", - MaxAge: 86400 * 30, + Path: "/", + MaxAge: 86400 * 30, + SameSite: http.SameSiteNoneMode, + Secure: true, }, } From c373b3e334dc26e3e513168292d784d7773f7d27 Mon Sep 17 00:00:00 2001 From: Muhammed Bacalan Date: Sat, 15 Jun 2024 04:32:13 +0200 Subject: [PATCH 04/12] Fix gorillatoolkit link in README.md (#278) Fix "more examples" link not taking you to where more examples are at Related Tickets & Documents: - Closes #246 Signed-off-by: Muhammed Bacalan --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 06119bb..3aef6a4 100644 --- a/README.md +++ b/README.md @@ -59,8 +59,7 @@ secret key used to authenticate the session. Inside the handler, we call some session values in session.Values, which is a `map[interface{}]interface{}`. And finally we call `session.Save()` to save the session in the response. -More examples are available [on the Gorilla -website](https://www.gorillatoolkit.org/pkg/sessions). +More examples are available at [package documentation](https://pkg.go.dev/github.com/gorilla/sessions). ## Store Implementations From 8e2d54718e949e895e6a0268e9d4df396b3fbe06 Mon Sep 17 00:00:00 2001 From: Daniel Holmes Date: Sat, 15 Jun 2024 02:39:54 +0000 Subject: [PATCH 05/12] chore(go): Remove vendored dependencies --- .../gorilla/securecookie/.editorconfig | 20 - .../gorilla/securecookie/.gitignore | 1 - .../github.com/gorilla/securecookie/LICENSE | 27 - .../github.com/gorilla/securecookie/Makefile | 39 -- .../github.com/gorilla/securecookie/README.md | 145 ---- vendor/github.com/gorilla/securecookie/doc.go | 61 -- .../gorilla/securecookie/securecookie.go | 649 ------------------ vendor/modules.txt | 3 - 8 files changed, 945 deletions(-) delete mode 100644 vendor/github.com/gorilla/securecookie/.editorconfig delete mode 100644 vendor/github.com/gorilla/securecookie/.gitignore delete mode 100644 vendor/github.com/gorilla/securecookie/LICENSE delete mode 100644 vendor/github.com/gorilla/securecookie/Makefile delete mode 100644 vendor/github.com/gorilla/securecookie/README.md delete mode 100644 vendor/github.com/gorilla/securecookie/doc.go delete mode 100644 vendor/github.com/gorilla/securecookie/securecookie.go delete mode 100644 vendor/modules.txt diff --git a/vendor/github.com/gorilla/securecookie/.editorconfig b/vendor/github.com/gorilla/securecookie/.editorconfig deleted file mode 100644 index 2940ec9..0000000 --- a/vendor/github.com/gorilla/securecookie/.editorconfig +++ /dev/null @@ -1,20 +0,0 @@ -; https://editorconfig.org/ - -root = true - -[*] -insert_final_newline = true -charset = utf-8 -trim_trailing_whitespace = true -indent_style = space -indent_size = 2 - -[{Makefile,go.mod,go.sum,*.go,.gitmodules}] -indent_style = tab -indent_size = 4 - -[*.md] -indent_size = 4 -trim_trailing_whitespace = false - -eclint_indent_style = unset diff --git a/vendor/github.com/gorilla/securecookie/.gitignore b/vendor/github.com/gorilla/securecookie/.gitignore deleted file mode 100644 index 84039fe..0000000 --- a/vendor/github.com/gorilla/securecookie/.gitignore +++ /dev/null @@ -1 +0,0 @@ -coverage.coverprofile diff --git a/vendor/github.com/gorilla/securecookie/LICENSE b/vendor/github.com/gorilla/securecookie/LICENSE deleted file mode 100644 index bb9d80b..0000000 --- a/vendor/github.com/gorilla/securecookie/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2023 The Gorilla Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gorilla/securecookie/Makefile b/vendor/github.com/gorilla/securecookie/Makefile deleted file mode 100644 index 2b9008a..0000000 --- a/vendor/github.com/gorilla/securecookie/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -GO_LINT=$(shell which golangci-lint 2> /dev/null || echo '') -GO_LINT_URI=github.com/golangci/golangci-lint/cmd/golangci-lint@latest - -GO_SEC=$(shell which gosec 2> /dev/null || echo '') -GO_SEC_URI=github.com/securego/gosec/v2/cmd/gosec@latest - -GO_VULNCHECK=$(shell which govulncheck 2> /dev/null || echo '') -GO_VULNCHECK_URI=golang.org/x/vuln/cmd/govulncheck@latest - -.PHONY: golangci-lint -golangci-lint: - $(if $(GO_LINT), ,go install $(GO_LINT_URI)) - @echo "##### Running golangci-lint" - golangci-lint run -v - -.PHONY: gosec -gosec: - $(if $(GO_SEC), ,go install $(GO_SEC_URI)) - @echo "##### Running gosec" - gosec ./... - -.PHONY: govulncheck -govulncheck: - $(if $(GO_VULNCHECK), ,go install $(GO_VULNCHECK_URI)) - @echo "##### Running govulncheck" - govulncheck ./... - -.PHONY: verify -verify: golangci-lint gosec govulncheck - -.PHONY: test -test: - @echo "##### Running tests" - go test -race -cover -coverprofile=coverage.coverprofile -covermode=atomic -v ./... - -.PHONY: fuzz -fuzz: - @echo "##### Running fuzz tests" - go test -v -fuzz FuzzEncodeDecode -fuzztime 60s diff --git a/vendor/github.com/gorilla/securecookie/README.md b/vendor/github.com/gorilla/securecookie/README.md deleted file mode 100644 index 62e4ec7..0000000 --- a/vendor/github.com/gorilla/securecookie/README.md +++ /dev/null @@ -1,145 +0,0 @@ -# gorilla/securecookie - -![testing](https://github.com/gorilla/securecookie/actions/workflows/test.yml/badge.svg) -[![codecov](https://codecov.io/github/gorilla/securecookie/branch/main/graph/badge.svg)](https://codecov.io/github/gorilla/securecookie) -[![godoc](https://godoc.org/github.com/gorilla/securecookie?status.svg)](https://godoc.org/github.com/gorilla/securecookie) -[![sourcegraph](https://sourcegraph.com/github.com/gorilla/securecookie/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/securecookie?badge) - -![Gorilla Logo](https://github.com/gorilla/.github/assets/53367916/d92caabf-98e0-473e-bfbf-ab554ba435e5) - -securecookie encodes and decodes authenticated and optionally encrypted -cookie values. - -Secure cookies can't be forged, because their values are validated using HMAC. -When encrypted, the content is also inaccessible to malicious eyes. It is still -recommended that sensitive data not be stored in cookies, and that HTTPS be used -to prevent cookie [replay attacks](https://en.wikipedia.org/wiki/Replay_attack). - -## Examples - -To use it, first create a new SecureCookie instance: - -```go -// Hash keys should be at least 32 bytes long -var hashKey = []byte("very-secret") -// Block keys should be 16 bytes (AES-128) or 32 bytes (AES-256) long. -// Shorter keys may weaken the encryption used. -var blockKey = []byte("a-lot-secret") -var s = securecookie.New(hashKey, blockKey) -``` - -The hashKey is required, used to authenticate the cookie value using HMAC. -It is recommended to use a key with 32 or 64 bytes. - -The blockKey is optional, used to encrypt the cookie value -- set it to nil -to not use encryption. If set, the length must correspond to the block size -of the encryption algorithm. For AES, used by default, valid lengths are -16, 24, or 32 bytes to select AES-128, AES-192, or AES-256. - -Strong keys can be created using the convenience function -`GenerateRandomKey()`. Note that keys created using `GenerateRandomKey()` are not -automatically persisted. New keys will be created when the application is -restarted, and previously issued cookies will not be able to be decoded. - -Once a SecureCookie instance is set, use it to encode a cookie value: - -```go -func SetCookieHandler(w http.ResponseWriter, r *http.Request) { - value := map[string]string{ - "foo": "bar", - } - if encoded, err := s.Encode("cookie-name", value); err == nil { - cookie := &http.Cookie{ - Name: "cookie-name", - Value: encoded, - Path: "/", - Secure: true, - HttpOnly: true, - Partitioned: true, - } - http.SetCookie(w, cookie) - } -} -``` - -Later, use the same SecureCookie instance to decode and validate a cookie -value: - -```go -func ReadCookieHandler(w http.ResponseWriter, r *http.Request) { - if cookie, err := r.Cookie("cookie-name"); err == nil { - value := make(map[string]string) - if err = s2.Decode("cookie-name", cookie.Value, &value); err == nil { - fmt.Fprintf(w, "The value of foo is %q", value["foo"]) - } - } -} -``` - -We stored a map[string]string, but secure cookies can hold any value that -can be encoded using `encoding/gob`. To store custom types, they must be -registered first using gob.Register(). For basic types this is not needed; -it works out of the box. An optional JSON encoder that uses `encoding/json` is -available for types compatible with JSON. - -### Key Rotation -Rotating keys is an important part of any security strategy. The `EncodeMulti` and -`DecodeMulti` functions allow for multiple keys to be rotated in and out. -For example, let's take a system that stores keys in a map: - -```go -// keys stored in a map will not be persisted between restarts -// a more persistent storage should be considered for production applications. -var cookies = map[string]*securecookie.SecureCookie{ - "previous": securecookie.New( - securecookie.GenerateRandomKey(64), - securecookie.GenerateRandomKey(32), - ), - "current": securecookie.New( - securecookie.GenerateRandomKey(64), - securecookie.GenerateRandomKey(32), - ), -} -``` - -Using the current key to encode new cookies: -```go -func SetCookieHandler(w http.ResponseWriter, r *http.Request) { - value := map[string]string{ - "foo": "bar", - } - if encoded, err := securecookie.EncodeMulti("cookie-name", value, cookies["current"]); err == nil { - cookie := &http.Cookie{ - Name: "cookie-name", - Value: encoded, - Path: "/", - } - http.SetCookie(w, cookie) - } -} -``` - -Later, decode cookies. Check against all valid keys: -```go -func ReadCookieHandler(w http.ResponseWriter, r *http.Request) { - if cookie, err := r.Cookie("cookie-name"); err == nil { - value := make(map[string]string) - err = securecookie.DecodeMulti("cookie-name", cookie.Value, &value, cookies["current"], cookies["previous"]) - if err == nil { - fmt.Fprintf(w, "The value of foo is %q", value["foo"]) - } - } -} -``` - -Rotate the keys. This strategy allows previously issued cookies to be valid until the next rotation: -```go -func Rotate(newCookie *securecookie.SecureCookie) { - cookies["previous"] = cookies["current"] - cookies["current"] = newCookie -} -``` - -## License - -BSD licensed. See the LICENSE file for details. diff --git a/vendor/github.com/gorilla/securecookie/doc.go b/vendor/github.com/gorilla/securecookie/doc.go deleted file mode 100644 index ae89408..0000000 --- a/vendor/github.com/gorilla/securecookie/doc.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2012 The Gorilla Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -Package securecookie encodes and decodes authenticated and optionally -encrypted cookie values. - -Secure cookies can't be forged, because their values are validated using HMAC. -When encrypted, the content is also inaccessible to malicious eyes. - -To use it, first create a new SecureCookie instance: - - var hashKey = []byte("very-secret") - var blockKey = []byte("a-lot-secret") - var s = securecookie.New(hashKey, blockKey) - -The hashKey is required, used to authenticate the cookie value using HMAC. -It is recommended to use a key with 32 or 64 bytes. - -The blockKey is optional, used to encrypt the cookie value -- set it to nil -to not use encryption. If set, the length must correspond to the block size -of the encryption algorithm. For AES, used by default, valid lengths are -16, 24, or 32 bytes to select AES-128, AES-192, or AES-256. - -Strong keys can be created using the convenience function GenerateRandomKey(). - -Once a SecureCookie instance is set, use it to encode a cookie value: - - func SetCookieHandler(w http.ResponseWriter, r *http.Request) { - value := map[string]string{ - "foo": "bar", - } - if encoded, err := s.Encode("cookie-name", value); err == nil { - cookie := &http.Cookie{ - Name: "cookie-name", - Value: encoded, - Path: "/", - } - http.SetCookie(w, cookie) - } - } - -Later, use the same SecureCookie instance to decode and validate a cookie -value: - - func ReadCookieHandler(w http.ResponseWriter, r *http.Request) { - if cookie, err := r.Cookie("cookie-name"); err == nil { - value := make(map[string]string) - if err = s2.Decode("cookie-name", cookie.Value, &value); err == nil { - fmt.Fprintf(w, "The value of foo is %q", value["foo"]) - } - } - } - -We stored a map[string]string, but secure cookies can hold any value that -can be encoded using encoding/gob. To store custom types, they must be -registered first using gob.Register(). For basic types this is not needed; -it works out of the box. -*/ -package securecookie diff --git a/vendor/github.com/gorilla/securecookie/securecookie.go b/vendor/github.com/gorilla/securecookie/securecookie.go deleted file mode 100644 index 4d5ea86..0000000 --- a/vendor/github.com/gorilla/securecookie/securecookie.go +++ /dev/null @@ -1,649 +0,0 @@ -// Copyright 2012 The Gorilla Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package securecookie - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/hmac" - "crypto/rand" - "crypto/sha256" - "crypto/subtle" - "encoding/base64" - "encoding/gob" - "encoding/json" - "fmt" - "hash" - "io" - "strconv" - "strings" - "time" -) - -// Error is the interface of all errors returned by functions in this library. -type Error interface { - error - - // IsUsage returns true for errors indicating the client code probably - // uses this library incorrectly. For example, the client may have - // failed to provide a valid hash key, or may have failed to configure - // the Serializer adequately for encoding value. - IsUsage() bool - - // IsDecode returns true for errors indicating that a cookie could not - // be decoded and validated. Since cookies are usually untrusted - // user-provided input, errors of this type should be expected. - // Usually, the proper action is simply to reject the request. - IsDecode() bool - - // IsInternal returns true for unexpected errors occurring in the - // securecookie implementation. - IsInternal() bool - - // Cause, if it returns a non-nil value, indicates that this error was - // propagated from some underlying library. If this method returns nil, - // this error was raised directly by this library. - // - // Cause is provided principally for debugging/logging purposes; it is - // rare that application logic should perform meaningfully different - // logic based on Cause. See, for example, the caveats described on - // (MultiError).Cause(). - Cause() error -} - -// errorType is a bitmask giving the error type(s) of an cookieError value. -type errorType int - -const ( - usageError = errorType(1 << iota) - decodeError - internalError -) - -type cookieError struct { - typ errorType - msg string - cause error -} - -func (e cookieError) IsUsage() bool { return (e.typ & usageError) != 0 } -func (e cookieError) IsDecode() bool { return (e.typ & decodeError) != 0 } -func (e cookieError) IsInternal() bool { return (e.typ & internalError) != 0 } - -func (e cookieError) Cause() error { return e.cause } - -func (e cookieError) Error() string { - parts := []string{"securecookie: "} - if e.msg == "" { - parts = append(parts, "error") - } else { - parts = append(parts, e.msg) - } - if c := e.Cause(); c != nil { - parts = append(parts, " - caused by: ", c.Error()) - } - return strings.Join(parts, "") -} - -var ( - errGeneratingIV = cookieError{typ: internalError, msg: "failed to generate random iv"} - - errNoCodecs = cookieError{typ: usageError, msg: "no codecs provided"} - errHashKeyNotSet = cookieError{typ: usageError, msg: "hash key is not set"} - errBlockKeyNotSet = cookieError{typ: usageError, msg: "block key is not set"} - errEncodedValueTooLong = cookieError{typ: usageError, msg: "the value is too long"} - - errValueToDecodeTooLong = cookieError{typ: decodeError, msg: "the value is too long"} - errTimestampInvalid = cookieError{typ: decodeError, msg: "invalid timestamp"} - errTimestampTooNew = cookieError{typ: decodeError, msg: "timestamp is too new"} - errTimestampExpired = cookieError{typ: decodeError, msg: "expired timestamp"} - errDecryptionFailed = cookieError{typ: decodeError, msg: "the value could not be decrypted"} - errValueNotByte = cookieError{typ: decodeError, msg: "value not a []byte."} - errValueNotBytePtr = cookieError{typ: decodeError, msg: "value not a pointer to []byte."} - - // ErrMacInvalid indicates that cookie decoding failed because the HMAC - // could not be extracted and verified. Direct use of this error - // variable is deprecated; it is public only for legacy compatibility, - // and may be privatized in the future, as it is rarely useful to - // distinguish between this error and other Error implementations. - ErrMacInvalid = cookieError{typ: decodeError, msg: "the value is not valid"} -) - -// Codec defines an interface to encode and decode cookie values. -type Codec interface { - Encode(name string, value interface{}) (string, error) - Decode(name, value string, dst interface{}) error -} - -// New returns a new SecureCookie. -// -// hashKey is required, used to authenticate values using HMAC. Create it using -// GenerateRandomKey(). It is recommended to use a key with 32 or 64 bytes. -// -// blockKey is optional, used to encrypt values. Create it using -// GenerateRandomKey(). The key length must correspond to the key size -// of the encryption algorithm. For AES, used by default, valid lengths are -// 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256. -// The default encoder used for cookie serialization is encoding/gob. -// -// Note that keys created using GenerateRandomKey() are not automatically -// persisted. New keys will be created when the application is restarted, and -// previously issued cookies will not be able to be decoded. -func New(hashKey, blockKey []byte) *SecureCookie { - s := &SecureCookie{ - hashKey: hashKey, - blockKey: blockKey, - hashFunc: sha256.New, - maxAge: 86400 * 30, - maxLength: 4096, - sz: GobEncoder{}, - } - if len(hashKey) == 0 { - s.err = errHashKeyNotSet - } - if blockKey != nil { - s.BlockFunc(aes.NewCipher) - } - return s -} - -// SecureCookie encodes and decodes authenticated and optionally encrypted -// cookie values. -type SecureCookie struct { - hashKey []byte - hashFunc func() hash.Hash - blockKey []byte - block cipher.Block - maxLength int - maxAge int64 - minAge int64 - err error - sz Serializer - // For testing purposes, the function that returns the current timestamp. - // If not set, it will use time.Now().UTC().Unix(). - timeFunc func() int64 -} - -// Serializer provides an interface for providing custom serializers for cookie -// values. -type Serializer interface { - Serialize(src interface{}) ([]byte, error) - Deserialize(src []byte, dst interface{}) error -} - -// GobEncoder encodes cookie values using encoding/gob. This is the simplest -// encoder and can handle complex types via gob.Register. -type GobEncoder struct{} - -// JSONEncoder encodes cookie values using encoding/json. Users who wish to -// encode complex types need to satisfy the json.Marshaller and -// json.Unmarshaller interfaces. -type JSONEncoder struct{} - -// NopEncoder does not encode cookie values, and instead simply accepts a []byte -// (as an interface{}) and returns a []byte. This is particularly useful when -// you encoding an object upstream and do not wish to re-encode it. -type NopEncoder struct{} - -// MaxLength restricts the maximum length, in bytes, for the cookie value. -// -// Default is 4096, which is the maximum value accepted by Internet Explorer. -func (s *SecureCookie) MaxLength(value int) *SecureCookie { - s.maxLength = value - return s -} - -// MaxAge restricts the maximum age, in seconds, for the cookie value. -// -// Default is 86400 * 30. Set it to 0 for no restriction. -func (s *SecureCookie) MaxAge(value int) *SecureCookie { - s.maxAge = int64(value) - return s -} - -// MinAge restricts the minimum age, in seconds, for the cookie value. -// -// Default is 0 (no restriction). -func (s *SecureCookie) MinAge(value int) *SecureCookie { - s.minAge = int64(value) - return s -} - -// HashFunc sets the hash function used to create HMAC. -// -// Default is crypto/sha256.New. -func (s *SecureCookie) HashFunc(f func() hash.Hash) *SecureCookie { - s.hashFunc = f - return s -} - -// BlockFunc sets the encryption function used to create a cipher.Block. -// -// Default is crypto/aes.New. -func (s *SecureCookie) BlockFunc(f func([]byte) (cipher.Block, error)) *SecureCookie { - if s.blockKey == nil { - s.err = errBlockKeyNotSet - } else if block, err := f(s.blockKey); err == nil { - s.block = block - } else { - s.err = cookieError{cause: err, typ: usageError} - } - return s -} - -// Encoding sets the encoding/serialization method for cookies. -// -// Default is encoding/gob. To encode special structures using encoding/gob, -// they must be registered first using gob.Register(). -func (s *SecureCookie) SetSerializer(sz Serializer) *SecureCookie { - s.sz = sz - - return s -} - -// Encode encodes a cookie value. -// -// It serializes, optionally encrypts, signs with a message authentication code, -// and finally encodes the value. -// -// The name argument is the cookie name. It is stored with the encoded value. -// The value argument is the value to be encoded. It can be any value that can -// be encoded using the currently selected serializer; see SetSerializer(). -// -// It is the client's responsibility to ensure that value, when encoded using -// the current serialization/encryption settings on s and then base64-encoded, -// is shorter than the maximum permissible length. -func (s *SecureCookie) Encode(name string, value interface{}) (string, error) { - if s.err != nil { - return "", s.err - } - if s.hashKey == nil { - s.err = errHashKeyNotSet - return "", s.err - } - var err error - var b []byte - // 1. Serialize. - if b, err = s.sz.Serialize(value); err != nil { - return "", cookieError{cause: err, typ: usageError} - } - // 2. Encrypt (optional). - if s.block != nil { - if b, err = encrypt(s.block, b); err != nil { - return "", cookieError{cause: err, typ: usageError} - } - } - b = encode(b) - // 3. Create MAC for "name|date|value". Extra pipe to be used later. - b = []byte(fmt.Sprintf("%s|%d|%s|", name, s.timestamp(), b)) - mac := createMac(hmac.New(s.hashFunc, s.hashKey), b[:len(b)-1]) - // Append mac, remove name. - b = append(b, mac...)[len(name)+1:] - // 4. Encode to base64. - b = encode(b) - // 5. Check length. - if s.maxLength != 0 && len(b) > s.maxLength { - return "", fmt.Errorf("%s: %d", errEncodedValueTooLong, len(b)) - } - // Done. - return string(b), nil -} - -// Decode decodes a cookie value. -// -// It decodes, verifies a message authentication code, optionally decrypts and -// finally deserializes the value. -// -// The name argument is the cookie name. It must be the same name used when -// it was stored. The value argument is the encoded cookie value. The dst -// argument is where the cookie will be decoded. It must be a pointer. -func (s *SecureCookie) Decode(name, value string, dst interface{}) error { - if s.err != nil { - return s.err - } - if s.hashKey == nil { - s.err = errHashKeyNotSet - return s.err - } - // 1. Check length. - if s.maxLength != 0 && len(value) > s.maxLength { - return fmt.Errorf("%s: %d", errValueToDecodeTooLong, len(value)) - } - // 2. Decode from base64. - b, err := decode([]byte(value)) - if err != nil { - return err - } - // 3. Verify MAC. Value is "date|value|mac". - parts := bytes.SplitN(b, []byte("|"), 3) - if len(parts) != 3 { - return ErrMacInvalid - } - h := hmac.New(s.hashFunc, s.hashKey) - b = append([]byte(name+"|"), b[:len(b)-len(parts[2])-1]...) - if err = verifyMac(h, b, parts[2]); err != nil { - return err - } - // 4. Verify date ranges. - var t1 int64 - if t1, err = strconv.ParseInt(string(parts[0]), 10, 64); err != nil { - return errTimestampInvalid - } - t2 := s.timestamp() - if s.minAge != 0 && t1 > t2-s.minAge { - return errTimestampTooNew - } - if s.maxAge != 0 && t1 < t2-s.maxAge { - return errTimestampExpired - } - // 5. Decrypt (optional). - b, err = decode(parts[1]) - if err != nil { - return err - } - if s.block != nil { - if b, err = decrypt(s.block, b); err != nil { - return err - } - } - // 6. Deserialize. - if err = s.sz.Deserialize(b, dst); err != nil { - return cookieError{cause: err, typ: decodeError} - } - // Done. - return nil -} - -// timestamp returns the current timestamp, in seconds. -// -// For testing purposes, the function that generates the timestamp can be -// overridden. If not set, it will return time.Now().UTC().Unix(). -func (s *SecureCookie) timestamp() int64 { - if s.timeFunc == nil { - return time.Now().UTC().Unix() - } - return s.timeFunc() -} - -// Authentication ------------------------------------------------------------- - -// createMac creates a message authentication code (MAC). -func createMac(h hash.Hash, value []byte) []byte { - h.Write(value) - return h.Sum(nil) -} - -// verifyMac verifies that a message authentication code (MAC) is valid. -func verifyMac(h hash.Hash, value []byte, mac []byte) error { - mac2 := createMac(h, value) - // Check that both MACs are of equal length, as subtle.ConstantTimeCompare - // does not do this prior to Go 1.4. - if len(mac) == len(mac2) && subtle.ConstantTimeCompare(mac, mac2) == 1 { - return nil - } - return ErrMacInvalid -} - -// Encryption ----------------------------------------------------------------- - -// encrypt encrypts a value using the given block in counter mode. -// -// A random initialization vector ( https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Initialization_vector_(IV) ) with the length of the -// block size is prepended to the resulting ciphertext. -func encrypt(block cipher.Block, value []byte) ([]byte, error) { - iv := GenerateRandomKey(block.BlockSize()) - if iv == nil { - return nil, errGeneratingIV - } - // Encrypt it. - stream := cipher.NewCTR(block, iv) - stream.XORKeyStream(value, value) - // Return iv + ciphertext. - return append(iv, value...), nil -} - -// decrypt decrypts a value using the given block in counter mode. -// -// The value to be decrypted must be prepended by a initialization vector -// ( https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Initialization_vector_(IV) ) with the length of the block size. -func decrypt(block cipher.Block, value []byte) ([]byte, error) { - size := block.BlockSize() - if len(value) > size { - // Extract iv. - iv := value[:size] - // Extract ciphertext. - value = value[size:] - // Decrypt it. - stream := cipher.NewCTR(block, iv) - stream.XORKeyStream(value, value) - return value, nil - } - return nil, errDecryptionFailed -} - -// Serialization -------------------------------------------------------------- - -// Serialize encodes a value using gob. -func (e GobEncoder) Serialize(src interface{}) ([]byte, error) { - buf := new(bytes.Buffer) - enc := gob.NewEncoder(buf) - if err := enc.Encode(src); err != nil { - return nil, cookieError{cause: err, typ: usageError} - } - return buf.Bytes(), nil -} - -// Deserialize decodes a value using gob. -func (e GobEncoder) Deserialize(src []byte, dst interface{}) error { - dec := gob.NewDecoder(bytes.NewBuffer(src)) - if err := dec.Decode(dst); err != nil { - return cookieError{cause: err, typ: decodeError} - } - return nil -} - -// Serialize encodes a value using encoding/json. -func (e JSONEncoder) Serialize(src interface{}) ([]byte, error) { - buf := new(bytes.Buffer) - enc := json.NewEncoder(buf) - if err := enc.Encode(src); err != nil { - return nil, cookieError{cause: err, typ: usageError} - } - return buf.Bytes(), nil -} - -// Deserialize decodes a value using encoding/json. -func (e JSONEncoder) Deserialize(src []byte, dst interface{}) error { - dec := json.NewDecoder(bytes.NewReader(src)) - if err := dec.Decode(dst); err != nil { - return cookieError{cause: err, typ: decodeError} - } - return nil -} - -// Serialize passes a []byte through as-is. -func (e NopEncoder) Serialize(src interface{}) ([]byte, error) { - if b, ok := src.([]byte); ok { - return b, nil - } - - return nil, errValueNotByte -} - -// Deserialize passes a []byte through as-is. -func (e NopEncoder) Deserialize(src []byte, dst interface{}) error { - if dat, ok := dst.(*[]byte); ok { - *dat = src - return nil - } - return errValueNotBytePtr -} - -// Encoding ------------------------------------------------------------------- - -// encode encodes a value using base64. -func encode(value []byte) []byte { - encoded := make([]byte, base64.URLEncoding.EncodedLen(len(value))) - base64.URLEncoding.Encode(encoded, value) - return encoded -} - -// decode decodes a cookie using base64. -func decode(value []byte) ([]byte, error) { - decoded := make([]byte, base64.URLEncoding.DecodedLen(len(value))) - b, err := base64.URLEncoding.Decode(decoded, value) - if err != nil { - return nil, cookieError{cause: err, typ: decodeError, msg: "base64 decode failed"} - } - return decoded[:b], nil -} - -// Helpers -------------------------------------------------------------------- - -// GenerateRandomKey creates a random key with the given length in bytes. -// On failure, returns nil. -// -// Note that keys created using `GenerateRandomKey()` are not automatically -// persisted. New keys will be created when the application is restarted, and -// previously issued cookies will not be able to be decoded. -// -// Callers should explicitly check for the possibility of a nil return, treat -// it as a failure of the system random number generator, and not continue. -func GenerateRandomKey(length int) []byte { - k := make([]byte, length) - if _, err := io.ReadFull(rand.Reader, k); err != nil { - return nil - } - return k -} - -// CodecsFromPairs returns a slice of SecureCookie instances. -// -// It is a convenience function to create a list of codecs for key rotation. Note -// that the generated Codecs will have the default options applied: callers -// should iterate over each Codec and type-assert the underlying *SecureCookie to -// change these. -// -// Example: -// -// codecs := securecookie.CodecsFromPairs( -// []byte("new-hash-key"), -// []byte("new-block-key"), -// []byte("old-hash-key"), -// []byte("old-block-key"), -// ) -// -// // Modify each instance. -// for _, s := range codecs { -// if cookie, ok := s.(*securecookie.SecureCookie); ok { -// cookie.MaxAge(86400 * 7) -// cookie.SetSerializer(securecookie.JSONEncoder{}) -// cookie.HashFunc(sha512.New512_256) -// } -// } -func CodecsFromPairs(keyPairs ...[]byte) []Codec { - codecs := make([]Codec, len(keyPairs)/2+len(keyPairs)%2) - for i := 0; i < len(keyPairs); i += 2 { - var blockKey []byte - if i+1 < len(keyPairs) { - blockKey = keyPairs[i+1] - } - codecs[i/2] = New(keyPairs[i], blockKey) - } - return codecs -} - -// EncodeMulti encodes a cookie value using a group of codecs. -// -// The codecs are tried in order. Multiple codecs are accepted to allow -// key rotation. -// -// On error, may return a MultiError. -func EncodeMulti(name string, value interface{}, codecs ...Codec) (string, error) { - if len(codecs) == 0 { - return "", errNoCodecs - } - - var errors MultiError - for _, codec := range codecs { - encoded, err := codec.Encode(name, value) - if err == nil { - return encoded, nil - } - errors = append(errors, err) - } - return "", errors -} - -// DecodeMulti decodes a cookie value using a group of codecs. -// -// The codecs are tried in order. Multiple codecs are accepted to allow -// key rotation. -// -// On error, may return a MultiError. -func DecodeMulti(name string, value string, dst interface{}, codecs ...Codec) error { - if len(codecs) == 0 { - return errNoCodecs - } - - var errors MultiError - for _, codec := range codecs { - err := codec.Decode(name, value, dst) - if err == nil { - return nil - } - errors = append(errors, err) - } - return errors -} - -// MultiError groups multiple errors. -type MultiError []error - -func (m MultiError) IsUsage() bool { return m.any(func(e Error) bool { return e.IsUsage() }) } -func (m MultiError) IsDecode() bool { return m.any(func(e Error) bool { return e.IsDecode() }) } -func (m MultiError) IsInternal() bool { return m.any(func(e Error) bool { return e.IsInternal() }) } - -// Cause returns nil for MultiError; there is no unique underlying cause in the -// general case. -// -// Note: we could conceivably return a non-nil Cause only when there is exactly -// one child error with a Cause. However, it would be brittle for client code -// to rely on the arity of causes inside a MultiError, so we have opted not to -// provide this functionality. Clients which really wish to access the Causes -// of the underlying errors are free to iterate through the errors themselves. -func (m MultiError) Cause() error { return nil } - -func (m MultiError) Error() string { - s, n := "", 0 - for _, e := range m { - if e != nil { - if n == 0 { - s = e.Error() - } - n++ - } - } - switch n { - case 0: - return "(0 errors)" - case 1: - return s - case 2: - return s + " (and 1 other error)" - } - return fmt.Sprintf("%s (and %d other errors)", s, n-1) -} - -// any returns true if any element of m is an Error for which pred returns true. -func (m MultiError) any(pred func(Error) bool) bool { - for _, e := range m { - if ourErr, ok := e.(Error); ok && pred(ourErr) { - return true - } - } - return false -} diff --git a/vendor/modules.txt b/vendor/modules.txt deleted file mode 100644 index 6224b61..0000000 --- a/vendor/modules.txt +++ /dev/null @@ -1,3 +0,0 @@ -# github.com/gorilla/securecookie v1.1.2 -## explicit; go 1.20 -github.com/gorilla/securecookie From ff5660f3c355c08371621b642d76c7a4c88836c6 Mon Sep 17 00:00:00 2001 From: Daniel Holmes Date: Sat, 15 Jun 2024 02:52:55 +0000 Subject: [PATCH 06/12] chore(go): Add warning about main branch Main branch is in a state of flux because of the partitioned cookie attribute which won't land in go until version 1.23. Added a warning to this affect as well as upped the version of go in the go.mod to 1.23 to indicate this change. --- README.md | 5 ++++- go.mod | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3aef6a4..184e7df 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -# sessions +# Gorilla Sessions + +> [!CAUTION] +> The main branch of this respository is in flux and being prepared for the [Go 1.23 release](https://github.com/golang/go/milestone/212). Please use released versions as per the release page. ![testing](https://github.com/gorilla/sessions/actions/workflows/test.yml/badge.svg) [![codecov](https://codecov.io/github/gorilla/sessions/branch/main/graph/badge.svg)](https://codecov.io/github/gorilla/sessions) diff --git a/go.mod b/go.mod index 64cc6a3..25a82cc 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module github.com/gorilla/sessions -go 1.20 +go 1.23 require github.com/gorilla/securecookie v1.1.2 From 7a8159ef2d1bc1afea2b9fbb52e947ded867cf0c Mon Sep 17 00:00:00 2001 From: Daniel Holmes Date: Sat, 15 Jun 2024 02:54:42 +0000 Subject: [PATCH 07/12] chore(go): Remove go version 1.11 support --- cookie_go111.go | 22 ---------------------- cookie_go111_test.go | 31 ------------------------------- cookie_test.go | 30 +++++++++++++++--------------- options_go111.go | 24 ------------------------ 4 files changed, 15 insertions(+), 92 deletions(-) delete mode 100644 cookie_go111.go delete mode 100644 cookie_go111_test.go delete mode 100644 options_go111.go diff --git a/cookie_go111.go b/cookie_go111.go deleted file mode 100644 index d5e9e62..0000000 --- a/cookie_go111.go +++ /dev/null @@ -1,22 +0,0 @@ -//go:build go1.11 -// +build go1.11 - -package sessions - -import "net/http" - -// newCookieFromOptions returns an http.Cookie with the options set. -func newCookieFromOptions(name, value string, options *Options) *http.Cookie { - return &http.Cookie{ - Name: name, - Value: value, - Path: options.Path, - Domain: options.Domain, - MaxAge: options.MaxAge, - Secure: options.Secure, - HttpOnly: options.HttpOnly, - SameSite: options.SameSite, - Partitioned: options.Partitioned, - } - -} diff --git a/cookie_go111_test.go b/cookie_go111_test.go deleted file mode 100644 index 2fad2e7..0000000 --- a/cookie_go111_test.go +++ /dev/null @@ -1,31 +0,0 @@ -//go:build go1.11 -// +build go1.11 - -package sessions - -import ( - "net/http" - "testing" -) - -// Test for setting SameSite field in new http.Cookie from name, value -// and options -func TestNewCookieFromOptionsSameSite(t *testing.T) { - tests := []struct { - sameSite http.SameSite - }{ - {http.SameSiteDefaultMode}, - {http.SameSiteLaxMode}, - {http.SameSiteStrictMode}, - {http.SameSiteNoneMode}, - } - for i, v := range tests { - options := &Options{ - SameSite: v.sameSite, - } - cookie := newCookieFromOptions("", "", options) - if cookie.SameSite != v.sameSite { - t.Fatalf("%v: bad cookie sameSite: got %v, want %v", i+1, cookie.SameSite, v.sameSite) - } - } -} diff --git a/cookie_test.go b/cookie_test.go index 8e02fbc..de1ef44 100644 --- a/cookie_test.go +++ b/cookie_test.go @@ -7,13 +7,13 @@ import ( // Test for creating new http.Cookie from name, value and options func TestNewCookieFromOptions(t *testing.T) { tests := []struct { - name string - value string - path string - domain string - maxAge int - secure bool - httpOnly bool + name string + value string + path string + domain string + maxAge int + secure bool + httpOnly bool partitioned bool }{ {"", "bar", "/foo/bar", "foo.example.com", 3600, true, true, true}, @@ -27,11 +27,11 @@ func TestNewCookieFromOptions(t *testing.T) { } for i, v := range tests { options := &Options{ - Path: v.path, - Domain: v.domain, - MaxAge: v.maxAge, - Secure: v.secure, - HttpOnly: v.httpOnly, + Path: v.path, + Domain: v.domain, + MaxAge: v.maxAge, + Secure: v.secure, + HttpOnly: v.httpOnly, Partitioned: v.partitioned, } cookie := newCookieFromOptions(v.name, v.value, options) @@ -56,8 +56,8 @@ func TestNewCookieFromOptions(t *testing.T) { if cookie.HttpOnly != v.httpOnly { t.Fatalf("%v: bad cookie httpOnly: got %v, want %v", i+1, cookie.HttpOnly, v.httpOnly) } - if cookie.Partitioned != v.partitioned { - t.Fatalf("%v: bad cookie partitioned: got %v, want %v", i+1, cookie.Partitioned, v.partitioned) - } + // if cookie.Partitioned != v.partitioned { + // t.Fatalf("%v: bad cookie partitioned: got %v, want %v", i+1, cookie.Partitioned, v.partitioned) + // } } } diff --git a/options_go111.go b/options_go111.go deleted file mode 100644 index 3214990..0000000 --- a/options_go111.go +++ /dev/null @@ -1,24 +0,0 @@ -//go:build go1.11 -// +build go1.11 - -package sessions - -import "net/http" - -// Options stores configuration for a session or session store. -// -// Fields are a subset of http.Cookie fields. -type Options struct { - Path string - Domain string - // MaxAge=0 means no Max-Age attribute specified and the cookie will be - // deleted after the browser session ends. - // MaxAge<0 means delete cookie immediately. - // MaxAge>0 means Max-Age attribute present and given in seconds. - MaxAge int - Secure bool - HttpOnly bool - Partitioned bool - // Defaults to http.SameSiteDefaultMode - SameSite http.SameSite -} From 466d29e7f343560836292b7c922e1385e908ef95 Mon Sep 17 00:00:00 2001 From: Daniel Holmes Date: Sat, 15 Jun 2024 02:58:08 +0000 Subject: [PATCH 08/12] chore: Update readme and copyrights --- LICENSE | 2 +- cookie.go | 19 ++++++++++--------- cookie_test.go | 10 +++++++--- options.go | 5 +++-- store_test.go | 4 ++++ 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/LICENSE b/LICENSE index bb9d80b..7fa9009 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2023 The Gorilla Authors. All rights reserved. +Copyright (c) 2024 The Gorilla Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/cookie.go b/cookie.go index fa70e7c..28cd31d 100644 --- a/cookie.go +++ b/cookie.go @@ -1,5 +1,6 @@ -//go:build !go1.11 -// +build !go1.11 +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. package sessions @@ -8,13 +9,13 @@ import "net/http" // newCookieFromOptions returns an http.Cookie with the options set. func newCookieFromOptions(name, value string, options *Options) *http.Cookie { return &http.Cookie{ - Name: name, - Value: value, - Path: options.Path, - Domain: options.Domain, - MaxAge: options.MaxAge, - Secure: options.Secure, - HttpOnly: options.HttpOnly, + Name: name, + Value: value, + Path: options.Path, + Domain: options.Domain, + MaxAge: options.MaxAge, + Secure: options.Secure, + HttpOnly: options.HttpOnly, Partitioned: options.Partitioned, } diff --git a/cookie_test.go b/cookie_test.go index de1ef44..de530ca 100644 --- a/cookie_test.go +++ b/cookie_test.go @@ -1,3 +1,7 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package sessions import ( @@ -56,8 +60,8 @@ func TestNewCookieFromOptions(t *testing.T) { if cookie.HttpOnly != v.httpOnly { t.Fatalf("%v: bad cookie httpOnly: got %v, want %v", i+1, cookie.HttpOnly, v.httpOnly) } - // if cookie.Partitioned != v.partitioned { - // t.Fatalf("%v: bad cookie partitioned: got %v, want %v", i+1, cookie.Partitioned, v.partitioned) - // } + if cookie.Partitioned != v.partitioned { + t.Fatalf("%v: bad cookie partitioned: got %v, want %v", i+1, cookie.Partitioned, v.partitioned) + } } } diff --git a/options.go b/options.go index ec07068..32e64d8 100644 --- a/options.go +++ b/options.go @@ -1,5 +1,6 @@ -//go:build !go1.11 -// +build !go1.11 +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. package sessions diff --git a/store_test.go b/store_test.go index 3561e5d..09580bd 100644 --- a/store_test.go +++ b/store_test.go @@ -1,3 +1,7 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package sessions import ( From a56e60c14e37beb3f690c14311c8ad7be7580bb5 Mon Sep 17 00:00:00 2001 From: "./daniele" <47982731+danielepintore@users.noreply.github.com> Date: Tue, 20 Aug 2024 15:38:06 +0200 Subject: [PATCH 09/12] Add mysql store to the readme (#279) Add MySQL store to README Signed-off-by: ./daniele <47982731+danielepintore@users.noreply.github.com> --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 184e7df..540b63d 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ Other implementations of the `sessions.Store` interface: - [github.com/dsoprea/go-appengine-sessioncascade](https://github.com/dsoprea/go-appengine-sessioncascade) - Memcache/Datastore/Context in AppEngine - [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB - [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL +- [github.com/danielepintore/gorilla-sessions-mysql](https://github.com/danielepintore/gorilla-sessions-mysql) - MySQL - [github.com/EnumApps/clustersqlstore](https://github.com/EnumApps/clustersqlstore) - MySQL Cluster - [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL - [github.com/boj/redistore](https://github.com/boj/redistore) - Redis From 6eef180e176e826b77f4d2f5f8e1cf855a87db02 Mon Sep 17 00:00:00 2001 From: Daniel Holmes Date: Tue, 20 Aug 2024 14:01:07 +0000 Subject: [PATCH 10/12] fix: Missing SameSite attribute on options --- cookie.go | 1 + options.go | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cookie.go b/cookie.go index 28cd31d..fd6f48c 100644 --- a/cookie.go +++ b/cookie.go @@ -17,6 +17,7 @@ func newCookieFromOptions(name, value string, options *Options) *http.Cookie { Secure: options.Secure, HttpOnly: options.HttpOnly, Partitioned: options.Partitioned, + SameSite: options.SameSite, } } diff --git a/options.go b/options.go index 32e64d8..6ed7934 100644 --- a/options.go +++ b/options.go @@ -4,6 +4,8 @@ package sessions +import "net/http" + // Options stores configuration for a session or session store. // // Fields are a subset of http.Cookie fields. @@ -14,8 +16,9 @@ type Options struct { // deleted after the browser session ends. // MaxAge<0 means delete cookie immediately. // MaxAge>0 means Max-Age attribute present and given in seconds. - MaxAge int - Secure bool - HttpOnly bool + MaxAge int + Secure bool + HttpOnly bool Partitioned bool + SameSite http.SameSite } From e2083f956282b2e627d9734f4ebbd51fa0339f09 Mon Sep 17 00:00:00 2001 From: Daniel Holmes Date: Tue, 20 Aug 2024 14:05:02 +0000 Subject: [PATCH 11/12] chore: update to go 1.23 for workflows --- .github/workflows/security.yml | 2 +- .github/workflows/test.yml | 2 +- .github/workflows/verify.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index ff4a613..19a6c1c 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -12,7 +12,7 @@ jobs: scan: strategy: matrix: - go: ['1.20','1.21'] + go: ['1.23'] fail-fast: true runs-on: ubuntu-latest steps: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 50a3946..c9987eb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ jobs: unit: strategy: matrix: - go: ['1.20','1.21'] + go: ['1.23'] os: [ubuntu-latest, macos-latest, windows-latest] fail-fast: true runs-on: ${{ matrix.os }} diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index a3eb74b..3471511 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -12,7 +12,7 @@ jobs: lint: strategy: matrix: - go: ['1.20','1.21'] + go: ['1.23'] fail-fast: true runs-on: ubuntu-latest steps: @@ -28,5 +28,5 @@ jobs: - name: Run GolangCI-Lint uses: golangci/golangci-lint-action@v3 with: - version: v1.53 + version: v1.60.1 args: --timeout=5m From bb4cd60c952a9ce48ea0dc6cc7b282ff79c38263 Mon Sep 17 00:00:00 2001 From: Daniel Holmes Date: Tue, 20 Aug 2024 14:07:20 +0000 Subject: [PATCH 12/12] chore: Update readme to relect go 1.23 release --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 540b63d..d2cbea6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Gorilla Sessions -> [!CAUTION] -> The main branch of this respository is in flux and being prepared for the [Go 1.23 release](https://github.com/golang/go/milestone/212). Please use released versions as per the release page. +> [!IMPORTANT] +> The latest version of this repository requires go 1.23 because of the new partitioned attribute. The last version that is compatible with older versions of go is v1.3.0. ![testing](https://github.com/gorilla/sessions/actions/workflows/test.yml/badge.svg) [![codecov](https://codecov.io/github/gorilla/sessions/branch/main/graph/badge.svg)](https://codecov.io/github/gorilla/sessions)