From 56c33e9da484d617e23f395ecec61db32d99dcf3 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Wed, 26 Jun 2019 21:06:25 -0700 Subject: [PATCH 01/34] Create config.yml (#195) * Create config.yml * Update config.yml * Delete .travis.yml --- .circleci/config.yml | 65 ++++++++++++++++++++++++++++++++++++++++++++ .travis.yml | 24 ---------------- 2 files changed, 65 insertions(+), 24 deletions(-) create mode 100644 .circleci/config.yml delete mode 100644 .travis.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..bd23335 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,65 @@ +version: 2.0 + +jobs: + # Base test configuration for Go library tests Each distinct version should + # inherit this base, and override (at least) the container image used. + "test": &test + docker: + - image: circleci/golang:latest + working_directory: /go/src/github.com/gorilla/sessions + steps: &steps + - checkout + - run: go version + - run: go get -t -v ./... + - run: diff -u <(echo -n) <(gofmt -d .) + - run: if [[ "$LATEST" = true ]]; then go vet -v .; fi + - run: go test -v -race ./... + + "latest": + <<: *test + environment: + LATEST: true + + + "1.12": + <<: *test + docker: + - image: circleci/golang:1.12 + + "1.11": + <<: *test + docker: + - image: circleci/golang:1.11 + + "1.10": + <<: *test + docker: + - image: circleci/golang:1.10 + + "1.9": + <<: *test + docker: + - image: circleci/golang:1.9 + + "1.8": + <<: *test + docker: + - image: circleci/golang:1.8 + + "1.7": + <<: *test + docker: + - image: circleci/golang:1.7 + + +workflows: + version: 2 + build: + jobs: + - "latest" + - "1.12" + - "1.11" + - "1.10" + - "1.9" + - "1.8" + - "1.7" diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 85c28cd..0000000 --- a/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -language: go -sudo: false - -matrix: - include: - - go: 1.7.x - - go: 1.8.x - - go: 1.9.x - - go: 1.10.x - - go: 1.11.x - - go: 1.x - env: LATEST=true - - go: tip - allow_failures: - - go: tip - -install: - - # skip - -script: - - go get -t -v ./... - - diff -u <(echo -n) <(gofmt -d .) - - if [[ "$LATEST" = true ]]; then go vet $(go list ./... | grep -v /vendor/); fi - - go test -v -race ./... From 4355a998706e83fe1d71c31b07af94e34f68d74a Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Tue, 9 Jul 2019 09:04:13 -0500 Subject: [PATCH 02/34] Update go.mod: removes gorilla/context (#196) --- go.mod | 5 +---- go.sum | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 go.sum diff --git a/go.mod b/go.mod index ea28ffe..9028bcf 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,3 @@ module github.com/gorilla/sessions -require ( - github.com/gorilla/context v1.1.1 - github.com/gorilla/securecookie v1.1.1 -) +require github.com/gorilla/securecookie v1.1.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e6a7ed5 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= From 9c6b24ee2dee928836dbf71f18bb0c7d0da4bf85 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 20 Jul 2019 22:21:36 +0200 Subject: [PATCH 03/34] Removed unused global var (#199) DefaultRemoteAddr seems to not be used (any longer?). --- sessions_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sessions_test.go b/sessions_test.go index a734f67..0b84fbc 100644 --- a/sessions_test.go +++ b/sessions_test.go @@ -20,10 +20,6 @@ func NewRecorder() *httptest.ResponseRecorder { } } -// DefaultRemoteAddr is the default remote address to return in RemoteAddr if -// an explicit DefaultRemoteAddr isn't set on ResponseRecorder. -const DefaultRemoteAddr = "1.2.3.4" - // ---------------------------------------------------------------------------- type FlashMessage struct { From daaabe7307937b2fde7a8fb84d689e6ec1aa7c09 Mon Sep 17 00:00:00 2001 From: Adam Jack Date: Tue, 17 Sep 2019 06:53:25 -0600 Subject: [PATCH 04/34] docs: added _ = to indicate there is a return from Save (#197) * Added _ = to indicate there is a return from Save This is because I copied the sample and failed to notice there was a return, which for me was failing, and lost a bunch of time to troubleshooting. * Added "err =" to session(s).Save() calls Indicated the need for error handling so developer who copy samples get the indication that error handling is required. ( I was learning/tinkering and copied a sample without error handling, failed to notice that Save returns an error in the documentation, and it took me a lot of time/troubleshooting to track down the problem.) * Highlighted possibility of Save() error. --- README.md | 6 +++++- doc.go | 19 ++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 98c993d..c730c05 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,11 @@ Let's start with an example that shows the sessions API in a nutshell: session.Values["foo"] = "bar" session.Values[42] = 43 // Save it before we write to the response/return from the handler. - session.Save(r, w) + err = session.Save(r, w) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } } ``` diff --git a/doc.go b/doc.go index 64f858c..f4673cc 100644 --- a/doc.go +++ b/doc.go @@ -30,6 +30,7 @@ Let's start with an example that shows the sessions API in a nutshell: // environmental variable, or flag (or both), and don't accidentally commit it // alongside your code. Ensure your key is sufficiently random - i.e. use Go's // crypto/rand or securecookie.GenerateRandomKey(32) and persist the result. + // Ensure SESSION_KEY exists in the environment, or sessions will fail. var store = sessions.NewCookieStore(os.Getenv("SESSION_KEY")) func MyHandler(w http.ResponseWriter, r *http.Request) { @@ -44,7 +45,11 @@ Let's start with an example that shows the sessions API in a nutshell: session.Values["foo"] = "bar" session.Values[42] = 43 // Save it before we write to the response/return from the handler. - session.Save(r, w) + err = session.Save(r, w) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } } First we initialize a session store calling NewCookieStore() and passing a @@ -82,7 +87,11 @@ flashes, call session.Flashes(). Here is an example: // Set a new flash. session.AddFlash("Hello, flash messages world!") } - session.Save(r, w) + err = session.Save(r, w) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } } Flash messages are useful to set information to be read after a redirection, @@ -185,7 +194,11 @@ at once: it's sessions.Save(). Here's an example: session2, _ := store.Get(r, "session-two") session2.Values[42] = 43 // Save all sessions. - sessions.Save(r, w) + err = sessions.Save(r, w) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } } This is possible because when we call Get() from a session store, it adds the From 36821f6da8fc2cb2d2b5c7a6ad481792be667489 Mon Sep 17 00:00:00 2001 From: Tyler Bui-Palsulich <26876514+tbpg@users.noreply.github.com> Date: Tue, 17 Sep 2019 08:54:14 -0400 Subject: [PATCH 05/34] docs: README.md: link Cloud Firestore implementation (#201) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c730c05..abc3809 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ Other implementations of the `sessions.Store` interface: - [github.com/gernest/qlstore](https://github.com/gernest/qlstore) - ql - [github.com/quasoft/memstore](https://github.com/quasoft/memstore) - In-memory implementation for use in unit tests - [github.com/lafriks/xormstore](https://github.com/lafriks/xormstore) - XORM (MySQL, PostgreSQL, SQLite, Microsoft SQL Server, TiDB) +- [github.com/GoogleCloudPlatform/firestore-gorilla-sessions](https://github.com/GoogleCloudPlatform/firestore-gorilla-sessions) - Cloud Firestore ## License From 400b592ab70b9f8ee876cf47bc6c794d255dd4aa Mon Sep 17 00:00:00 2001 From: Ruben Cervilla <47088081+rbcervilla@users.noreply.github.com> Date: Sun, 6 Oct 2019 17:13:01 +0200 Subject: [PATCH 06/34] docs: Add new Redis store to README (#202) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index abc3809..d4d70e9 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ Other implementations of the `sessions.Store` interface: - [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 +- [github.com/rbcervilla/redisstore](https://github.com/rbcervilla/redisstore) - Redis (Single, Sentinel, Cluster) - [github.com/boj/rethinkstore](https://github.com/boj/rethinkstore) - RethinkDB - [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak - [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite From 15ff3511704639ab26f7843f780c015f4bf49565 Mon Sep 17 00:00:00 2001 From: Waitire Colline Date: Sun, 5 Jan 2020 19:41:44 +0300 Subject: [PATCH 07/34] docs: fix CookieStore creation in doc.go (#206) --- doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc.go b/doc.go index f4673cc..946bf5c 100644 --- a/doc.go +++ b/doc.go @@ -31,7 +31,7 @@ Let's start with an example that shows the sessions API in a nutshell: // alongside your code. Ensure your key is sufficiently random - i.e. use Go's // crypto/rand or securecookie.GenerateRandomKey(32) and persist the result. // Ensure SESSION_KEY exists in the environment, or sessions will fail. - var store = sessions.NewCookieStore(os.Getenv("SESSION_KEY")) + var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY"))) func MyHandler(w http.ResponseWriter, r *http.Request) { // Get a session. Get() always returns a session, even if empty. From 947297c38923a85d18d497ad4cadfa23688ed3d4 Mon Sep 17 00:00:00 2001 From: Stephen Afam-Osemene Date: Mon, 13 Jul 2020 06:47:38 +0200 Subject: [PATCH 08/34] Add link to implementation for CockroachDB (#219) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d4d70e9..a787813 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ Other implementations of the `sessions.Store` interface: - [github.com/quasoft/memstore](https://github.com/quasoft/memstore) - In-memory implementation for use in unit tests - [github.com/lafriks/xormstore](https://github.com/lafriks/xormstore) - XORM (MySQL, PostgreSQL, SQLite, Microsoft SQL Server, TiDB) - [github.com/GoogleCloudPlatform/firestore-gorilla-sessions](https://github.com/GoogleCloudPlatform/firestore-gorilla-sessions) - Cloud Firestore +- [github.com/stephenafamo/crdbstore](https://github.com/stephenafamo/crdbstore) - CockroachDB ## License From 61fa50d034f99479a7de0d1c02c5e9dea5ad30cb Mon Sep 17 00:00:00 2001 From: James Cote <3276350+Coteh@users.noreply.github.com> Date: Wed, 19 Aug 2020 11:25:28 -0400 Subject: [PATCH 09/34] Fix typo in README example (#223) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a787813..a8fb98b 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Let's start with an example that shows the sessions API in a nutshell: session.Values["foo"] = "bar" session.Values[42] = 43 // Save it before we write to the response/return from the handler. - err = session.Save(r, w) + err := session.Save(r, w) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return From 9cb0b0a3e38d07f41143f03013eaf0a3243fb474 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Sat, 12 Sep 2020 12:25:02 -0700 Subject: [PATCH 10/34] build: use build matrix; drop Go <= 1.10 (#230) --- .circleci/config.yml | 119 ++++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 57 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index bd23335..1b545b7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,65 +1,70 @@ -version: 2.0 +version: 2.1 jobs: - # Base test configuration for Go library tests Each distinct version should - # inherit this base, and override (at least) the container image used. - "test": &test + "test": + parameters: + version: + type: string + default: "latest" + golint: + type: boolean + default: true + modules: + type: boolean + default: true + goproxy: + type: string + default: "" docker: - - image: circleci/golang:latest + - image: "circleci/golang:<< parameters.version >>" working_directory: /go/src/github.com/gorilla/sessions - steps: &steps - - checkout - - run: go version - - run: go get -t -v ./... - - run: diff -u <(echo -n) <(gofmt -d .) - - run: if [[ "$LATEST" = true ]]; then go vet -v .; fi - - run: go test -v -race ./... - - "latest": - <<: *test environment: - LATEST: true - - - "1.12": - <<: *test - docker: - - image: circleci/golang:1.12 - - "1.11": - <<: *test - docker: - - image: circleci/golang:1.11 - - "1.10": - <<: *test - docker: - - image: circleci/golang:1.10 - - "1.9": - <<: *test - docker: - - image: circleci/golang:1.9 - - "1.8": - <<: *test - docker: - - image: circleci/golang:1.8 - - "1.7": - <<: *test - docker: - - image: circleci/golang:1.7 - + GO111MODULE: "on" + GOPROXY: "<< parameters.goproxy >>" + steps: + - checkout + - run: + name: "Print the Go version" + command: > + go version + - run: + name: "Fetch dependencies" + command: > + if [[ << parameters.modules >> = true ]]; then + go mod download + export GO111MODULE=on + else + go get -v ./... + fi + # Only run gofmt, vet & lint against the latest Go version + - run: + name: "Run golint" + command: > + if [ << parameters.version >> = "latest" ] && [ << parameters.golint >> = true ]; then + go get -u golang.org/x/lint/golint + golint ./... + fi + - run: + name: "Run gofmt" + command: > + if [[ << parameters.version >> = "latest" ]]; then + diff -u <(echo -n) <(gofmt -d -e .) + fi + - run: + name: "Run go vet" + command: > + if [[ << parameters.version >> = "latest" ]]; then + go vet -v ./... + fi + - run: + name: "Run go test (+ race detector)" + command: > + go test -v -race ./... workflows: - version: 2 - build: + tests: jobs: - - "latest" - - "1.12" - - "1.11" - - "1.10" - - "1.9" - - "1.8" - - "1.7" + - test: + matrix: + parameters: + version: ["latest", "1.15", "1.14", "1.13", "1.12", "1.11"] From 0a84f353f00601bbc2fcf99e8da368ef368b4e51 Mon Sep 17 00:00:00 2001 From: Leung Yau Ming Date: Sat, 31 Jul 2021 21:13:50 +0800 Subject: [PATCH 11/34] refactor: use base32 encoder with no padding (#240) --- store.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/store.go b/store.go index bb7f964..eb45afb 100644 --- a/store.go +++ b/store.go @@ -10,7 +10,6 @@ import ( "net/http" "os" "path/filepath" - "strings" "sync" "github.com/gorilla/securecookie" @@ -201,6 +200,8 @@ func (s *FilesystemStore) New(r *http.Request, name string) (*Session, error) { return session, err } +var base32RawStdEncoding = base32.StdEncoding.WithPadding(base32.NoPadding) + // Save adds a single session to the response. // // If the Options.MaxAge of the session is <= 0 then the session file will be @@ -221,9 +222,8 @@ func (s *FilesystemStore) Save(r *http.Request, w http.ResponseWriter, if session.ID == "" { // Because the ID is used in the filename, encode it to // use alphanumeric characters only. - session.ID = strings.TrimRight( - base32.StdEncoding.EncodeToString( - securecookie.GenerateRandomKey(32)), "=") + session.ID = base32RawStdEncoding.EncodeToString( + securecookie.GenerateRandomKey(32)) } if err := s.save(session); err != nil { return err From d81e0696f6afcb11015b5f57b277994f501ec05f Mon Sep 17 00:00:00 2001 From: Ryuichiroh Ikeuchi <37844673+ryicoh@users.noreply.github.com> Date: Mon, 10 Jan 2022 00:14:04 +0900 Subject: [PATCH 12/34] docs: Add new TiKV store to README (#245) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a8fb98b..38c9f27 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,7 @@ Other implementations of the `sessions.Store` interface: - [github.com/lafriks/xormstore](https://github.com/lafriks/xormstore) - XORM (MySQL, PostgreSQL, SQLite, Microsoft SQL Server, TiDB) - [github.com/GoogleCloudPlatform/firestore-gorilla-sessions](https://github.com/GoogleCloudPlatform/firestore-gorilla-sessions) - Cloud Firestore - [github.com/stephenafamo/crdbstore](https://github.com/stephenafamo/crdbstore) - CockroachDB +- [github.com/ryicoh/tikvstore](github.com/ryicoh/tikvstore) - TiKV ## License From a6a8e49c83a2dba9cf7f486feb2662d6439851f2 Mon Sep 17 00:00:00 2001 From: Marius Orcsik Date: Mon, 7 Feb 2022 12:04:23 +0100 Subject: [PATCH 13/34] Fix linting errors for go1.17 (#253) --- cookie.go | 1 + cookie_go111.go | 1 + cookie_go111_test.go | 1 + options.go | 1 + options_go111.go | 1 + 5 files changed, 5 insertions(+) diff --git a/cookie.go b/cookie.go index 1928b04..6612662 100644 --- a/cookie.go +++ b/cookie.go @@ -1,3 +1,4 @@ +//go:build !go1.11 // +build !go1.11 package sessions diff --git a/cookie_go111.go b/cookie_go111.go index 173d1a3..9b58828 100644 --- a/cookie_go111.go +++ b/cookie_go111.go @@ -1,3 +1,4 @@ +//go:build go1.11 // +build go1.11 package sessions diff --git a/cookie_go111_test.go b/cookie_go111_test.go index 1aac273..e6137c7 100644 --- a/cookie_go111_test.go +++ b/cookie_go111_test.go @@ -1,3 +1,4 @@ +//go:build go1.11 // +build go1.11 package sessions diff --git a/options.go b/options.go index 38ba72f..d33d076 100644 --- a/options.go +++ b/options.go @@ -1,3 +1,4 @@ +//go:build !go1.11 // +build !go1.11 package sessions diff --git a/options_go111.go b/options_go111.go index 388112a..af9cdf0 100644 --- a/options_go111.go +++ b/options_go111.go @@ -1,3 +1,4 @@ +//go:build go1.11 // +build go1.11 package sessions From 0e1d1d7c382124033b710ef1ef0993327195ed40 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Fri, 9 Dec 2022 10:57:31 -0500 Subject: [PATCH 14/34] archive mode --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 38c9f27..b2b95ff 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,12 @@ [![GoDoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) [![Build Status](https://travis-ci.org/gorilla/sessions.svg?branch=master)](https://travis-ci.org/gorilla/sessions) [![Sourcegraph](https://sourcegraph.com/github.com/gorilla/sessions/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/sessions?badge) +--- + +**The Gorilla project has been archived, and is no longer under active maintainenance. You can read more here: https://github.com/gorilla#gorilla-toolkit** + +--- + gorilla/sessions provides cookie and filesystem sessions and infrastructure for custom session backends. From 7aa6ccf8b68628c0bf0404fc259d1a56340cb6ce Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Sat, 15 Jul 2023 10:54:36 -0400 Subject: [PATCH 15/34] Update README.md Signed-off-by: Corey Daley --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index b2b95ff..c44bdd4 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,6 @@ [![GoDoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) [![Build Status](https://travis-ci.org/gorilla/sessions.svg?branch=master)](https://travis-ci.org/gorilla/sessions) [![Sourcegraph](https://sourcegraph.com/github.com/gorilla/sessions/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/sessions?badge) ---- - -**The Gorilla project has been archived, and is no longer under active maintainenance. You can read more here: https://github.com/gorilla#gorilla-toolkit** - ---- gorilla/sessions provides cookie and filesystem sessions and infrastructure for custom session backends. From b1daf6dac7416f09174c1a6778ce4ce6bbdf6564 Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Sun, 23 Jul 2023 16:32:59 -0400 Subject: [PATCH 16/34] Update go version, add tools for verification and testing (#263) Fixes # **Summary of Changes** 1. 2. 3. > PS: Make sure your PR includes/updates tests! If you need help with this part, just ask! --- .circleci/config.yml | 70 ------------------------------------ .editorconfig | 20 +++++++++++ .github/release-drafter.yml | 8 ----- .github/stale.yml | 12 ------- .github/workflows/issues.yml | 20 +++++++++++ .github/workflows/test.yml | 55 ++++++++++++++++++++++++++++ .gitignore | 1 + AUTHORS | 43 ---------------------- Makefile | 34 ++++++++++++++++++ README.md | 6 ++-- go.mod | 2 ++ sessions_test.go | 2 -- store.go | 5 ++- 13 files changed, 138 insertions(+), 140 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .editorconfig delete mode 100644 .github/release-drafter.yml delete mode 100644 .github/stale.yml create mode 100644 .github/workflows/issues.yml create mode 100644 .github/workflows/test.yml create mode 100644 .gitignore delete mode 100644 AUTHORS create mode 100644 Makefile diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 1b545b7..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,70 +0,0 @@ -version: 2.1 - -jobs: - "test": - parameters: - version: - type: string - default: "latest" - golint: - type: boolean - default: true - modules: - type: boolean - default: true - goproxy: - type: string - default: "" - docker: - - image: "circleci/golang:<< parameters.version >>" - working_directory: /go/src/github.com/gorilla/sessions - environment: - GO111MODULE: "on" - GOPROXY: "<< parameters.goproxy >>" - steps: - - checkout - - run: - name: "Print the Go version" - command: > - go version - - run: - name: "Fetch dependencies" - command: > - if [[ << parameters.modules >> = true ]]; then - go mod download - export GO111MODULE=on - else - go get -v ./... - fi - # Only run gofmt, vet & lint against the latest Go version - - run: - name: "Run golint" - command: > - if [ << parameters.version >> = "latest" ] && [ << parameters.golint >> = true ]; then - go get -u golang.org/x/lint/golint - golint ./... - fi - - run: - name: "Run gofmt" - command: > - if [[ << parameters.version >> = "latest" ]]; then - diff -u <(echo -n) <(gofmt -d -e .) - fi - - run: - name: "Run go vet" - command: > - if [[ << parameters.version >> = "latest" ]]; then - go vet -v ./... - fi - - run: - name: "Run go test (+ race detector)" - command: > - go test -v -race ./... - -workflows: - tests: - jobs: - - test: - matrix: - parameters: - version: ["latest", "1.15", "1.14", "1.13", "1.12", "1.11"] diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2940ec9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +; 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/.github/release-drafter.yml b/.github/release-drafter.yml deleted file mode 100644 index 12440bf..0000000 --- a/.github/release-drafter.yml +++ /dev/null @@ -1,8 +0,0 @@ -# Config for https://github.com/apps/release-drafter -template: | - - - - ### CHANGELOG - - $CHANGES diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index de8a678..0000000 --- a/.github/stale.yml +++ /dev/null @@ -1,12 +0,0 @@ -daysUntilStale: 60 -daysUntilClose: 7 -# Issues with these labels will never be considered stale -exemptLabels: - - v2 - - needs-review - - work-required -staleLabel: stale -markComment: > - This issue has been automatically marked as stale because it hasn't seen - a recent update. It'll be automatically closed in a few days. -closeComment: false diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml new file mode 100644 index 0000000..5f56d21 --- /dev/null +++ b/.github/workflows/issues.yml @@ -0,0 +1,20 @@ +# Add all the issues created to the project. +name: Add issue or pull request to Project + +on: + issues: + types: + - opened + pull_request: + types: + - opened + +jobs: + add-to-project: + runs-on: ubuntu-latest + steps: + - name: Add issue to project + uses: actions/add-to-project@v0.5.0 + with: + project-url: https://github.com/orgs/gorilla/projects/4 + github-token: ${{ secrets.ADD_TO_PROJECT_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..f2e4b4d --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,55 @@ +name: CI +on: + push: + branches: + - main + pull_request: + branches: + - main + +permissions: + contents: read + +jobs: + verify-and-test: + strategy: + matrix: + go: ['1.19','1.20'] + os: [ubuntu-latest, macos-latest, windows-latest] + fail-fast: true + runs-on: ${{ matrix.os }} + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Setup Go ${{ matrix.go }} + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go }} + cache: false + + - name: Run GolangCI-Lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.53 + args: --timeout=5m + + - name: Run GoSec + if: matrix.os == 'ubuntu-latest' + uses: securego/gosec@master + with: + args: ./... + + - name: Run GoVulnCheck + uses: golang/govulncheck-action@v1 + with: + go-version-input: ${{ matrix.go }} + go-package: ./... + + - name: Run Tests + run: go test -race -cover -coverprofile=coverage -covermode=atomic -v ./... + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + files: ./coverage diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..84039fe --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +coverage.coverprofile diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 1e3e7ac..0000000 --- a/AUTHORS +++ /dev/null @@ -1,43 +0,0 @@ -# This is the official list of gorilla/sessions authors for copyright purposes. -# -# Please keep the list sorted. - -Ahmadreza Zibaei -Anton LindstrΓΆm -Brian Jones -Collin Stedman -Deniz Eren -Dmitry Chestnykh -Dustin Oprea -Egon Elbre -enumappstore -Geofrey Ernest -Google LLC (https://opensource.google.com/) -Jerry Saravia -Jonathan Gillham -Justin Clift -Justin Hellings -Kamil Kisiel -Keiji Yoshida -kliron -Kshitij Saraogi -Lauris BH -Lukas Rist -Mark Dain -Matt Ho -Matt Silverlock -Mattias Wadman -Michael Schuett -Michael Stapelberg -Mirco Zeiss -moraes -nvcnvn -pappz -Pontus Leitzler -QuaSoft -rcadena -rodrigo moraes -Shawn Smith -Taylor Hurt -Tortuoise -Vitor De Mario diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ac37ffd --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +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 ./... diff --git a/README.md b/README.md index c44bdd4..1fb27b3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # sessions -[![GoDoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) [![Build Status](https://travis-ci.org/gorilla/sessions.svg?branch=master)](https://travis-ci.org/gorilla/sessions) -[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/sessions/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/sessions?badge) +![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) +[![godoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) +[![sourcegraph](https://sourcegraph.com/github.com/gorilla/sessions/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/sessions?badge) gorilla/sessions provides cookie and filesystem sessions and infrastructure for diff --git a/go.mod b/go.mod index 9028bcf..9032b13 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module github.com/gorilla/sessions +go 1.19 + require github.com/gorilla/securecookie v1.1.1 diff --git a/sessions_test.go b/sessions_test.go index 0b84fbc..ddba006 100644 --- a/sessions_test.go +++ b/sessions_test.go @@ -75,7 +75,6 @@ func TestFlashes(t *testing.T) { req, _ = http.NewRequest("GET", "http://localhost:8080/", nil) req.Header.Add("Cookie", cookies[0]) - rsp = NewRecorder() // Get a session. if session, err = store.Get(req, "session-key"); err != nil { t.Fatalf("Error getting session: %v", err) @@ -135,7 +134,6 @@ func TestFlashes(t *testing.T) { req, _ = http.NewRequest("GET", "http://localhost:8080/", nil) req.Header.Add("Cookie", cookies[0]) - rsp = NewRecorder() // Get a session. if session, err = store.Get(req, "session-key"); err != nil { t.Fatalf("Error getting session: %v", err) diff --git a/store.go b/store.go index eb45afb..7b6c5ec 100644 --- a/store.go +++ b/store.go @@ -6,7 +6,6 @@ package sessions import ( "encoding/base32" - "io/ioutil" "net/http" "os" "path/filepath" @@ -261,7 +260,7 @@ func (s *FilesystemStore) save(session *Session) error { filename := filepath.Join(s.path, "session_"+session.ID) fileMutex.Lock() defer fileMutex.Unlock() - return ioutil.WriteFile(filename, []byte(encoded), 0600) + return os.WriteFile(filename, []byte(encoded), 0600) } // load reads a file and decodes its content into session.Values. @@ -269,7 +268,7 @@ func (s *FilesystemStore) load(session *Session) error { filename := filepath.Join(s.path, "session_"+session.ID) fileMutex.RLock() defer fileMutex.RUnlock() - fdata, err := ioutil.ReadFile(filename) + fdata, err := os.ReadFile(filepath.Clean(filename)) if err != nil { return err } From 070f6e732a4441f5301e1d44af1f73e68cab994d Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Tue, 25 Jul 2023 02:02:11 -0400 Subject: [PATCH 17/34] Update LICENSE (#264) 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 - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] 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 Signed-off-by: Corey Daley --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 6903df6..bb9d80b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved. +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 From 1a1e42e73b074121224de0e386b5291a97622a61 Mon Sep 17 00:00:00 2001 From: Apoorva Jagtap <35304110+apoorvajagtap@users.noreply.github.com> Date: Wed, 26 Jul 2023 13:12:22 +0530 Subject: [PATCH 18/34] Add gorilla logo to Readme (#265) 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 - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] 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 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1fb27b3..06119bb 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![godoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) [![sourcegraph](https://sourcegraph.com/github.com/gorilla/sessions/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/sessions?badge) +![Gorilla Logo](https://github.com/gorilla/.github/assets/53367916/d92caabf-98e0-473e-bfbf-ab554ba435e5) gorilla/sessions provides cookie and filesystem sessions and infrastructure for custom session backends. From 69327c514c1f898f7bec36af5ffe9f2f18f7c5ae Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Mon, 31 Jul 2023 03:43:45 -0400 Subject: [PATCH 19/34] Update issues.yml (#266) 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 - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] 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 Signed-off-by: Corey Daley --- .github/workflows/issues.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml index 5f56d21..8be6ced 100644 --- a/.github/workflows/issues.yml +++ b/.github/workflows/issues.yml @@ -1,13 +1,14 @@ -# Add all the issues created to the project. +# Add issues or pull-requests created to the project. name: Add issue or pull request to Project on: issues: types: - opened - pull_request: + pull_request_target: types: - opened + - reopened jobs: add-to-project: From dd83328c14c8fdd3ce533b409bc760166468ddea Mon Sep 17 00:00:00 2001 From: Marius Orcsik Date: Thu, 17 Aug 2023 19:05:40 +0200 Subject: [PATCH 20/34] =?UTF-8?q?Don't=20propagate=20"not=20exist"=20error?= =?UTF-8?q?=20if=20trying=20to=20erase=20a=20session=20matchi=E2=80=A6=20(?= =?UTF-8?q?#252)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary of Changes Don't consider "oserror.ErrNotExist" as a failure when trying to erase a session which corresponds to a missing file. Hello, in the current implementation, if a request contains a session token that has been stored in a file that has been deleted and we're trying to erase it using the Options.MaxAge = -1 workaround, the action still fails, because the missing file error gets propagated higher in the stack. This small fix prevents this, and ensures that the session is regenerated. ___ This is a reopen of #237 which was closed by the stale bot. Co-authored-by: Corey Daley --- store.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/store.go b/store.go index 7b6c5ec..aea37e4 100644 --- a/store.go +++ b/store.go @@ -211,7 +211,7 @@ func (s *FilesystemStore) Save(r *http.Request, w http.ResponseWriter, session *Session) error { // Delete if max-age is <= 0 if session.Options.MaxAge <= 0 { - if err := s.erase(session); err != nil { + if err := s.erase(session); err != nil && !os.IsNotExist(err) { return err } http.SetCookie(w, NewCookie(session.Name(), "", session.Options)) From 26d95ec758d6625a7e738960a9a0653a7fc71b66 Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Wed, 18 Oct 2023 07:16:00 -0400 Subject: [PATCH 21/34] update GitHub workflows (#268) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [x] Refactor - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update - [ ] Go Version Update - [ ] Dependency Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] 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 - [x] `make verify` is passing - [x] `make test` is passing --- .github/workflows/issues.yml | 2 +- .github/workflows/security.yml | 37 ++++++++++++++++++++++++++++++++++ .github/workflows/test.yml | 26 +++--------------------- .github/workflows/verify.yml | 32 +++++++++++++++++++++++++++++ go.mod | 2 +- 5 files changed, 74 insertions(+), 25 deletions(-) create mode 100644 .github/workflows/security.yml create mode 100644 .github/workflows/verify.yml diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml index 8be6ced..768b05b 100644 --- a/.github/workflows/issues.yml +++ b/.github/workflows/issues.yml @@ -1,4 +1,4 @@ -# Add issues or pull-requests created to the project. +# Add all the issues created to the project. name: Add issue or pull request to Project on: diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..ff4a613 --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,37 @@ +name: Security +on: + push: + branches: + - main + pull_request: + branches: + - main +permissions: + contents: read +jobs: + scan: + strategy: + matrix: + go: ['1.20','1.21'] + fail-fast: true + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Setup Go ${{ matrix.go }} + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go }} + cache: false + + - name: Run GoSec + uses: securego/gosec@master + with: + args: -exclude-dir examples ./... + + - name: Run GoVulnCheck + uses: golang/govulncheck-action@v1 + with: + go-version-input: ${{ matrix.go }} + go-package: ./... diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f2e4b4d..50a3946 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: CI +name: Test on: push: branches: @@ -6,15 +6,13 @@ on: pull_request: branches: - main - permissions: contents: read - jobs: - verify-and-test: + unit: strategy: matrix: - go: ['1.19','1.20'] + go: ['1.20','1.21'] os: [ubuntu-latest, macos-latest, windows-latest] fail-fast: true runs-on: ${{ matrix.os }} @@ -28,24 +26,6 @@ jobs: go-version: ${{ matrix.go }} cache: false - - name: Run GolangCI-Lint - uses: golangci/golangci-lint-action@v3 - with: - version: v1.53 - args: --timeout=5m - - - name: Run GoSec - if: matrix.os == 'ubuntu-latest' - uses: securego/gosec@master - with: - args: ./... - - - name: Run GoVulnCheck - uses: golang/govulncheck-action@v1 - with: - go-version-input: ${{ matrix.go }} - go-package: ./... - - name: Run Tests run: go test -race -cover -coverprofile=coverage -covermode=atomic -v ./... diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml new file mode 100644 index 0000000..a3eb74b --- /dev/null +++ b/.github/workflows/verify.yml @@ -0,0 +1,32 @@ +name: Verify +on: + push: + branches: + - main + pull_request: + branches: + - main +permissions: + contents: read +jobs: + lint: + strategy: + matrix: + go: ['1.20','1.21'] + fail-fast: true + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Setup Go ${{ matrix.go }} + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go }} + cache: false + + - name: Run GolangCI-Lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.53 + args: --timeout=5m diff --git a/go.mod b/go.mod index 9032b13..9ba2c6f 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module github.com/gorilla/sessions -go 1.19 +go 1.20 require github.com/gorilla/securecookie v1.1.1 From 3eed1c4ffcde6f23b6f88068c63c1ef6190df331 Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Sat, 4 Nov 2023 22:29:28 -0400 Subject: [PATCH 22/34] bump deps and add vendor dir (#269) 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 - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update - [ ] Go Version Update - [x] Dependency Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## 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 - [x] `make verify` is passing - [x] `make test` is passing --- go.mod | 2 +- go.sum | 5 +- .../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 | 144 ++++ vendor/github.com/gorilla/securecookie/doc.go | 61 ++ .../gorilla/securecookie/securecookie.go | 649 ++++++++++++++++++ vendor/modules.txt | 3 + 10 files changed, 948 insertions(+), 3 deletions(-) create mode 100644 vendor/github.com/gorilla/securecookie/.editorconfig create mode 100644 vendor/github.com/gorilla/securecookie/.gitignore create mode 100644 vendor/github.com/gorilla/securecookie/LICENSE create mode 100644 vendor/github.com/gorilla/securecookie/Makefile create mode 100644 vendor/github.com/gorilla/securecookie/README.md create mode 100644 vendor/github.com/gorilla/securecookie/doc.go create mode 100644 vendor/github.com/gorilla/securecookie/securecookie.go create mode 100644 vendor/modules.txt diff --git a/go.mod b/go.mod index 9ba2c6f..64cc6a3 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module github.com/gorilla/sessions go 1.20 -require github.com/gorilla/securecookie v1.1.1 +require github.com/gorilla/securecookie v1.1.2 diff --git a/go.sum b/go.sum index e6a7ed5..285ffee 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,3 @@ -github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= +github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= diff --git a/vendor/github.com/gorilla/securecookie/.editorconfig b/vendor/github.com/gorilla/securecookie/.editorconfig new file mode 100644 index 0000000..2940ec9 --- /dev/null +++ b/vendor/github.com/gorilla/securecookie/.editorconfig @@ -0,0 +1,20 @@ +; 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 new file mode 100644 index 0000000..84039fe --- /dev/null +++ b/vendor/github.com/gorilla/securecookie/.gitignore @@ -0,0 +1 @@ +coverage.coverprofile diff --git a/vendor/github.com/gorilla/securecookie/LICENSE b/vendor/github.com/gorilla/securecookie/LICENSE new file mode 100644 index 0000000..bb9d80b --- /dev/null +++ b/vendor/github.com/gorilla/securecookie/LICENSE @@ -0,0 +1,27 @@ +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 new file mode 100644 index 0000000..2b9008a --- /dev/null +++ b/vendor/github.com/gorilla/securecookie/Makefile @@ -0,0 +1,39 @@ +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 new file mode 100644 index 0000000..c3b9815 --- /dev/null +++ b/vendor/github.com/gorilla/securecookie/README.md @@ -0,0 +1,144 @@ +# 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, + } + 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 new file mode 100644 index 0000000..ae89408 --- /dev/null +++ b/vendor/github.com/gorilla/securecookie/doc.go @@ -0,0 +1,61 @@ +// 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 new file mode 100644 index 0000000..4d5ea86 --- /dev/null +++ b/vendor/github.com/gorilla/securecookie/securecookie.go @@ -0,0 +1,649 @@ +// 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 new file mode 100644 index 0000000..6224b61 --- /dev/null +++ b/vendor/modules.txt @@ -0,0 +1,3 @@ +# github.com/gorilla/securecookie v1.1.2 +## explicit; go 1.20 +github.com/gorilla/securecookie 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 23/34] 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 24/34] #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 25/34] 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 26/34] 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 27/34] 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 28/34] 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 29/34] 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 30/34] 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 31/34] 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 32/34] 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 33/34] 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 34/34] 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)