From a5ba7cadf7b6ebc470f11bbbd82ae17c9482b5fe Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Wed, 13 Nov 2024 22:55:56 +0100 Subject: [PATCH 01/49] chore(release-notes): update the v9.0.2 & v7.0.11 links --- RELEASE-NOTES.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 65209ee54a..431710389d 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -6,6 +6,10 @@ A [patch or minor release](https://semver.org/spec/v2.0.0.html) (e.g. upgrading The release notes of each release [are available in the corresponding milestone](https://codeberg.org/forgejo/forgejo/milestones), starting with [Forgejo 7.0.7](https://codeberg.org/forgejo/forgejo/milestone/7683) and [Forgejo 8.0.1](https://codeberg.org/forgejo/forgejo/milestone/7682). +## 9.0.2 + +The Forgejo v9.0.2 release notes are [available in the v9.0.2 milestone](https://codeberg.org/forgejo/forgejo/milestone/8610. + ## 9.0.1 The Forgejo v9.0.1 release notes are [available in the v9.0.1 milestone](https://codeberg.org/forgejo/forgejo/milestone/8544). @@ -163,6 +167,10 @@ A [companion blog post](https://forgejo.org/2024-07-release-v8-0/) provides addi - [PR](https://codeberg.org/forgejo/forgejo/pulls/2937): 31 March updates +## 7.0.11 + +The Forgejo v7.0.11 release notes are [available in the v7.0.11 milestone](https://codeberg.org/forgejo/forgejo/milestone/8609). + ## 7.0.10 The Forgejo v7.0.10 release notes are [available in the v7.0.10 milestone](https://codeberg.org/forgejo/forgejo/milestone/8286). From 8206d509fcdfa5a2f42055b3c7a972487221780d Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 14 Nov 2024 02:03:09 +0000 Subject: [PATCH 02/49] Update module code.forgejo.org/forgejo/act to v1.22.0 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 62900036a6..2b8a6cb88a 100644 --- a/go.mod +++ b/go.mod @@ -293,7 +293,7 @@ replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 -replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.21.5 +replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.22.0 replace github.com/mholt/archiver/v3 => code.forgejo.org/forgejo/archiver/v3 v3.5.1 diff --git a/go.sum b/go.sum index 993f797164..40a351dbe6 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ code.forgejo.org/f3/gof3/v3 v3.7.0 h1:ZfuCP8CGm8ZJbWmL+V0pUu3E0X4FCAA7GfRDy/y5/K code.forgejo.org/f3/gof3/v3 v3.7.0/go.mod h1:oNhOeqD4DZYjVcNjQXIOdDX9b/1tqxi9ITLS8H9/Csw= code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 h1:HTZl3CBk3ABNYtFI6TPLvJgGKFIhKT5CBk0sbOtkDKU= code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:PphB88CPbx601QrWPMZATeorACeVmQlyv3u+uUMbSaM= -code.forgejo.org/forgejo/act v1.21.5 h1:rWI+bhClocogdNwjRrM836rZYY7JBcHY3VUAwkYqEtw= -code.forgejo.org/forgejo/act v1.21.5/go.mod h1:+PcvJ9iv+NTFeJSh79ra9Jbk9l0vvyA9D9me5/dbxYM= +code.forgejo.org/forgejo/act v1.22.0 h1:NbUf0+vQ48+ddwe4zVkINqnxKYl/to+NUvW7iisPA60= +code.forgejo.org/forgejo/act v1.22.0/go.mod h1:+PcvJ9iv+NTFeJSh79ra9Jbk9l0vvyA9D9me5/dbxYM= code.forgejo.org/forgejo/archiver/v3 v3.5.1 h1:UmmbA7D5550uf71SQjarmrn6yKwOGxtEjb3jaYYtmSE= code.forgejo.org/forgejo/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= code.forgejo.org/forgejo/reply v1.0.2 h1:dMhQCHV6/O3L5CLWNTol+dNzDAuyCK88z4J/lCdgFuQ= From d1520cf08da06b10e4544678303d2926d41eef2a Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 14 Nov 2024 02:13:22 +0100 Subject: [PATCH 03/49] chore: improve preparing tests - Only prepare repositories once. - Move the repositories to temporary directories (these should usually be stored in memory) which are recreated for each test to avoid persistentance between tests. Doing some dirty profiling suggests that the preparing test functions from 140-100ms to 70-40ms --- tests/integration/api_wiki_test.go | 45 ++++++------- tests/integration/easymde_test.go | 3 + tests/integration/git_clone_wiki_test.go | 3 - .../git_helper_for_declarative_test.go | 9 +++ tests/test_utils.go | 66 ++++++++++++------- 5 files changed, 79 insertions(+), 47 deletions(-) diff --git a/tests/integration/api_wiki_test.go b/tests/integration/api_wiki_test.go index e5eb7a52c1..b930791969 100644 --- a/tests/integration/api_wiki_test.go +++ b/tests/integration/api_wiki_test.go @@ -9,6 +9,7 @@ import ( "encoding/base64" "fmt" "net/http" + "net/url" "testing" auth_model "code.gitea.io/gitea/models/auth" @@ -206,11 +207,11 @@ func TestAPIListWikiPages(t *testing.T) { } func TestAPINewWikiPage(t *testing.T) { + defer tests.PrepareTestEnv(t)() for _, title := range []string{ "New page", "&&&&", } { - defer tests.PrepareTestEnv(t)() username := "user2" session := loginUser(t, username) token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) @@ -386,26 +387,26 @@ func TestAPIListPageRevisions(t *testing.T) { } func TestAPIWikiNonMasterBranch(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - repo, _, f := tests.CreateDeclarativeRepoWithOptions(t, user, tests.DeclarativeRepoOptions{ - WikiBranch: optional.Some("main"), - }) - defer f() - - uris := []string{ - "revisions/Home", - "pages", - "page/Home", - } - baseURL := fmt.Sprintf("/api/v1/repos/%s/wiki", repo.FullName()) - for _, uri := range uris { - t.Run(uri, func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - req := NewRequestf(t, "GET", "%s/%s", baseURL, uri) - MakeRequest(t, req, http.StatusOK) + onGiteaRun(t, func(t *testing.T, _ *url.URL) { + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + repo, _, f := tests.CreateDeclarativeRepoWithOptions(t, user, tests.DeclarativeRepoOptions{ + WikiBranch: optional.Some("main"), }) - } + defer f() + + uris := []string{ + "revisions/Home", + "pages", + "page/Home", + } + baseURL := fmt.Sprintf("/api/v1/repos/%s/wiki", repo.FullName()) + for _, uri := range uris { + t.Run(uri, func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequestf(t, "GET", "%s/%s", baseURL, uri) + MakeRequest(t, req, http.StatusOK) + }) + } + }) } diff --git a/tests/integration/easymde_test.go b/tests/integration/easymde_test.go index 97f61c8449..2b4630d8b4 100644 --- a/tests/integration/easymde_test.go +++ b/tests/integration/easymde_test.go @@ -6,9 +6,12 @@ package integration import ( "net/http" "testing" + + "code.gitea.io/gitea/tests" ) func TestEasyMDESwitch(t *testing.T) { + defer tests.PrepareTestEnv(t)() session := loginUser(t, "user2") testEasyMDESwitch(t, session, "user2/glob/issues/1", false) testEasyMDESwitch(t, session, "user2/glob/issues/new", false) diff --git a/tests/integration/git_clone_wiki_test.go b/tests/integration/git_clone_wiki_test.go index ec99374c81..df260258de 100644 --- a/tests/integration/git_clone_wiki_test.go +++ b/tests/integration/git_clone_wiki_test.go @@ -13,7 +13,6 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -33,8 +32,6 @@ func assertFileEqual(t *testing.T, p string, content []byte) { func TestRepoCloneWiki(t *testing.T) { onGiteaRun(t, func(t *testing.T, u *url.URL) { - defer tests.PrepareTestEnv(t)() - dstPath := t.TempDir() r := fmt.Sprintf("%suser2/repo1.wiki.git", u.String()) diff --git a/tests/integration/git_helper_for_declarative_test.go b/tests/integration/git_helper_for_declarative_test.go index 490d4caae0..8bcab1b150 100644 --- a/tests/integration/git_helper_for_declarative_test.go +++ b/tests/integration/git_helper_for_declarative_test.go @@ -12,6 +12,7 @@ import ( "os" "path" "path/filepath" + "regexp" "strconv" "testing" "time" @@ -58,6 +59,8 @@ func createSSHUrl(gitPath string, u *url.URL) *url.URL { return &u2 } +var rootPathRe = regexp.MustCompile("\\[repository\\]\nROOT\\s=\\s.*") + func onGiteaRun[T testing.TB](t T, callback func(T, *url.URL)) { defer tests.PrepareTestEnv(t, 1)() s := http.Server{ @@ -76,7 +79,13 @@ func onGiteaRun[T testing.TB](t T, callback func(T, *url.URL)) { require.NoError(t, err) u.Host = listener.Addr().String() + // Override repository root in config. + conf, err := os.ReadFile(setting.CustomConf) + require.NoError(t, err) + require.NoError(t, os.WriteFile(setting.CustomConf, rootPathRe.ReplaceAll(conf, []byte("[repository]\nROOT = "+setting.RepoRootPath)), os.ModePerm)) + defer func() { + require.NoError(t, os.WriteFile(setting.CustomConf, conf, os.ModePerm)) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) s.Shutdown(ctx) cancel() diff --git a/tests/test_utils.go b/tests/test_utils.go index d6516dd99a..e83f19700a 100644 --- a/tests/test_utils.go +++ b/tests/test_utils.go @@ -48,6 +48,8 @@ func exitf(format string, args ...any) { os.Exit(1) } +var preparedDir string + func InitTest(requireGitea bool) { log.RegisterEventWriter("test", testlogger.NewTestLoggerWriter) @@ -177,6 +179,44 @@ func InitTest(requireGitea bool) { } } + setting.Repository.Local.LocalCopyPath = os.TempDir() + dir, err := os.MkdirTemp("", "prepared-forgejo") + if err != nil { + log.Fatal("os.MkdirTemp: %v", err) + } + preparedDir = dir + + setting.Repository.Local.LocalCopyPath, err = os.MkdirTemp("", "local-upload") + if err != nil { + log.Fatal("os.MkdirTemp: %v", err) + } + + if err := unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), dir); err != nil { + log.Fatal("os.RemoveAll: %v", err) + } + ownerDirs, err := os.ReadDir(dir) + if err != nil { + log.Fatal("os.ReadDir: %v", err) + } + fmt.Println(ownerDirs) + + for _, ownerDir := range ownerDirs { + if !ownerDir.Type().IsDir() { + continue + } + repoDirs, err := os.ReadDir(filepath.Join(dir, ownerDir.Name())) + if err != nil { + log.Fatal("os.ReadDir: %v", err) + } + for _, repoDir := range repoDirs { + _ = os.MkdirAll(filepath.Join(dir, ownerDir.Name(), repoDir.Name(), "objects", "pack"), 0o755) + _ = os.MkdirAll(filepath.Join(dir, ownerDir.Name(), repoDir.Name(), "objects", "info"), 0o755) + _ = os.MkdirAll(filepath.Join(dir, ownerDir.Name(), repoDir.Name(), "refs", "heads"), 0o755) + _ = os.MkdirAll(filepath.Join(dir, ownerDir.Name(), repoDir.Name(), "refs", "tag"), 0o755) + _ = os.MkdirAll(filepath.Join(dir, ownerDir.Name(), repoDir.Name(), "refs", "pull"), 0o755) + } + } + routers.InitWebInstalled(graceful.GetManager().HammerContext()) } @@ -225,28 +265,10 @@ func cancelProcesses(t testing.TB, delay time.Duration) { } func PrepareGitRepoDirectory(t testing.TB) { - require.NoError(t, util.RemoveAll(setting.RepoRootPath)) - require.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath)) - ownerDirs, err := os.ReadDir(setting.RepoRootPath) - if err != nil { - require.NoError(t, err, "unable to read the new repo root: %v\n", err) - } - for _, ownerDir := range ownerDirs { - if !ownerDir.Type().IsDir() { - continue - } - repoDirs, err := os.ReadDir(filepath.Join(setting.RepoRootPath, ownerDir.Name())) - if err != nil { - require.NoError(t, err, "unable to read the new repo root: %v\n", err) - } - for _, repoDir := range repoDirs { - _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "pack"), 0o755) - _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "info"), 0o755) - _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "heads"), 0o755) - _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "tag"), 0o755) - _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "pull"), 0o755) - } - } + var err error + setting.RepoRootPath, err = os.MkdirTemp(t.TempDir(), "forgejo-repo-rooth") + require.NoError(t, err) + require.NoError(t, unittest.CopyDir(preparedDir, setting.RepoRootPath)) } func PrepareArtifactsStorage(t testing.TB) { From 19c9e0a0c23f5b2e0d2303ac845f2b0ce830f7e9 Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Fri, 15 Nov 2024 00:48:45 +0100 Subject: [PATCH 04/49] ci: proper job name --- .forgejo/workflows/testing.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.forgejo/workflows/testing.yml b/.forgejo/workflows/testing.yml index 053fdb0450..d3b22c0a64 100644 --- a/.forgejo/workflows/testing.yml +++ b/.forgejo/workflows/testing.yml @@ -135,20 +135,21 @@ jobs: needs: [backend-checks, frontend-checks, test-unit] container: image: 'code.forgejo.org/oci/node:20-bookworm' + name: ${{ format('test-remote-cacher ({0})', matrix.cacher.name) }} strategy: matrix: cacher: - # redis - - image: docker.io/bitnami/redis:7.2 + - name: redis + image: docker.io/bitnami/redis:7.2 options: --tmpfs /bitnami/redis/data - # redict - - image: registry.redict.io/redict:7.3.0-scratch + - name: redict + image: registry.redict.io/redict:7.3.0-scratch options: --tmpfs /data - # valkey - - image: docker.io/bitnami/valkey:7.2 + - name: valkey + image: docker.io/bitnami/valkey:7.2 options: --tmpfs /bitnami/redis/data - # garnet - - image: ghcr.io/microsoft/garnet-alpine:1.0.14 + - name: garnet + image: ghcr.io/microsoft/garnet-alpine:1.0.14 options: --tmpfs /data services: cacher: From c226b4d00a18290ea133076f125f7108d81922a8 Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Fri, 15 Nov 2024 00:55:43 +0100 Subject: [PATCH 05/49] feat: use oci mirror for `tonistiigi/xx` image --- Dockerfile | 2 +- Dockerfile.rootless | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 05c4f33e05..ae21a0821e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM docker.io/tonistiigi/xx AS xx +FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/xx AS xx FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/golang:1.23-alpine3.20 as build-env diff --git a/Dockerfile.rootless b/Dockerfile.rootless index cc6820664a..c5d6a13f35 100644 --- a/Dockerfile.rootless +++ b/Dockerfile.rootless @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM docker.io/tonistiigi/xx AS xx +FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/xx AS xx FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/golang:1.23-alpine3.20 as build-env From 4043377377bf92b4106237ff6ac0405da58925df Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 15 Nov 2024 00:09:10 +0000 Subject: [PATCH 06/49] Update dependency tailwindcss to v3.4.15 --- package-lock.json | 30 +++++++++++++++--------------- package.json | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index c5c75532c8..a7f66ead2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,7 @@ "pretty-ms": "9.0.0", "sortablejs": "1.15.3", "swagger-ui-dist": "5.17.14", - "tailwindcss": "3.4.13", + "tailwindcss": "3.4.15", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", "tippy.js": "6.3.7", @@ -15389,33 +15389,33 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.13", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.13.tgz", - "integrity": "sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.15.tgz", + "integrity": "sha512-r4MeXnfBmSOuKUWmXe6h2CcyfzJCEk4F0pptO5jlnYSIViUkVmsawj80N5h2lO3gwcmSb4n3PuN+e+GC1Guylw==", "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", - "chokidar": "^3.5.3", + "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "fast-glob": "^3.3.0", + "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.21.0", + "jiti": "^1.21.6", "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", + "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", diff --git a/package.json b/package.json index f76fbf0764..a3e012d673 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "pretty-ms": "9.0.0", "sortablejs": "1.15.3", "swagger-ui-dist": "5.17.14", - "tailwindcss": "3.4.13", + "tailwindcss": "3.4.15", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", "tippy.js": "6.3.7", From cdc38ace391ae44a080eb470ee6c879465f65c0a Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 15 Nov 2024 02:03:08 +0000 Subject: [PATCH 07/49] Update module google.golang.org/grpc to v1.68.0 --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 2b8a6cb88a..3b978d27b2 100644 --- a/go.mod +++ b/go.mod @@ -107,7 +107,7 @@ require ( golang.org/x/sys v0.27.0 golang.org/x/text v0.20.0 golang.org/x/tools v0.26.0 - google.golang.org/grpc v1.67.1 + google.golang.org/grpc v1.68.0 google.golang.org/protobuf v1.35.1 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/ini.v1 v1.67.0 @@ -283,7 +283,7 @@ require ( golang.org/x/mod v0.21.0 // indirect golang.org/x/sync v0.9.0 // indirect golang.org/x/time v0.5.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 40a351dbe6..ae895e5297 100644 --- a/go.sum +++ b/go.sum @@ -843,10 +843,10 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= -google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= +google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= From c8d97e5594cdf7563d258878c97f2536e5e4f932 Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Thu, 14 Nov 2024 23:12:57 +0100 Subject: [PATCH 08/49] ci: use oci mirror images --- .forgejo/workflows/testing.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.forgejo/workflows/testing.yml b/.forgejo/workflows/testing.yml index d3b22c0a64..a04817fd04 100644 --- a/.forgejo/workflows/testing.yml +++ b/.forgejo/workflows/testing.yml @@ -56,13 +56,13 @@ jobs: image: 'code.forgejo.org/oci/node:20-bookworm' services: elasticsearch: - image: docker.io/bitnami/elasticsearch:7 + image: code.forgejo.org/oci/bitnami/elasticsearch:7 options: --tmpfs /bitnami/elasticsearch/data env: discovery.type: single-node ES_JAVA_OPTS: "-Xms512m -Xmx512m" minio: - image: docker.io/bitnami/minio:2024.8.17 + image: code.forgejo.org/oci/bitnami/minio:2024.8.17 options: >- --hostname gitea.minio --tmpfs /bitnami/minio/data env: @@ -140,13 +140,13 @@ jobs: matrix: cacher: - name: redis - image: docker.io/bitnami/redis:7.2 + image: code.forgejo.org/oci/bitnami/redis:7.2 options: --tmpfs /bitnami/redis/data - name: redict image: registry.redict.io/redict:7.3.0-scratch options: --tmpfs /data - name: valkey - image: docker.io/bitnami/valkey:7.2 + image: code.forgejo.org/oci/bitnami/valkey:7.2 options: --tmpfs /bitnami/redis/data - name: garnet image: ghcr.io/microsoft/garnet-alpine:1.0.14 @@ -178,7 +178,7 @@ jobs: image: 'code.forgejo.org/oci/node:20-bookworm' services: mysql: - image: 'docker.io/bitnami/mysql:8.4' + image: 'code.forgejo.org/oci/bitnami/mysql:8.4' env: ALLOW_EMPTY_PASSWORD: yes MYSQL_DATABASE: testgitea @@ -208,13 +208,13 @@ jobs: image: 'code.forgejo.org/oci/node:20-bookworm' services: minio: - image: docker.io/bitnami/minio:2024.8.17 + image: code.forgejo.org/oci/bitnami/minio:2024.8.17 env: MINIO_ROOT_USER: 123456 MINIO_ROOT_PASSWORD: 12345678 options: --tmpfs /bitnami/minio/data ldap: - image: docker.io/gitea/test-openldap:latest + image: code.forgejo.org/oci/test-openldap:latest pgsql: image: 'code.forgejo.org/oci/postgres:15' env: From 1c25bbe77313fb4f035ed6fb6a61b74b4ab12935 Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Fri, 15 Nov 2024 01:41:55 +0100 Subject: [PATCH 09/49] test: fix e2e tests --- tests/e2e/actions.test.e2e.ts | 5 +---- tests/e2e/example.test.e2e.ts | 8 ++++---- tests/e2e/issue-sidebar.test.e2e.ts | 16 +++++++-------- tests/e2e/markdown-editor.test.e2e.ts | 26 ++++++++++++------------ tests/e2e/reaction-selectors.test.e2e.ts | 6 +++--- tests/e2e/repo-code.test.e2e.ts | 4 ++-- tests/e2e/shared/accessibility.ts | 4 ++-- tests/e2e/utils_e2e.ts | 8 +++----- tests/e2e/webauthn.test.e2e.ts | 1 - 9 files changed, 35 insertions(+), 43 deletions(-) diff --git a/tests/e2e/actions.test.e2e.ts b/tests/e2e/actions.test.e2e.ts index 0aa1c747dc..e9f7db52f2 100644 --- a/tests/e2e/actions.test.e2e.ts +++ b/tests/e2e/actions.test.e2e.ts @@ -20,7 +20,6 @@ const workflow_trigger_notification_text = 'This workflow has a workflow_dispatc test('workflow dispatch present', async ({browser}, workerInfo) => { const context = await load_logged_in_context(browser, workerInfo, 'user2'); - /** @type {import('@playwright/test').Page} */ const page = await context.newPage(); await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0'); @@ -40,7 +39,6 @@ test('workflow dispatch error: missing inputs', async ({browser}, workerInfo) => test.skip(workerInfo.project.name === 'Mobile Safari', 'Flaky behaviour on mobile safari; see https://codeberg.org/forgejo/forgejo/pulls/3334#issuecomment-2033383'); const context = await load_logged_in_context(browser, workerInfo, 'user2'); - /** @type {import('@playwright/test').Page} */ const page = await context.newPage(); await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0'); @@ -62,14 +60,13 @@ test('workflow dispatch success', async ({browser}, workerInfo) => { test.skip(workerInfo.project.name === 'Mobile Safari', 'Flaky behaviour on mobile safari; see https://codeberg.org/forgejo/forgejo/pulls/3334#issuecomment-2033383'); const context = await load_logged_in_context(browser, workerInfo, 'user2'); - /** @type {import('@playwright/test').Page} */ const page = await context.newPage(); await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0'); await page.locator('#workflow_dispatch_dropdown>button').click(); - await page.type('input[name="inputs[string2]"]', 'abc'); + await page.fill('input[name="inputs[string2]"]', 'abc'); await page.locator('#workflow-dispatch-submit').click(); await expect(page.getByText('Workflow run was successfully requested.')).toBeVisible(); diff --git a/tests/e2e/example.test.e2e.ts b/tests/e2e/example.test.e2e.ts index 90fd9169a4..64818c4557 100644 --- a/tests/e2e/example.test.e2e.ts +++ b/tests/e2e/example.test.e2e.ts @@ -21,10 +21,10 @@ test('Load Homepage', async ({page}) => { test('Register Form', async ({page}, workerInfo) => { const response = await page.goto('/user/sign_up'); expect(response?.status()).toBe(200); // Status OK - await page.type('input[name=user_name]', `e2e-test-${workerInfo.workerIndex}`); - await page.type('input[name=email]', `e2e-test-${workerInfo.workerIndex}@test.com`); - await page.type('input[name=password]', 'test123test123'); - await page.type('input[name=retype]', 'test123test123'); + await page.fill('input[name=user_name]', `e2e-test-${workerInfo.workerIndex}`); + await page.fill('input[name=email]', `e2e-test-${workerInfo.workerIndex}@test.com`); + await page.fill('input[name=password]', 'test123test123'); + await page.fill('input[name=retype]', 'test123test123'); await page.click('form button.ui.primary.button:visible'); // Make sure we routed to the home page. Else login failed. expect(page.url()).toBe(`${workerInfo.project.use.baseURL}/`); diff --git a/tests/e2e/issue-sidebar.test.e2e.ts b/tests/e2e/issue-sidebar.test.e2e.ts index 422f3ef94e..1e05069e7f 100644 --- a/tests/e2e/issue-sidebar.test.e2e.ts +++ b/tests/e2e/issue-sidebar.test.e2e.ts @@ -4,19 +4,18 @@ // web_src/js/features/repo-issue** // @watch end -import {expect} from '@playwright/test'; +/* eslint playwright/expect-expect: ["error", { "assertFunctionNames": ["check_wip"] }] */ + +import {expect, type Page} from '@playwright/test'; import {test, login_user, login} from './utils_e2e.ts'; test.beforeAll(async ({browser}, workerInfo) => { await login_user(browser, workerInfo, 'user2'); }); -/* eslint-disable playwright/expect-expect */ -// some tests are reported to have no assertions, -// which is not correct, because they use the global helper function test.describe('Pull: Toggle WIP', () => { const prTitle = 'pull5'; - async function toggle_wip_to({page}, should) { + async function toggle_wip_to({page}, should: boolean) { await page.waitForLoadState('domcontentloaded'); if (should) { await page.getByText('Still in progress?').click(); @@ -25,7 +24,7 @@ test.describe('Pull: Toggle WIP', () => { } } - async function check_wip({page}, is) { + async function check_wip({page}, is: boolean) { const elemTitle = 'h1'; const stateLabel = '.issue-state-label'; await page.waitForLoadState('domcontentloaded'); @@ -96,12 +95,11 @@ test.describe('Pull: Toggle WIP', () => { await expect(page.locator('h1')).toContainText(maxLenStr); }); }); -/* eslint-enable playwright/expect-expect */ test('Issue: Labels', async ({browser}, workerInfo) => { test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636'); - async function submitLabels({page}) { + async function submitLabels({page}: {page: Page}) { const submitted = page.waitForResponse('/user2/repo1/issues/labels'); await page.locator('textarea').first().click(); // close via unrelated element await submitted; @@ -199,7 +197,7 @@ test('New Issue: Assignees', async ({browser}, workerInfo) => { // Assign other user (with searchbox) await page.locator('.select-assignees.dropdown').click(); - await page.type('.select-assignees .menu .search input', 'user4'); + await page.fill('.select-assignees .menu .search input', 'user4'); await expect(page.locator('.select-assignees .menu .item').filter({hasText: 'user2'})).toBeHidden(); await expect(page.locator('.select-assignees .menu .item').filter({hasText: 'user4'})).toBeVisible(); await page.locator('.select-assignees .menu .item').filter({hasText: 'user4'}).click(); diff --git a/tests/e2e/markdown-editor.test.e2e.ts b/tests/e2e/markdown-editor.test.e2e.ts index 5db242bb36..4cdf4644f7 100644 --- a/tests/e2e/markdown-editor.test.e2e.ts +++ b/tests/e2e/markdown-editor.test.e2e.ts @@ -29,7 +29,7 @@ test('markdown indentation', async ({browser}, workerInfo) => { // Indent, then unindent first line await textarea.focus(); - await textarea.evaluate((it) => it.setSelectionRange(0, 0)); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(0, 0)); await indent.click(); await expect(textarea).toHaveValue(`${tab}* first\n* second\n* third\n* last`); await unindent.click(); @@ -45,7 +45,7 @@ test('markdown indentation', async ({browser}, workerInfo) => { // Subsequently, select a chunk of 2nd and 3rd line and indent both, preserving the cursor position in relation to text await textarea.focus(); - await textarea.evaluate((it) => it.setSelectionRange(it.value.indexOf('cond'), it.value.indexOf('hird'))); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('cond'), it.value.indexOf('hird'))); await indent.click(); const lines23 = `* first\n${tab}${tab}* second\n${tab}* third\n* last`; await expect(textarea).toHaveValue(lines23); @@ -60,7 +60,7 @@ test('markdown indentation', async ({browser}, workerInfo) => { // Indent and unindent with cursor at the end of the line await textarea.focus(); - await textarea.evaluate((it) => it.setSelectionRange(it.value.indexOf('cond'), it.value.indexOf('cond'))); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('cond'), it.value.indexOf('cond'))); await textarea.press('End'); await indent.click(); await expect(textarea).toHaveValue(`* first\n${tab}* second\n* third\n* last`); @@ -69,7 +69,7 @@ test('markdown indentation', async ({browser}, workerInfo) => { // Check that Tab does work after input await textarea.focus(); - await textarea.evaluate((it) => it.setSelectionRange(it.value.length, it.value.length)); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length)); await textarea.press('Shift+Enter'); // Avoid triggering the prefix continuation feature await textarea.pressSequentially('* least'); await indent.click(); @@ -78,7 +78,7 @@ test('markdown indentation', async ({browser}, workerInfo) => { // Check that partial indents are cleared await textarea.focus(); await textarea.fill(initText); - await textarea.evaluate((it) => it.setSelectionRange(it.value.indexOf('* second'), it.value.indexOf('* second'))); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('* second'), it.value.indexOf('* second'))); await textarea.pressSequentially(' '); await unindent.click(); await expect(textarea).toHaveValue(initText); @@ -99,7 +99,7 @@ test('markdown list continuation', async ({browser}, workerInfo) => { await textarea.fill(initText); // Test continuation of '* ' prefix - await textarea.evaluate((it) => it.setSelectionRange(it.value.indexOf('cond'), it.value.indexOf('cond'))); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('cond'), it.value.indexOf('cond'))); await textarea.press('End'); await textarea.press('Enter'); await textarea.pressSequentially('middle'); @@ -112,7 +112,7 @@ test('markdown list continuation', async ({browser}, workerInfo) => { await expect(textarea).toHaveValue(`* first\n* second\n${tab}* middle\n${tab}* muddle\n* third\n* last`); // Test breaking in the middle of a line - await textarea.evaluate((it) => it.setSelectionRange(it.value.lastIndexOf('ddle'), it.value.lastIndexOf('ddle'))); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.lastIndexOf('ddle'), it.value.lastIndexOf('ddle'))); await textarea.pressSequentially('tate'); await textarea.press('Enter'); await textarea.pressSequentially('me'); @@ -120,7 +120,7 @@ test('markdown list continuation', async ({browser}, workerInfo) => { // Test not triggering when Shift held await textarea.fill(initText); - await textarea.evaluate((it) => it.setSelectionRange(it.value.length, it.value.length)); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length)); await textarea.press('Shift+Enter'); await textarea.press('Enter'); await textarea.pressSequentially('...but not least'); @@ -128,28 +128,28 @@ test('markdown list continuation', async ({browser}, workerInfo) => { // Test continuation of ordered list await textarea.fill(`1. one\n2. two`); - await textarea.evaluate((it) => it.setSelectionRange(it.value.length, it.value.length)); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length)); await textarea.press('Enter'); await textarea.pressSequentially('three'); await expect(textarea).toHaveValue(`1. one\n2. two\n3. three`); // Test continuation of alternative ordered list syntax await textarea.fill(`1) one\n2) two`); - await textarea.evaluate((it) => it.setSelectionRange(it.value.length, it.value.length)); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length)); await textarea.press('Enter'); await textarea.pressSequentially('three'); await expect(textarea).toHaveValue(`1) one\n2) two\n3) three`); // Test continuation of blockquote await textarea.fill(`> knowledge is power`); - await textarea.evaluate((it) => it.setSelectionRange(it.value.length, it.value.length)); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length)); await textarea.press('Enter'); await textarea.pressSequentially('france is bacon'); await expect(textarea).toHaveValue(`> knowledge is power\n> france is bacon`); // Test continuation of checklists await textarea.fill(`- [ ] have a problem\n- [x] create a solution`); - await textarea.evaluate((it) => it.setSelectionRange(it.value.length, it.value.length)); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length)); await textarea.press('Enter'); await textarea.pressSequentially('write a test'); await expect(textarea).toHaveValue(`- [ ] have a problem\n- [x] create a solution\n- [ ] write a test`); @@ -174,7 +174,7 @@ test('markdown list continuation', async ({browser}, workerInfo) => { ]; for (const prefix of prefixes) { await textarea.fill(`${prefix}one`); - await textarea.evaluate((it) => it.setSelectionRange(it.value.length, it.value.length)); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.length, it.value.length)); await textarea.press('Enter'); await textarea.pressSequentially('two'); await expect(textarea).toHaveValue(`${prefix}one\n${prefix}two`); diff --git a/tests/e2e/reaction-selectors.test.e2e.ts b/tests/e2e/reaction-selectors.test.e2e.ts index a52b47e036..3bd54c7881 100644 --- a/tests/e2e/reaction-selectors.test.e2e.ts +++ b/tests/e2e/reaction-selectors.test.e2e.ts @@ -3,14 +3,14 @@ // routers/web/repo/issue.go // @watch end -import {expect} from '@playwright/test'; +import {expect, type Locator} from '@playwright/test'; import {test, login_user, load_logged_in_context} from './utils_e2e.ts'; test.beforeAll(async ({browser}, workerInfo) => { await login_user(browser, workerInfo, 'user2'); }); -const assertReactionCounts = (comment, counts) => +const assertReactionCounts = (comment: Locator, counts: unknown) => expect(async () => { await expect(comment.locator('.reactions')).toBeVisible(); @@ -29,7 +29,7 @@ const assertReactionCounts = (comment, counts) => return expect(reactions).toStrictEqual(counts); }).toPass(); -async function toggleReaction(menu, reaction) { +async function toggleReaction(menu: Locator, reaction: string) { await menu.evaluateAll((menus) => menus[0].focus()); await menu.locator('.add-reaction').click(); await menu.locator(`[role=menuitem][data-reaction-content="${reaction}"]`).click(); diff --git a/tests/e2e/repo-code.test.e2e.ts b/tests/e2e/repo-code.test.e2e.ts index d114a9b9c0..d78fa33fe5 100644 --- a/tests/e2e/repo-code.test.e2e.ts +++ b/tests/e2e/repo-code.test.e2e.ts @@ -4,7 +4,7 @@ // services/gitdiff/** // @watch end -import {expect} from '@playwright/test'; +import {expect, type Page} from '@playwright/test'; import {test, login_user, login} from './utils_e2e.ts'; import {accessibilityCheck} from './shared/accessibility.ts'; @@ -12,7 +12,7 @@ test.beforeAll(async ({browser}, workerInfo) => { await login_user(browser, workerInfo, 'user2'); }); -async function assertSelectedLines(page, nums) { +async function assertSelectedLines(page: Page, nums: string[]) { const pageAssertions = async () => { expect( await Promise.all((await page.locator('tr.active [data-line-number]').all()).map((line) => line.getAttribute('data-line-number'))), diff --git a/tests/e2e/shared/accessibility.ts b/tests/e2e/shared/accessibility.ts index 6675e0d9eb..1b59d89485 100644 --- a/tests/e2e/shared/accessibility.ts +++ b/tests/e2e/shared/accessibility.ts @@ -3,9 +3,9 @@ import {AxeBuilder} from '@axe-core/playwright'; export async function accessibilityCheck({page}: {page: Page}, includes: string[], excludes: string[], disabledRules: string[]) { // contrast of inline links is still a global issue in Forgejo - disabledRules += 'link-in-text-block'; + disabledRules.push('link-in-text-block'); - let accessibilityScanner = await new AxeBuilder({page}) + let accessibilityScanner = new AxeBuilder({page}) .disableRules(disabledRules); // passing the whole array seems to be not supported, // iterating has the nice side-effectof skipping this if the array is empty diff --git a/tests/e2e/utils_e2e.ts b/tests/e2e/utils_e2e.ts index 89dacce8a4..a52495bcc6 100644 --- a/tests/e2e/utils_e2e.ts +++ b/tests/e2e/utils_e2e.ts @@ -33,8 +33,8 @@ export async function login_user(browser: Browser, workerInfo: TestInfo, user: s expect(response?.status()).toBe(200); // Status OK // Fill out form - await page.type('input[name=user_name]', user); - await page.type('input[name=password]', LOGIN_PASSWORD); + await page.fill('input[name=user_name]', user); + await page.fill('input[name=password]', LOGIN_PASSWORD); await page.click('form button.ui.primary.button:visible'); await page.waitForLoadState(); @@ -48,15 +48,13 @@ export async function login_user(browser: Browser, workerInfo: TestInfo, user: s } export async function load_logged_in_context(browser: Browser, workerInfo: TestInfo, user: string) { - let context; try { - context = await test_context(browser, {storageState: `${ARTIFACTS_PATH}/state-${user}-${workerInfo.workerIndex}.json`}); + return await test_context(browser, {storageState: `${ARTIFACTS_PATH}/state-${user}-${workerInfo.workerIndex}.json`}); } catch (err) { if (err.code === 'ENOENT') { throw new Error(`Could not find state for '${user}'. Did you call login_user(browser, workerInfo, '${user}') in test.beforeAll()?`); } } - return context; } export async function login({browser}: {browser: Browser}, workerInfo: TestInfo) { diff --git a/tests/e2e/webauthn.test.e2e.ts b/tests/e2e/webauthn.test.e2e.ts index c351b6a468..98a2d1c152 100644 --- a/tests/e2e/webauthn.test.e2e.ts +++ b/tests/e2e/webauthn.test.e2e.ts @@ -30,7 +30,6 @@ test('WebAuthn register & login flow', async ({browser, request}, workerInfo) => transport: 'usb', automaticPresenceSimulation: true, isUserVerified: true, - backupEligibility: true, // TODO: this doesn't seem to be available?! }, }); From 1ce33aa38d1d258d14523ff2c7c2dbf339f22b74 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 11 Aug 2024 05:13:01 +0200 Subject: [PATCH 10/49] fix: extend `forgejo_auth_token` table - Add a `purpose` column, this allows the `forgejo_auth_token` table to be used by other parts of Forgejo, while still enjoying the no-compromise architecture. - Remove the 'roll your own crypto' time limited code functions and migrate them to the `forgejo_auth_token` table. This migration ensures generated codes can only be used for their purpose and ensure they are invalidated after their usage by deleting it from the database, this also should help making auditing of the security code easier, as we're no longer trying to stuff a lot of data into a HMAC construction. -Helper functions are rewritten to ensure a safe-by-design approach to these tokens. - Add the `forgejo_auth_token` to dbconsistency doctor and add it to the `deleteUser` function. - TODO: Add cron job to delete expired authorization tokens. - Unit and integration tests added. --- models/auth/auth_token.go | 26 +++- models/forgejo_migrations/migrate.go | 2 + models/forgejo_migrations/v24.go | 19 +++ models/user/email_address.go | 19 --- models/user/user.go | 76 ++++++---- models/user/user_test.go | 66 +++++++++ modules/base/tool.go | 66 --------- modules/base/tool_test.go | 57 ------- routers/web/auth/auth.go | 100 ++++++------- routers/web/auth/password.go | 18 ++- routers/web/user/setting/account.go | 15 +- services/context/context_cookie.go | 2 +- services/doctor/dbconsistency.go | 3 + services/mailer/mail.go | 47 ++++-- services/user/delete.go | 1 + tests/integration/auth_token_test.go | 8 +- tests/integration/org_team_invite_test.go | 7 +- tests/integration/user_test.go | 172 ++++++++++++++++++++++ 18 files changed, 448 insertions(+), 256 deletions(-) create mode 100644 models/forgejo_migrations/v24.go diff --git a/models/auth/auth_token.go b/models/auth/auth_token.go index 2c3ca90734..8955b57a4f 100644 --- a/models/auth/auth_token.go +++ b/models/auth/auth_token.go @@ -15,12 +15,31 @@ import ( "code.gitea.io/gitea/modules/util" ) +type AuthorizationPurpose string + +var ( + // Used to store long term authorization tokens. + LongTermAuthorization AuthorizationPurpose = "long_term_authorization" + + // Used to activate a user account. + UserActivation AuthorizationPurpose = "user_activation" + + // Used to reset the password. + PasswordReset AuthorizationPurpose = "password_reset" +) + +// Used to activate the specified email address for a user. +func EmailActivation(email string) AuthorizationPurpose { + return AuthorizationPurpose("email_activation:" + email) +} + // AuthorizationToken represents a authorization token to a user. type AuthorizationToken struct { ID int64 `xorm:"pk autoincr"` UID int64 `xorm:"INDEX"` LookupKey string `xorm:"INDEX UNIQUE"` HashedValidator string + Purpose AuthorizationPurpose `xorm:"NOT NULL"` Expiry timeutil.TimeStamp } @@ -41,7 +60,7 @@ func (authToken *AuthorizationToken) IsExpired() bool { // GenerateAuthToken generates a new authentication token for the given user. // It returns the lookup key and validator values that should be passed to the // user via a long-term cookie. -func GenerateAuthToken(ctx context.Context, userID int64, expiry timeutil.TimeStamp) (lookupKey, validator string, err error) { +func GenerateAuthToken(ctx context.Context, userID int64, expiry timeutil.TimeStamp, purpose AuthorizationPurpose) (lookupKey, validator string, err error) { // Request 64 random bytes. The first 32 bytes will be used for the lookupKey // and the other 32 bytes will be used for the validator. rBytes, err := util.CryptoRandomBytes(64) @@ -56,14 +75,15 @@ func GenerateAuthToken(ctx context.Context, userID int64, expiry timeutil.TimeSt Expiry: expiry, LookupKey: lookupKey, HashedValidator: HashValidator(rBytes[32:]), + Purpose: purpose, }) return lookupKey, validator, err } // FindAuthToken will find a authorization token via the lookup key. -func FindAuthToken(ctx context.Context, lookupKey string) (*AuthorizationToken, error) { +func FindAuthToken(ctx context.Context, lookupKey string, purpose AuthorizationPurpose) (*AuthorizationToken, error) { var authToken AuthorizationToken - has, err := db.GetEngine(ctx).Where("lookup_key = ?", lookupKey).Get(&authToken) + has, err := db.GetEngine(ctx).Where("lookup_key = ? AND purpose = ?", lookupKey, purpose).Get(&authToken) if err != nil { return nil, err } else if !has { diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index 116579eeff..53159e31f2 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -84,6 +84,8 @@ var migrations = []*Migration{ NewMigration("Add `legacy` to `web_authn_credential` table", AddLegacyToWebAuthnCredential), // v23 -> v24 NewMigration("Add `delete_branch_after_merge` to `auto_merge` table", AddDeleteBranchAfterMergeToAutoMerge), + // v24 -> v25 + NewMigration("Add `purpose` column to `forgejo_auth_token` table", AddPurposeToForgejoAuthToken), } // GetCurrentDBVersion returns the current Forgejo database version. diff --git a/models/forgejo_migrations/v24.go b/models/forgejo_migrations/v24.go new file mode 100644 index 0000000000..8c751e3f5f --- /dev/null +++ b/models/forgejo_migrations/v24.go @@ -0,0 +1,19 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package forgejo_migrations //nolint:revive + +import "xorm.io/xorm" + +func AddPurposeToForgejoAuthToken(x *xorm.Engine) error { + type ForgejoAuthToken struct { + ID int64 `xorm:"pk autoincr"` + Purpose string `xorm:"NOT NULL"` + } + if err := x.Sync(new(ForgejoAuthToken)); err != nil { + return err + } + + _, err := x.Exec("UPDATE `forgejo_auth_token` SET purpose = 'long_term_authorization' WHERE purpose = ''") + return err +} diff --git a/models/user/email_address.go b/models/user/email_address.go index b14ff7886c..54667986ac 100644 --- a/models/user/email_address.go +++ b/models/user/email_address.go @@ -8,10 +8,8 @@ import ( "context" "fmt" "strings" - "time" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" @@ -246,23 +244,6 @@ func updateActivation(ctx context.Context, email *EmailAddress, activate bool) e return UpdateUserCols(ctx, user, "rands") } -// VerifyActiveEmailCode verifies active email code when active account -func VerifyActiveEmailCode(ctx context.Context, code, email string) *EmailAddress { - if user := GetVerifyUser(ctx, code); user != nil { - // time limit code - prefix := code[:base.TimeLimitCodeLength] - data := fmt.Sprintf("%d%s%s%s%s", user.ID, email, user.LowerName, user.Passwd, user.Rands) - - if base.VerifyTimeLimitCode(time.Now(), data, setting.Service.ActiveCodeLives, prefix) { - emailAddress := &EmailAddress{UID: user.ID, Email: email} - if has, _ := db.GetEngine(ctx).Get(emailAddress); has { - return emailAddress - } - } - } - return nil -} - // SearchEmailOrderBy is used to sort the results from SearchEmails() type SearchEmailOrderBy string diff --git a/models/user/user.go b/models/user/user.go index 43bab4f3e9..0fa8bb0ca9 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -7,7 +7,9 @@ package user import ( "context" + "crypto/subtle" "encoding/hex" + "errors" "fmt" "net/mail" "net/url" @@ -318,15 +320,14 @@ func (u *User) OrganisationLink() string { return setting.AppSubURL + "/org/" + url.PathEscape(u.Name) } -// GenerateEmailActivateCode generates an activate code based on user information and given e-mail. -func (u *User) GenerateEmailActivateCode(email string) string { - code := base.CreateTimeLimitCode( - fmt.Sprintf("%d%s%s%s%s", u.ID, email, u.LowerName, u.Passwd, u.Rands), - setting.Service.ActiveCodeLives, time.Now(), nil) - - // Add tail hex username - code += hex.EncodeToString([]byte(u.LowerName)) - return code +// GenerateEmailAuthorizationCode generates an activation code based for the user for the specified purpose. +// The standard expiry is ActiveCodeLives minutes. +func (u *User) GenerateEmailAuthorizationCode(ctx context.Context, purpose auth.AuthorizationPurpose) (string, error) { + lookup, validator, err := auth.GenerateAuthToken(ctx, u.ID, timeutil.TimeStampNow().Add(int64(setting.Service.ActiveCodeLives)*60), purpose) + if err != nil { + return "", err + } + return lookup + ":" + validator, nil } // GetUserFollowers returns range of user's followers. @@ -838,35 +839,50 @@ func countUsers(ctx context.Context, opts *CountUserFilter) int64 { return count } -// GetVerifyUser get user by verify code -func GetVerifyUser(ctx context.Context, code string) (user *User) { - if len(code) <= base.TimeLimitCodeLength { - return nil +// VerifyUserActiveCode verifies that the code is valid for the given purpose for this user. +// If delete is specified, the token will be deleted. +func VerifyUserAuthorizationToken(ctx context.Context, code string, purpose auth.AuthorizationPurpose, delete bool) (*User, error) { + lookupKey, validator, found := strings.Cut(code, ":") + if !found { + return nil, nil } - // use tail hex username query user - hexStr := code[base.TimeLimitCodeLength:] - if b, err := hex.DecodeString(hexStr); err == nil { - if user, err = GetUserByName(ctx, string(b)); user != nil { - return user + authToken, err := auth.FindAuthToken(ctx, lookupKey, purpose) + if err != nil { + if errors.Is(err, util.ErrNotExist) { + return nil, nil } - log.Error("user.getVerifyUser: %v", err) + return nil, err } - return nil -} + if authToken.IsExpired() { + return nil, auth.DeleteAuthToken(ctx, authToken) + } -// VerifyUserActiveCode verifies active code when active account -func VerifyUserActiveCode(ctx context.Context, code string) (user *User) { - if user = GetVerifyUser(ctx, code); user != nil { - // time limit code - prefix := code[:base.TimeLimitCodeLength] - data := fmt.Sprintf("%d%s%s%s%s", user.ID, user.Email, user.LowerName, user.Passwd, user.Rands) - if base.VerifyTimeLimitCode(time.Now(), data, setting.Service.ActiveCodeLives, prefix) { - return user + rawValidator, err := hex.DecodeString(validator) + if err != nil { + return nil, err + } + + if subtle.ConstantTimeCompare([]byte(authToken.HashedValidator), []byte(auth.HashValidator(rawValidator))) == 0 { + return nil, errors.New("validator doesn't match") + } + + u, err := GetUserByID(ctx, authToken.UID) + if err != nil { + if IsErrUserNotExist(err) { + return nil, nil + } + return nil, err + } + + if delete { + if err := auth.DeleteAuthToken(ctx, authToken); err != nil { + return nil, err } } - return nil + + return u, nil } // ValidateUser check if user is valid to insert / update into database diff --git a/models/user/user_test.go b/models/user/user_test.go index 082c21063c..6f20a577f3 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -7,6 +7,7 @@ package user_test import ( "context" "crypto/rand" + "encoding/hex" "fmt" "strings" "testing" @@ -21,7 +22,9 @@ import ( "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/tests" @@ -700,3 +703,66 @@ func TestDisabledUserFeatures(t *testing.T) { assert.True(t, user_model.IsFeatureDisabledWithLoginType(user, f)) } } + +func TestGenerateEmailAuthorizationCode(t *testing.T) { + defer test.MockVariableValue(&setting.Service.ActiveCodeLives, 2)() + require.NoError(t, unittest.PrepareTestDatabase()) + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + code, err := user.GenerateEmailAuthorizationCode(db.DefaultContext, auth.UserActivation) + require.NoError(t, err) + + lookupKey, validator, ok := strings.Cut(code, ":") + assert.True(t, ok) + + rawValidator, err := hex.DecodeString(validator) + require.NoError(t, err) + + authToken, err := auth.FindAuthToken(db.DefaultContext, lookupKey, auth.UserActivation) + require.NoError(t, err) + assert.False(t, authToken.IsExpired()) + assert.EqualValues(t, authToken.HashedValidator, auth.HashValidator(rawValidator)) + + authToken.Expiry = authToken.Expiry.Add(-int64(setting.Service.ActiveCodeLives) * 60) + assert.True(t, authToken.IsExpired()) +} + +func TestVerifyUserAuthorizationToken(t *testing.T) { + defer test.MockVariableValue(&setting.Service.ActiveCodeLives, 2)() + require.NoError(t, unittest.PrepareTestDatabase()) + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + code, err := user.GenerateEmailAuthorizationCode(db.DefaultContext, auth.UserActivation) + require.NoError(t, err) + + lookupKey, _, ok := strings.Cut(code, ":") + assert.True(t, ok) + + t.Run("Wrong purpose", func(t *testing.T) { + u, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.PasswordReset, false) + require.NoError(t, err) + assert.Nil(t, u) + }) + + t.Run("No delete", func(t *testing.T) { + u, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.UserActivation, false) + require.NoError(t, err) + assert.EqualValues(t, user.ID, u.ID) + + authToken, err := auth.FindAuthToken(db.DefaultContext, lookupKey, auth.UserActivation) + require.NoError(t, err) + assert.NotNil(t, authToken) + }) + + t.Run("Delete", func(t *testing.T) { + u, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.UserActivation, true) + require.NoError(t, err) + assert.EqualValues(t, user.ID, u.ID) + + authToken, err := auth.FindAuthToken(db.DefaultContext, lookupKey, auth.UserActivation) + require.ErrorIs(t, err, util.ErrNotExist) + assert.Nil(t, authToken) + }) +} diff --git a/modules/base/tool.go b/modules/base/tool.go index 7612fff73a..02f1db59d3 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -4,26 +4,20 @@ package base import ( - "crypto/hmac" - "crypto/sha1" "crypto/sha256" - "crypto/subtle" "encoding/base64" "encoding/hex" "errors" "fmt" - "hash" "os" "path/filepath" "runtime" "strconv" "strings" - "time" "unicode/utf8" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" "github.com/dustin/go-humanize" ) @@ -54,66 +48,6 @@ func BasicAuthDecode(encoded string) (string, string, error) { return "", "", errors.New("invalid basic authentication") } -// VerifyTimeLimitCode verify time limit code -func VerifyTimeLimitCode(now time.Time, data string, minutes int, code string) bool { - if len(code) <= 18 { - return false - } - - startTimeStr := code[:12] - aliveTimeStr := code[12:18] - aliveTime, _ := strconv.Atoi(aliveTimeStr) // no need to check err, if anything wrong, the following code check will fail soon - - // check code - retCode := CreateTimeLimitCode(data, aliveTime, startTimeStr, nil) - if subtle.ConstantTimeCompare([]byte(retCode), []byte(code)) != 1 { - retCode = CreateTimeLimitCode(data, aliveTime, startTimeStr, sha1.New()) // TODO: this is only for the support of legacy codes, remove this in/after 1.23 - if subtle.ConstantTimeCompare([]byte(retCode), []byte(code)) != 1 { - return false - } - } - - // check time is expired or not: startTime <= now && now < startTime + minutes - startTime, _ := time.ParseInLocation("200601021504", startTimeStr, time.Local) - return (startTime.Before(now) || startTime.Equal(now)) && now.Before(startTime.Add(time.Minute*time.Duration(minutes))) -} - -// TimeLimitCodeLength default value for time limit code -const TimeLimitCodeLength = 12 + 6 + 40 - -// CreateTimeLimitCode create a time-limited code. -// Format: 12 length date time string + 6 minutes string (not used) + 40 hash string, some other code depends on this fixed length -// If h is nil, then use the default hmac hash. -func CreateTimeLimitCode[T time.Time | string](data string, minutes int, startTimeGeneric T, h hash.Hash) string { - const format = "200601021504" - - var start time.Time - var startTimeAny any = startTimeGeneric - if t, ok := startTimeAny.(time.Time); ok { - start = t - } else { - var err error - start, err = time.ParseInLocation(format, startTimeAny.(string), time.Local) - if err != nil { - return "" // return an invalid code because the "parse" failed - } - } - startStr := start.Format(format) - end := start.Add(time.Minute * time.Duration(minutes)) - - if h == nil { - h = hmac.New(sha1.New, setting.GetGeneralTokenSigningSecret()) - } - _, _ = fmt.Fprintf(h, "%s%s%s%s%d", data, hex.EncodeToString(setting.GetGeneralTokenSigningSecret()), startStr, end.Format(format), minutes) - encoded := hex.EncodeToString(h.Sum(nil)) - - code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded) - if len(code) != TimeLimitCodeLength { - panic("there is a hard requirement for the length of time-limited code") // it shouldn't happen - } - return code -} - // FileSize calculates the file size and generate user-friendly string. func FileSize(s int64) string { return humanize.IBytes(uint64(s)) diff --git a/modules/base/tool_test.go b/modules/base/tool_test.go index 81fd4b6a9e..ed1b469161 100644 --- a/modules/base/tool_test.go +++ b/modules/base/tool_test.go @@ -4,13 +4,7 @@ package base import ( - "crypto/sha1" - "fmt" "testing" - "time" - - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -46,57 +40,6 @@ func TestBasicAuthDecode(t *testing.T) { require.Error(t, err) } -func TestVerifyTimeLimitCode(t *testing.T) { - defer test.MockVariableValue(&setting.InstallLock, true)() - initGeneralSecret := func(secret string) { - setting.InstallLock = true - setting.CfgProvider, _ = setting.NewConfigProviderFromData(fmt.Sprintf(` -[oauth2] -JWT_SECRET = %s -`, secret)) - setting.LoadCommonSettings() - } - - initGeneralSecret("KZb_QLUd4fYVyxetjxC4eZkrBgWM2SndOOWDNtgUUko") - now := time.Now() - - t.Run("TestGenericParameter", func(t *testing.T) { - time2000 := time.Date(2000, 1, 2, 3, 4, 5, 0, time.Local) - assert.Equal(t, "2000010203040000026fa5221b2731b7cf80b1b506f5e39e38c115fee5", CreateTimeLimitCode("test-sha1", 2, time2000, sha1.New())) - assert.Equal(t, "2000010203040000026fa5221b2731b7cf80b1b506f5e39e38c115fee5", CreateTimeLimitCode("test-sha1", 2, "200001020304", sha1.New())) - assert.Equal(t, "2000010203040000024842227a2f87041ff82025199c0187410a9297bf", CreateTimeLimitCode("test-hmac", 2, time2000, nil)) - assert.Equal(t, "2000010203040000024842227a2f87041ff82025199c0187410a9297bf", CreateTimeLimitCode("test-hmac", 2, "200001020304", nil)) - }) - - t.Run("TestInvalidCode", func(t *testing.T) { - assert.False(t, VerifyTimeLimitCode(now, "data", 2, "")) - assert.False(t, VerifyTimeLimitCode(now, "data", 2, "invalid code")) - }) - - t.Run("TestCreateAndVerify", func(t *testing.T) { - code := CreateTimeLimitCode("data", 2, now, nil) - assert.False(t, VerifyTimeLimitCode(now.Add(-time.Minute), "data", 2, code)) // not started yet - assert.True(t, VerifyTimeLimitCode(now, "data", 2, code)) - assert.True(t, VerifyTimeLimitCode(now.Add(time.Minute), "data", 2, code)) - assert.False(t, VerifyTimeLimitCode(now.Add(time.Minute), "DATA", 2, code)) // invalid data - assert.False(t, VerifyTimeLimitCode(now.Add(2*time.Minute), "data", 2, code)) // expired - }) - - t.Run("TestDifferentSecret", func(t *testing.T) { - // use another secret to ensure the code is invalid for different secret - verifyDataCode := func(c string) bool { - return VerifyTimeLimitCode(now, "data", 2, c) - } - code1 := CreateTimeLimitCode("data", 2, now, sha1.New()) - code2 := CreateTimeLimitCode("data", 2, now, nil) - assert.True(t, verifyDataCode(code1)) - assert.True(t, verifyDataCode(code2)) - initGeneralSecret("000_QLUd4fYVyxetjxC4eZkrBgWM2SndOOWDNtgUUko") - assert.False(t, verifyDataCode(code1)) - assert.False(t, verifyDataCode(code2)) - }) -} - func TestFileSize(t *testing.T) { var size int64 = 512 assert.Equal(t, "512 B", FileSize(size)) diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index 1f5ebef7d7..941586db72 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -5,8 +5,6 @@ package auth import ( - "crypto/subtle" - "encoding/hex" "errors" "fmt" "net/http" @@ -63,38 +61,11 @@ func autoSignIn(ctx *context.Context) (bool, error) { return false, nil } - lookupKey, validator, found := strings.Cut(authCookie, ":") - if !found { - return false, nil - } - - authToken, err := auth.FindAuthToken(ctx, lookupKey) + u, err := user_model.VerifyUserAuthorizationToken(ctx, authCookie, auth.LongTermAuthorization, false) if err != nil { - if errors.Is(err, util.ErrNotExist) { - return false, nil - } - return false, err + return false, fmt.Errorf("VerifyUserAuthorizationToken: %w", err) } - - if authToken.IsExpired() { - err = auth.DeleteAuthToken(ctx, authToken) - return false, err - } - - rawValidator, err := hex.DecodeString(validator) - if err != nil { - return false, err - } - - if subtle.ConstantTimeCompare([]byte(authToken.HashedValidator), []byte(auth.HashValidator(rawValidator))) == 0 { - return false, nil - } - - u, err := user_model.GetUserByID(ctx, authToken.UID) - if err != nil { - if !user_model.IsErrUserNotExist(err) { - return false, fmt.Errorf("GetUserByID: %w", err) - } + if u == nil { return false, nil } @@ -633,7 +604,10 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth. return false } - mailer.SendActivateAccountMail(ctx.Locale, u) + if err := mailer.SendActivateAccountMail(ctx, u); err != nil { + ctx.ServerError("SendActivateAccountMail", err) + return false + } ctx.Data["IsSendRegisterMail"] = true ctx.Data["Email"] = u.Email @@ -674,7 +648,10 @@ func Activate(ctx *context.Context) { ctx.Data["ResendLimited"] = true } else { ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale) - mailer.SendActivateAccountMail(ctx.Locale, ctx.Doer) + if err := mailer.SendActivateAccountMail(ctx, ctx.Doer); err != nil { + ctx.ServerError("SendActivateAccountMail", err) + return + } if err := ctx.Cache.Put(cacheKey+ctx.Doer.LowerName, ctx.Doer.LowerName, 180); err != nil { log.Error("Set cache(MailResendLimit) fail: %v", err) @@ -687,7 +664,12 @@ func Activate(ctx *context.Context) { return } - user := user_model.VerifyUserActiveCode(ctx, code) + user, err := user_model.VerifyUserAuthorizationToken(ctx, code, auth.UserActivation, false) + if err != nil { + ctx.ServerError("VerifyUserAuthorizationToken", err) + return + } + // if code is wrong if user == nil { ctx.Data["IsCodeInvalid"] = true @@ -751,7 +733,12 @@ func ActivatePost(ctx *context.Context) { return } - user := user_model.VerifyUserActiveCode(ctx, code) + user, err := user_model.VerifyUserAuthorizationToken(ctx, code, auth.UserActivation, true) + if err != nil { + ctx.ServerError("VerifyUserAuthorizationToken", err) + return + } + // if code is wrong if user == nil { ctx.Data["IsCodeInvalid"] = true @@ -835,23 +822,32 @@ func ActivateEmail(ctx *context.Context) { code := ctx.FormString("code") emailStr := ctx.FormString("email") - // Verify code. - if email := user_model.VerifyActiveEmailCode(ctx, code, emailStr); email != nil { - if err := user_model.ActivateEmail(ctx, email); err != nil { - ctx.ServerError("ActivateEmail", err) - return - } - - log.Trace("Email activated: %s", email.Email) - ctx.Flash.Success(ctx.Tr("settings.add_email_success")) - - if u, err := user_model.GetUserByID(ctx, email.UID); err != nil { - log.Warn("GetUserByID: %d", email.UID) - } else { - // Allow user to validate more emails - _ = ctx.Cache.Delete("MailResendLimit_" + u.LowerName) - } + u, err := user_model.VerifyUserAuthorizationToken(ctx, code, auth.EmailActivation(emailStr), true) + if err != nil { + ctx.ServerError("VerifyUserAuthorizationToken", err) + return } + if u == nil { + ctx.Redirect(setting.AppSubURL + "/user/settings/account") + return + } + + email, err := user_model.GetEmailAddressOfUser(ctx, emailStr, u.ID) + if err != nil { + ctx.ServerError("GetEmailAddressOfUser", err) + return + } + + if err := user_model.ActivateEmail(ctx, email); err != nil { + ctx.ServerError("ActivateEmail", err) + return + } + + log.Trace("Email activated: %s", email.Email) + ctx.Flash.Success(ctx.Tr("settings.add_email_success")) + + // Allow user to validate more emails + _ = ctx.Cache.Delete("MailResendLimit_" + u.LowerName) // FIXME: e-mail verification does not require the user to be logged in, // so this could be redirecting to the login page. diff --git a/routers/web/auth/password.go b/routers/web/auth/password.go index d25bd682e2..363c01c6a8 100644 --- a/routers/web/auth/password.go +++ b/routers/web/auth/password.go @@ -86,7 +86,10 @@ func ForgotPasswdPost(ctx *context.Context) { return } - mailer.SendResetPasswordMail(u) + if err := mailer.SendResetPasswordMail(ctx, u); err != nil { + ctx.ServerError("SendResetPasswordMail", err) + return + } if err = ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil { log.Error("Set cache(MailResendLimit) fail: %v", err) @@ -97,7 +100,7 @@ func ForgotPasswdPost(ctx *context.Context) { ctx.HTML(http.StatusOK, tplForgotPassword) } -func commonResetPassword(ctx *context.Context) (*user_model.User, *auth.TwoFactor) { +func commonResetPassword(ctx *context.Context, shouldDeleteToken bool) (*user_model.User, *auth.TwoFactor) { code := ctx.FormString("code") ctx.Data["Title"] = ctx.Tr("auth.reset_password") @@ -113,7 +116,12 @@ func commonResetPassword(ctx *context.Context) (*user_model.User, *auth.TwoFacto } // Fail early, don't frustrate the user - u := user_model.VerifyUserActiveCode(ctx, code) + u, err := user_model.VerifyUserAuthorizationToken(ctx, code, auth.PasswordReset, shouldDeleteToken) + if err != nil { + ctx.ServerError("VerifyUserAuthorizationToken", err) + return nil, nil + } + if u == nil { ctx.Flash.Error(ctx.Tr("auth.invalid_code_forgot_password", fmt.Sprintf("%s/user/forgot_password", setting.AppSubURL)), true) return nil, nil @@ -145,7 +153,7 @@ func commonResetPassword(ctx *context.Context) (*user_model.User, *auth.TwoFacto func ResetPasswd(ctx *context.Context) { ctx.Data["IsResetForm"] = true - commonResetPassword(ctx) + commonResetPassword(ctx, false) if ctx.Written() { return } @@ -155,7 +163,7 @@ func ResetPasswd(ctx *context.Context) { // ResetPasswdPost response from account recovery request func ResetPasswdPost(ctx *context.Context) { - u, twofa := commonResetPassword(ctx) + u, twofa := commonResetPassword(ctx, true) if ctx.Written() { return } diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index 7e4d45e991..6f40e39c8d 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -155,9 +155,15 @@ func EmailPost(ctx *context.Context) { return } // Only fired when the primary email is inactive (Wrong state) - mailer.SendActivateAccountMail(ctx.Locale, ctx.Doer) + if err := mailer.SendActivateAccountMail(ctx, ctx.Doer); err != nil { + ctx.ServerError("SendActivateAccountMail", err) + return + } } else { - mailer.SendActivateEmailMail(ctx.Doer, email.Email) + if err := mailer.SendActivateEmailMail(ctx, ctx.Doer, email.Email); err != nil { + ctx.ServerError("SendActivateEmailMail", err) + return + } } address = email.Email @@ -218,7 +224,10 @@ func EmailPost(ctx *context.Context) { // Send confirmation email if setting.Service.RegisterEmailConfirm { - mailer.SendActivateEmailMail(ctx.Doer, form.Email) + if err := mailer.SendActivateEmailMail(ctx, ctx.Doer, form.Email); err != nil { + ctx.ServerError("SendActivateEmailMail", err) + return + } if err := ctx.Cache.Put("MailResendLimit_"+ctx.Doer.LowerName, ctx.Doer.LowerName, 180); err != nil { log.Error("Set cache(MailResendLimit) fail: %v", err) } diff --git a/services/context/context_cookie.go b/services/context/context_cookie.go index 39e3218d1b..3699f81071 100644 --- a/services/context/context_cookie.go +++ b/services/context/context_cookie.go @@ -47,7 +47,7 @@ func (ctx *Context) GetSiteCookie(name string) string { // SetLTACookie will generate a LTA token and add it as an cookie. func (ctx *Context) SetLTACookie(u *user_model.User) error { days := 86400 * setting.LogInRememberDays - lookup, validator, err := auth_model.GenerateAuthToken(ctx, u.ID, timeutil.TimeStampNow().Add(int64(days))) + lookup, validator, err := auth_model.GenerateAuthToken(ctx, u.ID, timeutil.TimeStampNow().Add(int64(days)), auth_model.LongTermAuthorization) if err != nil { return err } diff --git a/services/doctor/dbconsistency.go b/services/doctor/dbconsistency.go index 7fce505d52..7207c3ff50 100644 --- a/services/doctor/dbconsistency.go +++ b/services/doctor/dbconsistency.go @@ -243,6 +243,9 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er // find archive download count without existing release genericOrphanCheck("Archive download count without existing Release", "repo_archive_download_count", "release", "repo_archive_download_count.release_id=release.id"), + // find authorization tokens without existing user + genericOrphanCheck("Authorization token without existing User", + "forgejo_auth_token", "user", "forgejo_auth_token.uid=user.id"), ) for _, c := range consistencyChecks { diff --git a/services/mailer/mail.go b/services/mailer/mail.go index 01ab84bcf5..bfede28bbe 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -70,7 +70,7 @@ func SendTestMail(email string) error { } // sendUserMail sends a mail to the user -func sendUserMail(language string, u *user_model.User, tpl base.TplName, code, subject, info string) { +func sendUserMail(language string, u *user_model.User, tpl base.TplName, code, subject, info string) error { locale := translation.NewLocale(language) data := map[string]any{ "locale": locale, @@ -84,47 +84,66 @@ func sendUserMail(language string, u *user_model.User, tpl base.TplName, code, s var content bytes.Buffer if err := bodyTemplates.ExecuteTemplate(&content, string(tpl), data); err != nil { - log.Error("Template: %v", err) - return + return err } msg := NewMessage(u.EmailTo(), subject, content.String()) msg.Info = fmt.Sprintf("UID: %d, %s", u.ID, info) SendAsync(msg) + return nil } // SendActivateAccountMail sends an activation mail to the user (new user registration) -func SendActivateAccountMail(locale translation.Locale, u *user_model.User) { +func SendActivateAccountMail(ctx context.Context, u *user_model.User) error { if setting.MailService == nil { // No mail service configured - return + return nil } - sendUserMail(locale.Language(), u, mailAuthActivate, u.GenerateEmailActivateCode(u.Email), locale.TrString("mail.activate_account"), "activate account") + + locale := translation.NewLocale(u.Language) + code, err := u.GenerateEmailAuthorizationCode(ctx, auth_model.UserActivation) + if err != nil { + return err + } + + return sendUserMail(locale.Language(), u, mailAuthActivate, code, locale.TrString("mail.activate_account"), "activate account") } // SendResetPasswordMail sends a password reset mail to the user -func SendResetPasswordMail(u *user_model.User) { +func SendResetPasswordMail(ctx context.Context, u *user_model.User) error { if setting.MailService == nil { // No mail service configured - return + return nil } + locale := translation.NewLocale(u.Language) - sendUserMail(u.Language, u, mailAuthResetPassword, u.GenerateEmailActivateCode(u.Email), locale.TrString("mail.reset_password"), "recover account") + code, err := u.GenerateEmailAuthorizationCode(ctx, auth_model.PasswordReset) + if err != nil { + return err + } + + return sendUserMail(u.Language, u, mailAuthResetPassword, code, locale.TrString("mail.reset_password"), "recover account") } // SendActivateEmailMail sends confirmation email to confirm new email address -func SendActivateEmailMail(u *user_model.User, email string) { +func SendActivateEmailMail(ctx context.Context, u *user_model.User, email string) error { if setting.MailService == nil { // No mail service configured - return + return nil } + locale := translation.NewLocale(u.Language) + code, err := u.GenerateEmailAuthorizationCode(ctx, auth_model.EmailActivation(email)) + if err != nil { + return err + } + data := map[string]any{ "locale": locale, "DisplayName": u.DisplayName(), "ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, locale), - "Code": u.GenerateEmailActivateCode(email), + "Code": code, "Email": email, "Language": locale.Language(), } @@ -132,14 +151,14 @@ func SendActivateEmailMail(u *user_model.User, email string) { var content bytes.Buffer if err := bodyTemplates.ExecuteTemplate(&content, string(mailAuthActivateEmail), data); err != nil { - log.Error("Template: %v", err) - return + return err } msg := NewMessage(email, locale.TrString("mail.activate_email"), content.String()) msg.Info = fmt.Sprintf("UID: %d, activate email", u.ID) SendAsync(msg) + return nil } // SendRegisterNotifyMail triggers a notify e-mail by admin created a account. diff --git a/services/user/delete.go b/services/user/delete.go index 74dbc09b82..587e3c2a8f 100644 --- a/services/user/delete.go +++ b/services/user/delete.go @@ -96,6 +96,7 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error) &user_model.BlockedUser{BlockID: u.ID}, &user_model.BlockedUser{UserID: u.ID}, &actions_model.ActionRunnerToken{OwnerID: u.ID}, + &auth_model.AuthorizationToken{UID: u.ID}, ); err != nil { return fmt.Errorf("deleteBeans: %w", err) } diff --git a/tests/integration/auth_token_test.go b/tests/integration/auth_token_test.go index 2c39c87da2..d1fd5dda83 100644 --- a/tests/integration/auth_token_test.go +++ b/tests/integration/auth_token_test.go @@ -84,7 +84,7 @@ func TestLTACookie(t *testing.T) { assert.True(t, found) rawValidator, err := hex.DecodeString(validator) require.NoError(t, err) - unittest.AssertExistsAndLoadBean(t, &auth.AuthorizationToken{LookupKey: lookupKey, HashedValidator: auth.HashValidator(rawValidator), UID: user.ID}) + unittest.AssertExistsAndLoadBean(t, &auth.AuthorizationToken{LookupKey: lookupKey, HashedValidator: auth.HashValidator(rawValidator), UID: user.ID, Purpose: auth.LongTermAuthorization}) // Check if the LTA cookie it provides authentication. // If LTA cookie provides authentication /user/login shouldn't return status 200. @@ -143,7 +143,7 @@ func TestLTAExpiry(t *testing.T) { assert.True(t, found) // Ensure it's not expired. - lta := unittest.AssertExistsAndLoadBean(t, &auth.AuthorizationToken{UID: user.ID, LookupKey: lookupKey}) + lta := unittest.AssertExistsAndLoadBean(t, &auth.AuthorizationToken{UID: user.ID, LookupKey: lookupKey, Purpose: auth.LongTermAuthorization}) assert.False(t, lta.IsExpired()) // Manually stub LTA's expiry. @@ -151,7 +151,7 @@ func TestLTAExpiry(t *testing.T) { require.NoError(t, err) // Ensure it's expired. - lta = unittest.AssertExistsAndLoadBean(t, &auth.AuthorizationToken{UID: user.ID, LookupKey: lookupKey}) + lta = unittest.AssertExistsAndLoadBean(t, &auth.AuthorizationToken{UID: user.ID, LookupKey: lookupKey, Purpose: auth.LongTermAuthorization}) assert.True(t, lta.IsExpired()) // Should return 200 OK, because LTA doesn't provide authorization anymore. @@ -160,5 +160,5 @@ func TestLTAExpiry(t *testing.T) { session.MakeRequest(t, req, http.StatusOK) // Ensure it's deleted. - unittest.AssertNotExistsBean(t, &auth.AuthorizationToken{UID: user.ID, LookupKey: lookupKey}) + unittest.AssertNotExistsBean(t, &auth.AuthorizationToken{UID: user.ID, LookupKey: lookupKey, Purpose: auth.LongTermAuthorization}) } diff --git a/tests/integration/org_team_invite_test.go b/tests/integration/org_team_invite_test.go index d04199a2c1..2fe296e8c3 100644 --- a/tests/integration/org_team_invite_test.go +++ b/tests/integration/org_team_invite_test.go @@ -10,6 +10,7 @@ import ( "strings" "testing" + "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/unittest" @@ -293,8 +294,10 @@ func TestOrgTeamEmailInviteRedirectsNewUserWithActivation(t *testing.T) { require.NoError(t, err) session.jar.SetCookies(baseURL, cr.Cookies()) - activateURL := fmt.Sprintf("/user/activate?code=%s", user.GenerateEmailActivateCode("doesnotexist@example.com")) - req = NewRequestWithValues(t, "POST", activateURL, map[string]string{ + code, err := user.GenerateEmailAuthorizationCode(db.DefaultContext, auth.UserActivation) + require.NoError(t, err) + + req = NewRequestWithValues(t, "POST", "/user/activate?code="+url.QueryEscape(code), map[string]string{ "password": "examplePassword!1", }) diff --git a/tests/integration/user_test.go b/tests/integration/user_test.go index f706f7e755..d2b5f112a3 100644 --- a/tests/integration/user_test.go +++ b/tests/integration/user_test.go @@ -5,14 +5,18 @@ package integration import ( + "bytes" + "encoding/hex" "fmt" "net/http" + "net/url" "strconv" "strings" "testing" "time" auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" @@ -836,3 +840,171 @@ func TestUserRepos(t *testing.T) { } } } + +func TestUserActivate(t *testing.T) { + defer tests.PrepareTestEnv(t)() + defer test.MockVariableValue(&setting.Service.RegisterEmailConfirm, true)() + + called := false + code := "" + defer test.MockVariableValue(&mailer.SendAsync, func(msgs ...*mailer.Message) { + called = true + assert.Len(t, msgs, 1) + assert.Equal(t, `"doesnotexist" `, msgs[0].To) + assert.EqualValues(t, translation.NewLocale("en-US").Tr("mail.activate_account"), msgs[0].Subject) + + messageDoc := NewHTMLParser(t, bytes.NewBuffer([]byte(msgs[0].Body))) + link, ok := messageDoc.Find("a").Attr("href") + assert.True(t, ok) + u, err := url.Parse(link) + require.NoError(t, err) + code = u.Query()["code"][0] + })() + + session := emptyTestSession(t) + req := NewRequestWithValues(t, "POST", "/user/sign_up", map[string]string{ + "_csrf": GetCSRF(t, session, "/user/sign_up"), + "user_name": "doesnotexist", + "email": "doesnotexist@example.com", + "password": "examplePassword!1", + "retype": "examplePassword!1", + }) + session.MakeRequest(t, req, http.StatusOK) + assert.True(t, called) + + queryCode, err := url.QueryUnescape(code) + require.NoError(t, err) + + lookupKey, validator, ok := strings.Cut(queryCode, ":") + assert.True(t, ok) + + rawValidator, err := hex.DecodeString(validator) + require.NoError(t, err) + + authToken, err := auth_model.FindAuthToken(db.DefaultContext, lookupKey, auth_model.UserActivation) + require.NoError(t, err) + assert.False(t, authToken.IsExpired()) + assert.EqualValues(t, authToken.HashedValidator, auth_model.HashValidator(rawValidator)) + + req = NewRequest(t, "POST", "/user/activate?code="+code) + session.MakeRequest(t, req, http.StatusOK) + + unittest.AssertNotExistsBean(t, &auth_model.AuthorizationToken{ID: authToken.ID}) + unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "doesnotexist", IsActive: true}) +} + +func TestUserPasswordReset(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + called := false + code := "" + defer test.MockVariableValue(&mailer.SendAsync, func(msgs ...*mailer.Message) { + if called { + return + } + called = true + + assert.Len(t, msgs, 1) + assert.Equal(t, user2.EmailTo(), msgs[0].To) + assert.EqualValues(t, translation.NewLocale("en-US").Tr("mail.reset_password"), msgs[0].Subject) + + messageDoc := NewHTMLParser(t, bytes.NewBuffer([]byte(msgs[0].Body))) + link, ok := messageDoc.Find("a").Attr("href") + assert.True(t, ok) + u, err := url.Parse(link) + require.NoError(t, err) + code = u.Query()["code"][0] + })() + + session := emptyTestSession(t) + req := NewRequestWithValues(t, "POST", "/user/forgot_password", map[string]string{ + "_csrf": GetCSRF(t, session, "/user/forgot_password"), + "email": user2.Email, + }) + session.MakeRequest(t, req, http.StatusOK) + assert.True(t, called) + + queryCode, err := url.QueryUnescape(code) + require.NoError(t, err) + + lookupKey, validator, ok := strings.Cut(queryCode, ":") + assert.True(t, ok) + + rawValidator, err := hex.DecodeString(validator) + require.NoError(t, err) + + authToken, err := auth_model.FindAuthToken(db.DefaultContext, lookupKey, auth_model.PasswordReset) + require.NoError(t, err) + assert.False(t, authToken.IsExpired()) + assert.EqualValues(t, authToken.HashedValidator, auth_model.HashValidator(rawValidator)) + + req = NewRequestWithValues(t, "POST", "/user/recover_account", map[string]string{ + "_csrf": GetCSRF(t, session, "/user/recover_account"), + "code": code, + "password": "new_password", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + + unittest.AssertNotExistsBean(t, &auth_model.AuthorizationToken{ID: authToken.ID}) + assert.True(t, unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).ValidatePassword("new_password")) +} + +func TestActivateEmailAddress(t *testing.T) { + defer tests.PrepareTestEnv(t)() + defer test.MockVariableValue(&setting.Service.RegisterEmailConfirm, true)() + + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + called := false + code := "" + defer test.MockVariableValue(&mailer.SendAsync, func(msgs ...*mailer.Message) { + if called { + return + } + called = true + + assert.Len(t, msgs, 1) + assert.Equal(t, "newemail@example.org", msgs[0].To) + assert.EqualValues(t, translation.NewLocale("en-US").Tr("mail.activate_email"), msgs[0].Subject) + + messageDoc := NewHTMLParser(t, bytes.NewBuffer([]byte(msgs[0].Body))) + link, ok := messageDoc.Find("a").Attr("href") + assert.True(t, ok) + u, err := url.Parse(link) + require.NoError(t, err) + code = u.Query()["code"][0] + })() + + session := loginUser(t, user2.Name) + req := NewRequestWithValues(t, "POST", "/user/settings/account/email", map[string]string{ + "_csrf": GetCSRF(t, session, "/user/settings"), + "email": "newemail@example.org", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + assert.True(t, called) + + queryCode, err := url.QueryUnescape(code) + require.NoError(t, err) + + lookupKey, validator, ok := strings.Cut(queryCode, ":") + assert.True(t, ok) + + rawValidator, err := hex.DecodeString(validator) + require.NoError(t, err) + + authToken, err := auth_model.FindAuthToken(db.DefaultContext, lookupKey, auth_model.EmailActivation("newemail@example.org")) + require.NoError(t, err) + assert.False(t, authToken.IsExpired()) + assert.EqualValues(t, authToken.HashedValidator, auth_model.HashValidator(rawValidator)) + + req = NewRequestWithValues(t, "POST", "/user/activate_email", map[string]string{ + "code": code, + "email": "newemail@example.org", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + + unittest.AssertNotExistsBean(t, &auth_model.AuthorizationToken{ID: authToken.ID}) + unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{UID: user2.ID, IsActivated: true, Email: "newemail@example.org"}) +} From 9508aa7713632ed40124a933d91d5766cf2369c2 Mon Sep 17 00:00:00 2001 From: Gusted Date: Mon, 12 Aug 2024 20:01:09 +0200 Subject: [PATCH 11/49] Improve usage of HMAC output for mailer tokens - If the incoming mail feature is enabled, tokens are being sent with outgoing mails. These tokens contains information about what type of action is allow with such token (such as replying to a certain issue ID), to verify these tokens the code uses the HMAC-SHA256 construction. - The output of the HMAC is truncated to 80 bits, because this is recommended by RFC2104, but RFC2104 actually doesn't recommend this. It recommends, if truncation should need to take place, it should use max(80, hash_len/2) of the leftmost bits. For HMAC-SHA256 this works out to 128 bits instead of the currently used 80 bits. - Update to token version 2 and disallow any usage of token version 1, token version 2 are generated with 128 bits of HMAC output. - Add test to verify the deprecation of token version 1 and a general MAC check test. --- services/mailer/token/token.go | 16 +++++++-- tests/integration/incoming_email_test.go | 46 ++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/services/mailer/token/token.go b/services/mailer/token/token.go index 8a5a762d6b..1a52bce803 100644 --- a/services/mailer/token/token.go +++ b/services/mailer/token/token.go @@ -22,9 +22,16 @@ import ( // // The payload is verifiable by the generated HMAC using the user secret. It contains: // | Timestamp | Action/Handler Type | Action/Handler Data | +// +// +// Version changelog +// +// v1 -> v2: +// Use 128 instead of 80 bits of the HMAC-SHA256 output. const ( tokenVersion1 byte = 1 + tokenVersion2 byte = 2 tokenLifetimeInYears int = 1 ) @@ -70,7 +77,7 @@ func CreateToken(ht HandlerType, user *user_model.User, data []byte) (string, er return "", err } - return encodingWithoutPadding.EncodeToString(append([]byte{tokenVersion1}, packagedData...)), nil + return encodingWithoutPadding.EncodeToString(append([]byte{tokenVersion2}, packagedData...)), nil } // ExtractToken extracts the action/user tuple from the token and verifies the content @@ -84,7 +91,7 @@ func ExtractToken(ctx context.Context, token string) (HandlerType, *user_model.U return UnknownHandlerType, nil, nil, &ErrToken{"no data"} } - if data[0] != tokenVersion1 { + if data[0] != tokenVersion2 { return UnknownHandlerType, nil, nil, &ErrToken{fmt.Sprintf("unsupported token version: %v", data[0])} } @@ -124,5 +131,8 @@ func generateHmac(secret, payload []byte) []byte { mac.Write(payload) hmac := mac.Sum(nil) - return hmac[:10] // RFC2104 recommends not using less then 80 bits + // RFC2104 section 5 recommends that if you do HMAC truncation, you should use + // the max(80, hash_len/2) of the leftmost bits. + // For SHA256 this works out to using 128 of the leftmost bits. + return hmac[:16] } diff --git a/tests/integration/incoming_email_test.go b/tests/integration/incoming_email_test.go index fdc0425b54..66f833b28d 100644 --- a/tests/integration/incoming_email_test.go +++ b/tests/integration/incoming_email_test.go @@ -4,6 +4,7 @@ package integration import ( + "encoding/base32" "io" "net" "net/smtp" @@ -75,6 +76,51 @@ func TestIncomingEmail(t *testing.T) { assert.Equal(t, payload, p) }) + tokenEncoding := base32.StdEncoding.WithPadding(base32.NoPadding) + t.Run("Deprecated token version", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + payload := []byte{1, 2, 3, 4, 5} + + token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload) + require.NoError(t, err) + assert.NotEmpty(t, token) + + // Set the token to version 1. + unencodedToken, err := tokenEncoding.DecodeString(token) + require.NoError(t, err) + unencodedToken[0] = 1 + token = tokenEncoding.EncodeToString(unencodedToken) + + ht, u, p, err := token_service.ExtractToken(db.DefaultContext, token) + require.ErrorContains(t, err, "unsupported token version: 1") + assert.Equal(t, token_service.UnknownHandlerType, ht) + assert.Nil(t, u) + assert.Nil(t, p) + }) + + t.Run("MAC check", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + payload := []byte{1, 2, 3, 4, 5} + + token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload) + require.NoError(t, err) + assert.NotEmpty(t, token) + + // Modify the MAC. + unencodedToken, err := tokenEncoding.DecodeString(token) + require.NoError(t, err) + unencodedToken[len(unencodedToken)-1] ^= 0x01 + token = tokenEncoding.EncodeToString(unencodedToken) + + ht, u, p, err := token_service.ExtractToken(db.DefaultContext, token) + require.ErrorContains(t, err, "verification failed") + assert.Equal(t, token_service.UnknownHandlerType, ht) + assert.Nil(t, u) + assert.Nil(t, p) + }) + t.Run("Handler", func(t *testing.T) { t.Run("Reply", func(t *testing.T) { checkReply := func(t *testing.T, payload []byte, issue *issues_model.Issue, commentType issues_model.CommentType) { From b70196653f9d7d3b9d4e72d114e5cc6f472988c4 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 9 Nov 2024 23:47:39 +0100 Subject: [PATCH 12/49] fix: anomynous users code search for private/limited user's repository - Consider private/limited users in the `AccessibleRepositoryCondition` query, previously this only considered private/limited organization. This limits the ability for anomynous users to do code search on private/limited user's repository - Unit test added. --- .../repository.yml | 30 +++++++++++++ models/repo/repo_list.go | 7 +-- models/repo/repo_list_test.go | 45 +++++++++++++++++++ 3 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 models/repo/TestSearchRepositoryIDsByCondition/repository.yml diff --git a/models/repo/TestSearchRepositoryIDsByCondition/repository.yml b/models/repo/TestSearchRepositoryIDsByCondition/repository.yml new file mode 100644 index 0000000000..9ce830783d --- /dev/null +++ b/models/repo/TestSearchRepositoryIDsByCondition/repository.yml @@ -0,0 +1,30 @@ +- + id: 1001 + owner_id: 33 + owner_name: user33 + lower_name: repo1001 + name: repo1001 + default_branch: main + num_watches: 0 + num_stars: 0 + num_forks: 0 + num_issues: 0 + num_closed_issues: 0 + num_pulls: 0 + num_closed_pulls: 0 + num_milestones: 0 + num_closed_milestones: 0 + num_projects: 0 + num_closed_projects: 0 + is_private: false + is_empty: false + is_archived: false + is_mirror: false + status: 0 + is_fork: false + fork_id: 0 + is_template: false + template_id: 0 + size: 0 + is_fsck_enabled: true + close_issues_via_commit_in_any_branch: false diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go index 162f933fbe..fc51f64f6a 100644 --- a/models/repo/repo_list.go +++ b/models/repo/repo_list.go @@ -641,12 +641,9 @@ func AccessibleRepositoryCondition(user *user_model.User, unitType unit.Type) bu // 1. Be able to see all non-private repositories that either: cond = cond.Or(builder.And( builder.Eq{"`repository`.is_private": false}, - // 2. Aren't in an private organisation or limited organisation if we're not logged in + // 2. Aren't in an private organisation/user or limited organisation/user if the doer is not logged in. builder.NotIn("`repository`.owner_id", builder.Select("id").From("`user`").Where( - builder.And( - builder.Eq{"type": user_model.UserTypeOrganization}, - builder.In("visibility", orgVisibilityLimit)), - )))) + builder.In("visibility", orgVisibilityLimit))))) } if user != nil { diff --git a/models/repo/repo_list_test.go b/models/repo/repo_list_test.go index b31aa1780f..8c13f387ba 100644 --- a/models/repo/repo_list_test.go +++ b/models/repo/repo_list_test.go @@ -4,13 +4,18 @@ package repo_test import ( + "path/filepath" + "slices" "strings" "testing" "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/optional" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -403,3 +408,43 @@ func TestSearchRepositoryByTopicName(t *testing.T) { }) } } + +func TestSearchRepositoryIDsByCondition(t *testing.T) { + defer unittest.OverrideFixtures( + unittest.FixturesOptions{ + Dir: filepath.Join(setting.AppWorkPath, "models/fixtures/"), + Base: setting.AppWorkPath, + Dirs: []string{"models/repo/TestSearchRepositoryIDsByCondition/"}, + }, + )() + require.NoError(t, unittest.PrepareTestDatabase()) + // Sanity check of the database + limitedUser := unittest.AssertExistsAndLoadBean(t, &user.User{ID: 33, Visibility: structs.VisibleTypeLimited}) + unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1001, OwnerID: limitedUser.ID}) + + testCases := []struct { + user *user.User + repoIDs []int64 + }{ + { + user: nil, + repoIDs: []int64{1, 4, 8, 9, 10, 11, 12, 14, 17, 18, 21, 23, 25, 27, 29, 32, 33, 34, 35, 36, 37, 42, 44, 45, 46, 47, 48, 49, 50, 51, 53, 57, 58, 60, 61, 62, 1059}, + }, + { + user: unittest.AssertExistsAndLoadBean(t, &user.User{ID: 4}), + repoIDs: []int64{1, 3, 4, 8, 9, 10, 11, 12, 14, 17, 18, 21, 23, 25, 27, 29, 32, 33, 34, 35, 36, 37, 38, 40, 42, 44, 45, 46, 47, 48, 49, 50, 51, 53, 57, 58, 60, 61, 62, 1001, 1059}, + }, + { + user: unittest.AssertExistsAndLoadBean(t, &user.User{ID: 5}), + repoIDs: []int64{1, 4, 8, 9, 10, 11, 12, 14, 17, 18, 21, 23, 25, 27, 29, 32, 33, 34, 35, 36, 37, 38, 40, 42, 44, 45, 46, 47, 48, 49, 50, 51, 53, 57, 58, 60, 61, 62, 1001, 1059}, + }, + } + + for _, testCase := range testCases { + repoIDs, err := repo_model.FindUserCodeAccessibleRepoIDs(db.DefaultContext, testCase.user) + require.NoError(t, err) + + slices.Sort(repoIDs) + assert.EqualValues(t, testCase.repoIDs, repoIDs) + } +} From e6bbecb02d47730d3cc630d419fe27ef2fb5cb39 Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 29 Oct 2024 18:01:42 +0100 Subject: [PATCH 13/49] fix: disallow basic authorization when security keys are enrolled - This unifies the security behavior of enrolling security keys with enrolling TOTP as a 2FA method. When TOTP is enrolled, you cannot use basic authorization (user:password) to make API request on behalf of the user, this is now also the case when you enroll security keys. - The usage of access tokens are the only method to make API requests on behalf of the user when a 2FA method is enrolled for the user. - Integration test added. --- services/auth/basic.go | 11 +++++++++++ tests/integration/api_twofa_test.go | 22 ++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/services/auth/basic.go b/services/auth/basic.go index 382c8bc90c..d489164954 100644 --- a/services/auth/basic.go +++ b/services/auth/basic.go @@ -5,6 +5,7 @@ package auth import ( + "errors" "net/http" "strings" @@ -132,6 +133,16 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore return nil, err } + hashWebAuthn, err := auth_model.HasWebAuthnRegistrationsByUID(req.Context(), u.ID) + if err != nil { + log.Error("HasWebAuthnRegistrationsByUID: %v", err) + return nil, err + } + + if hashWebAuthn { + return nil, errors.New("Basic authorization is not allowed while having security keys enrolled") + } + if skipper, ok := source.Cfg.(LocalTwoFASkipper); !ok || !skipper.IsSkipLocalTwoFA() { if err := validateTOTP(req, u); err != nil { return nil, err diff --git a/tests/integration/api_twofa_test.go b/tests/integration/api_twofa_test.go index 0bb20255a2..fb1d2badfc 100644 --- a/tests/integration/api_twofa_test.go +++ b/tests/integration/api_twofa_test.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/tests" "github.com/pquerna/otp/totp" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -58,3 +59,24 @@ func TestAPITwoFactor(t *testing.T) { req.Header.Set("X-Forgejo-OTP", passcode) MakeRequest(t, req, http.StatusOK) } + +func TestAPIWebAuthn(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 32}) + unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{UserID: user.ID}) + + req := NewRequest(t, "GET", "/api/v1/user") + req.SetBasicAuth(user.Name, "notpassword") + + resp := MakeRequest(t, req, http.StatusUnauthorized) + + type userResponse struct { + Message string `json:"message"` + } + var userParsed userResponse + + DecodeJSON(t, resp, &userParsed) + + assert.EqualValues(t, "Basic authorization is not allowed while having security keys enrolled", userParsed.Message) +} From 7067cc7da4f144cc8a2fd2ae6e5307e0465ace7f Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 2 Nov 2024 14:08:04 +0100 Subject: [PATCH 14/49] fix: strict matching of allowed content for sanitizer - _Simply_ add `^$` to regexp that didn't had it yet, this avoids any content being allowed that simply had the allowed content as a substring. - Fix file-preview regex to have `$` instead of `*`. --- modules/markup/sanitizer.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go index 53ccdfab0d..72d6571e4e 100644 --- a/modules/markup/sanitizer.go +++ b/modules/markup/sanitizer.go @@ -97,7 +97,7 @@ func createDefaultPolicy() *bluemonday.Policy { policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(ref-issue( ref-external-issue)?|mention)$`)).OnElements("a") // Allow classes for task lists - policy.AllowAttrs("class").Matching(regexp.MustCompile(`task-list-item`)).OnElements("li") + policy.AllowAttrs("class").Matching(regexp.MustCompile(`^task-list-item$`)).OnElements("li") // Allow classes for org mode list item status. policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(unchecked|checked|indeterminate)$`)).OnElements("li") @@ -106,7 +106,7 @@ func createDefaultPolicy() *bluemonday.Policy { policy.AllowAttrs("class").Matching(regexp.MustCompile(`^icon(\s+[\p{L}\p{N}_-]+)+$`)).OnElements("i") // Allow classes for emojis - policy.AllowAttrs("class").Matching(regexp.MustCompile(`emoji`)).OnElements("img") + policy.AllowAttrs("class").Matching(regexp.MustCompile(`^emoji$`)).OnElements("img") // Allow icons, emojis, chroma syntax and keyword markup on span policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji)|(language-math display)|(language-math inline))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span") @@ -123,13 +123,13 @@ func createDefaultPolicy() *bluemonday.Policy { policy.AllowAttrs("class").Matching(regexp.MustCompile("^header$")).OnElements("div") policy.AllowAttrs("data-line-number").Matching(regexp.MustCompile("^[0-9]+$")).OnElements("span") policy.AllowAttrs("class").Matching(regexp.MustCompile("^text small grey$")).OnElements("span") - policy.AllowAttrs("class").Matching(regexp.MustCompile("^file-preview*")).OnElements("table") + policy.AllowAttrs("class").Matching(regexp.MustCompile("^file-preview$")).OnElements("table") policy.AllowAttrs("class").Matching(regexp.MustCompile("^lines-escape$")).OnElements("td") policy.AllowAttrs("class").Matching(regexp.MustCompile("^toggle-escape-button btn interact-bg$")).OnElements("button") policy.AllowAttrs("title").OnElements("button") policy.AllowAttrs("class").Matching(regexp.MustCompile("^ambiguous-code-point$")).OnElements("span") policy.AllowAttrs("data-tooltip-content").OnElements("span") - policy.AllowAttrs("class").Matching(regexp.MustCompile("muted|(text black)")).OnElements("a") + policy.AllowAttrs("class").Matching(regexp.MustCompile("^muted|(text black)$")).OnElements("a") policy.AllowAttrs("class").Matching(regexp.MustCompile("^ui warning message tw-text-left$")).OnElements("div") // Allow generally safe attributes From 3e3ef76808100cb1c853378733d0f6a910324ac6 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 2 Nov 2024 17:41:34 +0100 Subject: [PATCH 15/49] fix: require code permissions for branch feed - The RSS and atom feed for branches exposes details about the code, it therefore should be guarded by the requirement that the doer has access to the code of that repository. - Added integration testing. --- routers/web/web.go | 6 +- tests/integration/api_feed_user_test.go | 20 +++++ tests/integration/fixtures/TestFeed/team.yml | 21 +++++ .../fixtures/TestFeed/team_repo.yml | 11 +++ .../fixtures/TestFeed/team_unit.yml | 83 +++++++++++++++++++ .../fixtures/TestFeed/team_user.yml | 11 +++ 6 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 tests/integration/fixtures/TestFeed/team.yml create mode 100644 tests/integration/fixtures/TestFeed/team_repo.yml create mode 100644 tests/integration/fixtures/TestFeed/team_unit.yml create mode 100644 tests/integration/fixtures/TestFeed/team_user.yml diff --git a/routers/web/web.go b/routers/web/web.go index 34880bdda1..ecdd5d8d92 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1562,8 +1562,10 @@ func registerRoutes(m *web.Route) { m.Get("/cherry-pick/{sha:([a-f0-9]{4,64})$}", repo.SetEditorconfigIfExists, repo.CherryPick) }, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) - m.Get("/rss/branch/*", repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed("rss")) - m.Get("/atom/branch/*", repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed("atom")) + m.Group("", func() { + m.Get("/rss/branch/*", feed.RenderBranchFeed("rss")) + m.Get("/atom/branch/*", feed.RenderBranchFeed("atom")) + }, repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefBranch), reqRepoCodeReader, feedEnabled) m.Group("/src", func() { m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home) diff --git a/tests/integration/api_feed_user_test.go b/tests/integration/api_feed_user_test.go index 3fa9b86150..e0e5faed1b 100644 --- a/tests/integration/api_feed_user_test.go +++ b/tests/integration/api_feed_user_test.go @@ -109,4 +109,24 @@ func TestFeed(t *testing.T) { }) }) }) + + t.Run("View permission", func(t *testing.T) { + t.Run("Anomynous", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + req := NewRequest(t, "GET", "/org3/repo3/rss/branch/master") + MakeRequest(t, req, http.StatusNotFound) + }) + t.Run("No code permission", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + session := loginUser(t, "user8") + req := NewRequest(t, "GET", "/org3/repo3/rss/branch/master") + session.MakeRequest(t, req, http.StatusNotFound) + }) + t.Run("With code permission", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + session := loginUser(t, "user9") + req := NewRequest(t, "GET", "/org3/repo3/rss/branch/master") + session.MakeRequest(t, req, http.StatusOK) + }) + }) } diff --git a/tests/integration/fixtures/TestFeed/team.yml b/tests/integration/fixtures/TestFeed/team.yml new file mode 100644 index 0000000000..da27ac7c0c --- /dev/null +++ b/tests/integration/fixtures/TestFeed/team.yml @@ -0,0 +1,21 @@ +- + id: 1001 + org_id: 3 + lower_name: no_code + name: no_code + authorize: 1 # read + num_repos: 1 + num_members: 1 + includes_all_repositories: false + can_create_org_repo: false + +- + id: 1002 + org_id: 3 + lower_name: read_code + name: no_code + authorize: 1 # read + num_repos: 1 + num_members: 1 + includes_all_repositories: false + can_create_org_repo: false diff --git a/tests/integration/fixtures/TestFeed/team_repo.yml b/tests/integration/fixtures/TestFeed/team_repo.yml new file mode 100644 index 0000000000..922d1ef51e --- /dev/null +++ b/tests/integration/fixtures/TestFeed/team_repo.yml @@ -0,0 +1,11 @@ +- + id: 1001 + org_id: 3 + team_id: 1001 + repo_id: 3 + +- + id: 1002 + org_id: 3 + team_id: 1002 + repo_id: 3 diff --git a/tests/integration/fixtures/TestFeed/team_unit.yml b/tests/integration/fixtures/TestFeed/team_unit.yml new file mode 100644 index 0000000000..9fcb4396dc --- /dev/null +++ b/tests/integration/fixtures/TestFeed/team_unit.yml @@ -0,0 +1,83 @@ +- + id: 1001 + team_id: 1001 + type: 1 + access_mode: 0 + +- + id: 1002 + team_id: 1001 + type: 2 + access_mode: 1 + +- + id: 1003 + team_id: 1001 + type: 3 + access_mode: 1 + +- + id: 1004 + team_id: 1001 + type: 4 + access_mode: 1 + +- + id: 1005 + team_id: 1001 + type: 5 + access_mode: 1 + +- + id: 1006 + team_id: 1001 + type: 6 + access_mode: 1 + +- + id: 1007 + team_id: 1001 + type: 7 + access_mode: 1 + +- + id: 1008 + team_id: 1002 + type: 1 + access_mode: 1 + +- + id: 1009 + team_id: 1002 + type: 2 + access_mode: 1 + +- + id: 1010 + team_id: 1002 + type: 3 + access_mode: 1 + +- + id: 1011 + team_id: 1002 + type: 4 + access_mode: 1 + +- + id: 1012 + team_id: 1002 + type: 5 + access_mode: 1 + +- + id: 1013 + team_id: 1002 + type: 6 + access_mode: 1 + +- + id: 1014 + team_id: 1002 + type: 7 + access_mode: 1 diff --git a/tests/integration/fixtures/TestFeed/team_user.yml b/tests/integration/fixtures/TestFeed/team_user.yml new file mode 100644 index 0000000000..15fa3ebb1d --- /dev/null +++ b/tests/integration/fixtures/TestFeed/team_user.yml @@ -0,0 +1,11 @@ +- + id: 1001 + org_id: 3 + team_id: 1001 + uid: 8 + +- + id: 1002 + org_id: 3 + team_id: 1002 + uid: 9 From 061abe60045212acf8c3f5c49b5cc758b4cbcde9 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 2 Nov 2024 20:55:30 +0100 Subject: [PATCH 16/49] fix: don't show private forks in forks list - If a repository is forked to a private or limited user/organization, the fork should not be visible in the list of forks depending on the doer requesting the list of forks. - Added integration testing for web and API route. --- models/repo/fork.go | 10 ++++--- routers/api/v1/repo/fork.go | 4 +-- routers/web/repo/view.go | 10 +++---- tests/integration/api_fork_test.go | 43 +++++++++++++++++++++++++++++ tests/integration/repo_fork_test.go | 32 +++++++++++++++++++++ 5 files changed, 88 insertions(+), 11 deletions(-) diff --git a/models/repo/fork.go b/models/repo/fork.go index 07cd31c269..632e91c2bb 100644 --- a/models/repo/fork.go +++ b/models/repo/fork.go @@ -7,6 +7,7 @@ import ( "context" "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "xorm.io/builder" @@ -54,9 +55,9 @@ func GetUserFork(ctx context.Context, repoID, userID int64) (*Repository, error) return &forkedRepo, nil } -// GetForks returns all the forks of the repository -func GetForks(ctx context.Context, repo *Repository, listOptions db.ListOptions) ([]*Repository, error) { - sess := db.GetEngine(ctx) +// GetForks returns all the forks of the repository that are visible to the user. +func GetForks(ctx context.Context, repo *Repository, user *user_model.User, listOptions db.ListOptions) ([]*Repository, int64, error) { + sess := db.GetEngine(ctx).Where(AccessibleRepositoryCondition(user, unit.TypeInvalid)) var forks []*Repository if listOptions.Page == 0 { @@ -66,7 +67,8 @@ func GetForks(ctx context.Context, repo *Repository, listOptions db.ListOptions) sess = db.SetSessionPagination(sess, &listOptions) } - return forks, sess.Find(&forks, &Repository{ForkID: repo.ID}) + count, err := sess.FindAndCount(&forks, &Repository{ForkID: repo.ID}) + return forks, count, err } // IncrementRepoForkNum increment repository fork number diff --git a/routers/api/v1/repo/fork.go b/routers/api/v1/repo/fork.go index 97aaffd103..c9dc9681c9 100644 --- a/routers/api/v1/repo/fork.go +++ b/routers/api/v1/repo/fork.go @@ -56,7 +56,7 @@ func ListForks(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - forks, err := repo_model.GetForks(ctx, ctx.Repo.Repository, utils.GetListOptions(ctx)) + forks, total, err := repo_model.GetForks(ctx, ctx.Repo.Repository, ctx.Doer, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetForks", err) return @@ -71,7 +71,7 @@ func ListForks(ctx *context.APIContext) { apiForks[i] = convert.ToRepo(ctx, fork, permission) } - ctx.SetTotalCountHeader(int64(ctx.Repo.Repository.NumForks)) + ctx.SetTotalCountHeader(total) ctx.JSON(http.StatusOK, apiForks) } diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 41ff5f97f7..e177c81902 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -1232,11 +1232,8 @@ func Forks(ctx *context.Context) { page = 1 } - pager := context.NewPagination(ctx.Repo.Repository.NumForks, setting.MaxForksPerPage, page, 5) - ctx.Data["Page"] = pager - - forks, err := repo_model.GetForks(ctx, ctx.Repo.Repository, db.ListOptions{ - Page: pager.Paginater.Current(), + forks, total, err := repo_model.GetForks(ctx, ctx.Repo.Repository, ctx.Doer, db.ListOptions{ + Page: page, PageSize: setting.MaxForksPerPage, }) if err != nil { @@ -1244,6 +1241,9 @@ func Forks(ctx *context.Context) { return } + pager := context.NewPagination(int(total), setting.MaxForksPerPage, page, 5) + ctx.Data["Page"] = pager + for _, fork := range forks { if err = fork.LoadOwner(ctx); err != nil { ctx.ServerError("LoadOwner", err) diff --git a/tests/integration/api_fork_test.go b/tests/integration/api_fork_test.go index 6a6b05f700..6614f4b799 100644 --- a/tests/integration/api_fork_test.go +++ b/tests/integration/api_fork_test.go @@ -17,6 +17,8 @@ import ( "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/routers" "code.gitea.io/gitea/tests" + + "github.com/stretchr/testify/assert" ) func TestAPIForkAsAdminIgnoringLimits(t *testing.T) { @@ -106,3 +108,44 @@ func TestAPIDisabledForkRepo(t *testing.T) { session.MakeRequest(t, req, http.StatusNotFound) }) } + +func TestAPIForkListPrivateRepo(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + session := loginUser(t, "user5") + token := getTokenForLoggedInUser(t, session, + auth_model.AccessTokenScopeWriteRepository, + auth_model.AccessTokenScopeWriteOrganization) + org23 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 23, Visibility: api.VisibleTypePrivate}) + + req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/forks", &api.CreateForkOption{ + Organization: &org23.Name, + }).AddTokenAuth(token) + MakeRequest(t, req, http.StatusAccepted) + + t.Run("Anomynous", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1/forks") + resp := MakeRequest(t, req, http.StatusOK) + + var forks []*api.Repository + DecodeJSON(t, resp, &forks) + + assert.Empty(t, forks) + assert.EqualValues(t, "0", resp.Header().Get("X-Total-Count")) + }) + + t.Run("Logged in", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1/forks").AddTokenAuth(token) + resp := MakeRequest(t, req, http.StatusOK) + + var forks []*api.Repository + DecodeJSON(t, resp, &forks) + + assert.Len(t, forks, 1) + assert.EqualValues(t, "1", resp.Header().Get("X-Total-Count")) + }) +} diff --git a/tests/integration/repo_fork_test.go b/tests/integration/repo_fork_test.go index 2627749836..b2e40671ba 100644 --- a/tests/integration/repo_fork_test.go +++ b/tests/integration/repo_fork_test.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/routers" repo_service "code.gitea.io/gitea/services/repository" @@ -238,3 +239,34 @@ func TestRepoForkToOrg(t *testing.T) { }) }) } + +func TestForkListPrivateRepo(t *testing.T) { + forkItemSelector := ".tw-flex.tw-items-center.tw-py-2" + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + session := loginUser(t, "user5") + org23 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 23, Visibility: structs.VisibleTypePrivate}) + + testRepoFork(t, session, "user2", "repo1", org23.Name, "repo1") + + t.Run("Anomynous", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", "/user2/repo1/forks") + resp := MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + + htmlDoc.AssertElement(t, forkItemSelector, false) + }) + + t.Run("Logged in", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", "/user2/repo1/forks") + resp := session.MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + + htmlDoc.AssertElement(t, forkItemSelector, true) + }) + }) +} From 786dfc7fb81ee76d4292ca5fcb33e6ea7bdccc29 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 2 Nov 2024 15:06:27 +0100 Subject: [PATCH 17/49] fix: add ID check for updating push mirror interval - Ensure that the specified push mirror ID belongs to the requested repository, otherwise it is possible to modify the intervals of the push mirrors that do not belong to the requested repository. - Integration test added. --- routers/web/repo/setting/setting.go | 16 +++--- tests/integration/mirror_push_test.go | 79 +++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 9 deletions(-) diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index aee2e2f469..ce506eafb1 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -566,21 +566,19 @@ func SettingsPost(ctx *context.Context) { // as an error on the UI for this action ctx.Data["Err_RepoName"] = nil + m, err := selectPushMirrorByForm(ctx, form, repo) + if err != nil { + ctx.NotFound("", nil) + return + } + interval, err := time.ParseDuration(form.PushMirrorInterval) if err != nil || (interval != 0 && interval < setting.Mirror.MinInterval) { ctx.RenderWithErr(ctx.Tr("repo.mirror_interval_invalid"), tplSettingsOptions, &forms.RepoSettingForm{}) return } - id, err := strconv.ParseInt(form.PushMirrorID, 10, 64) - if err != nil { - ctx.ServerError("UpdatePushMirrorIntervalPushMirrorID", err) - return - } - m := &repo_model.PushMirror{ - ID: id, - Interval: interval, - } + m.Interval = interval if err := repo_model.UpdatePushMirrorInterval(ctx, m); err != nil { ctx.ServerError("UpdatePushMirrorInterval", err) return diff --git a/tests/integration/mirror_push_test.go b/tests/integration/mirror_push_test.go index 2dda4d6836..fa62219707 100644 --- a/tests/integration/mirror_push_test.go +++ b/tests/integration/mirror_push_test.go @@ -323,3 +323,82 @@ func TestSSHPushMirror(t *testing.T) { }) }) } + +func TestPushMirrorSettings(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + defer test.MockVariableValue(&setting.Migrations.AllowLocalNetworks, true)() + defer test.MockVariableValue(&setting.Mirror.Enabled, true)() + require.NoError(t, migrations.Init()) + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + srcRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) + srcRepo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) + assert.False(t, srcRepo.HasWiki()) + sess := loginUser(t, user.Name) + pushToRepo, _, f := tests.CreateDeclarativeRepoWithOptions(t, user, tests.DeclarativeRepoOptions{ + Name: optional.Some("push-mirror-test"), + AutoInit: optional.Some(false), + EnabledUnits: optional.Some([]unit.Type{unit.TypeCode}), + }) + defer f() + + t.Run("Adding", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/settings", srcRepo2.FullName()), map[string]string{ + "_csrf": GetCSRF(t, sess, fmt.Sprintf("/%s/settings", srcRepo2.FullName())), + "action": "push-mirror-add", + "push_mirror_address": u.String() + pushToRepo.FullName(), + "push_mirror_interval": "0", + }) + sess.MakeRequest(t, req, http.StatusSeeOther) + + req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/settings", srcRepo.FullName()), map[string]string{ + "_csrf": GetCSRF(t, sess, fmt.Sprintf("/%s/settings", srcRepo.FullName())), + "action": "push-mirror-add", + "push_mirror_address": u.String() + pushToRepo.FullName(), + "push_mirror_interval": "0", + }) + sess.MakeRequest(t, req, http.StatusSeeOther) + + flashCookie := sess.GetCookie(gitea_context.CookieNameFlash) + assert.NotNil(t, flashCookie) + assert.Contains(t, flashCookie.Value, "success") + }) + + mirrors, _, err := repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo.ID, db.ListOptions{}) + require.NoError(t, err) + assert.Len(t, mirrors, 1) + mirrorID := mirrors[0].ID + + mirrors, _, err = repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo2.ID, db.ListOptions{}) + require.NoError(t, err) + assert.Len(t, mirrors, 1) + + t.Run("Interval", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + unittest.AssertExistsAndLoadBean(t, &repo_model.PushMirror{ID: mirrorID - 1}) + + req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/settings", srcRepo.FullName()), map[string]string{ + "_csrf": GetCSRF(t, sess, fmt.Sprintf("/%s/settings", srcRepo.FullName())), + "action": "push-mirror-update", + "push_mirror_id": strconv.FormatInt(mirrorID-1, 10), + "push_mirror_interval": "10m0s", + }) + sess.MakeRequest(t, req, http.StatusNotFound) + + req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/settings", srcRepo.FullName()), map[string]string{ + "_csrf": GetCSRF(t, sess, fmt.Sprintf("/%s/settings", srcRepo.FullName())), + "action": "push-mirror-update", + "push_mirror_id": strconv.FormatInt(mirrorID, 10), + "push_mirror_interval": "10m0s", + }) + sess.MakeRequest(t, req, http.StatusSeeOther) + + flashCookie := sess.GetCookie(gitea_context.CookieNameFlash) + assert.NotNil(t, flashCookie) + assert.Contains(t, flashCookie.Value, "success") + }) + }) +} From b1bc2949550cd9f357d33b22a88a2fb7440a4ff8 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Fri, 15 Nov 2024 10:30:15 +0100 Subject: [PATCH 18/49] chore(release-notes): 15 November 2024 security fixes --- release-notes/5974.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 release-notes/5974.md diff --git a/release-notes/5974.md b/release-notes/5974.md new file mode 100644 index 0000000000..2f78d4733f --- /dev/null +++ b/release-notes/5974.md @@ -0,0 +1,8 @@ +fix: [commit](https://codeberg.org/forgejo/forgejo/commit/1ce33aa38d1d258d14523ff2c7c2dbf339f22b74) it was possible to use a token sent via email for secondary email validation to reset the password instead. In other words, a token sent for a given action (registration, password reset or secondary email validation) could be used to perform a different action. It is no longer possible to use a token for an action that is different from its original purpose. +fix: [commit](https://codeberg.org/forgejo/forgejo/commit/061abe60045212acf8c3f5c49b5cc758b4cbcde9) a fork of a public repository would show in the list of forks, even if its owner was not a public user or organization. Such a fork is now hidden from the list of forks of the public repository. +fix: [commit](https://codeberg.org/forgejo/forgejo/commit/3e3ef76808100cb1c853378733d0f6a910324ac6) the members of an organization team with read access to a repository (e.g. to read issues) but no read access to the code could read the RSS or atom feeds which include the commit activity. Reading the RSS or atom feeds is now denied unless the team has read permissions on the code. +fix: [commit](https://codeberg.org/forgejo/forgejo/commit/9508aa7713632ed40124a933d91d5766cf2369c2) the tokens used when [replying by email to issues or pull requests](https://forgejo.org/docs/v9.0/user/incoming/) were weaker than the [rfc2104 recommendations](https://datatracker.ietf.org/doc/html/rfc2104#section-5). The tokens are now truncated to 128 bits instead of 80 bits. It is no longer possible to reply to emails sent before the upgrade because the weaker tokens are invalid. +fix: [commit](https://codeberg.org/forgejo/forgejo/commit/786dfc7fb81ee76d4292ca5fcb33e6ea7bdccc29) a registered user could modify the update frequency of any push mirror (e.g. every 4h instead of every 8h). They are now only able to do that if they have administrative permissions on the repository. +fix: [commit](https://codeberg.org/forgejo/forgejo/commit/e6bbecb02d47730d3cc630d419fe27ef2fb5cb39) it was possible to use basic authorization (i.e. user:password) for requests to the API even when security keys were enrolled for a user. It is no longer possible, an application token must be used instead. +fix: [commit](https://codeberg.org/forgejo/forgejo/commit/7067cc7da4f144cc8a2fd2ae6e5307e0465ace7f) some markup sanitation rules were not as strong as they could be (e.g. allowing `emoji somethingelse` as well as `emoji`). The rules are now stricter and do not allow for such cases. +fix: [commit](https://codeberg.org/forgejo/forgejo/commit/b70196653f9d7d3b9d4e72d114e5cc6f472988c4) when Forgejo is configured to enable instance wide search (e.g. with [bleve](https://blevesearch.com/)), results found in the repositories of private or limited users were displayed to anonymous visitors. The results found in private or limited organizations were not displayed. The search results found in the repositories of private or limited user are no longer displayed to anonymous visitors. From 4a5d9d4b78c6084a38ce278d16f9ba6d11d912f8 Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 15 Nov 2024 14:02:16 +0100 Subject: [PATCH 19/49] chore: fix e2e - Regression from #5948 - Use proper permission. - Remove debug statement --- tests/e2e/utils_e2e_test.go | 10 ++++++++++ tests/integration/git_helper_for_declarative_test.go | 4 ++-- tests/test_utils.go | 1 - 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/e2e/utils_e2e_test.go b/tests/e2e/utils_e2e_test.go index cfd3ff9e3b..bf1a8a418c 100644 --- a/tests/e2e/utils_e2e_test.go +++ b/tests/e2e/utils_e2e_test.go @@ -8,6 +8,8 @@ import ( "net" "net/http" "net/url" + "os" + "regexp" "testing" "time" @@ -17,6 +19,8 @@ import ( "github.com/stretchr/testify/require" ) +var rootPathRe = regexp.MustCompile("\\[repository\\]\nROOT\\s=\\s.*") + func onForgejoRunTB(t testing.TB, callback func(testing.TB, *url.URL), prepare ...bool) { if len(prepare) == 0 || prepare[0] { defer tests.PrepareTestEnv(t, 1)() @@ -37,7 +41,13 @@ func onForgejoRunTB(t testing.TB, callback func(testing.TB, *url.URL), prepare . require.NoError(t, err) u.Host = listener.Addr().String() + // Override repository root in config. + conf, err := os.ReadFile(setting.CustomConf) + require.NoError(t, err) + require.NoError(t, os.WriteFile(setting.CustomConf, rootPathRe.ReplaceAll(conf, []byte("[repository]\nROOT = "+setting.RepoRootPath)), 0o644)) + defer func() { + require.NoError(t, os.WriteFile(setting.CustomConf, conf, 0o644)) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) s.Shutdown(ctx) cancel() diff --git a/tests/integration/git_helper_for_declarative_test.go b/tests/integration/git_helper_for_declarative_test.go index 11c72ce7f3..83d8177460 100644 --- a/tests/integration/git_helper_for_declarative_test.go +++ b/tests/integration/git_helper_for_declarative_test.go @@ -83,10 +83,10 @@ func onGiteaRun[T testing.TB](t T, callback func(T, *url.URL)) { // Override repository root in config. conf, err := os.ReadFile(setting.CustomConf) require.NoError(t, err) - require.NoError(t, os.WriteFile(setting.CustomConf, rootPathRe.ReplaceAll(conf, []byte("[repository]\nROOT = "+setting.RepoRootPath)), os.ModePerm)) + require.NoError(t, os.WriteFile(setting.CustomConf, rootPathRe.ReplaceAll(conf, []byte("[repository]\nROOT = "+setting.RepoRootPath)), 0o600)) defer func() { - require.NoError(t, os.WriteFile(setting.CustomConf, conf, os.ModePerm)) + require.NoError(t, os.WriteFile(setting.CustomConf, conf, 0o600)) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) s.Shutdown(ctx) cancel() diff --git a/tests/test_utils.go b/tests/test_utils.go index 55cbc8eb10..b3c03a30a1 100644 --- a/tests/test_utils.go +++ b/tests/test_utils.go @@ -201,7 +201,6 @@ func InitTest(requireGitea bool) { if err != nil { log.Fatal("os.ReadDir: %v", err) } - fmt.Println(ownerDirs) for _, ownerDir := range ownerDirs { if !ownerDir.Type().IsDir() { From 5406310f3e2463f8e2272a8ce16baaaeeb143dce Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Fri, 15 Nov 2024 09:43:33 +0100 Subject: [PATCH 20/49] ci: upload all e2e artifacts --- .forgejo/workflows/testing.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.forgejo/workflows/testing.yml b/.forgejo/workflows/testing.yml index a04817fd04..2f17603f78 100644 --- a/.forgejo/workflows/testing.yml +++ b/.forgejo/workflows/testing.yml @@ -122,12 +122,12 @@ jobs: USE_REPO_TEST_DIR: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 CHANGED_FILES: ${{steps.changed-files.outputs.all_changed_files}} - - name: Upload screenshots on failure + - name: Upload test artifacts on failure if: failure() uses: https://code.forgejo.org/forgejo/upload-artifact@v4 with: - name: screenshots.zip - path: /workspace/forgejo/forgejo/tests/e2e/test-artifacts/*/*.png + name: test-artifacts.zip + path: tests/e2e/test-artifacts/ retention-days: 3 test-remote-cacher: if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing' From 9fd2df6e306688bbe228ee69f7371c96f9737853 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Fri, 15 Nov 2024 22:59:52 +0100 Subject: [PATCH 21/49] chore(release-notes): fix the v9.0.2 links --- RELEASE-NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 431710389d..c5f3831cf9 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -8,7 +8,7 @@ The release notes of each release [are available in the corresponding milestone] ## 9.0.2 -The Forgejo v9.0.2 release notes are [available in the v9.0.2 milestone](https://codeberg.org/forgejo/forgejo/milestone/8610. +The Forgejo v9.0.2 release notes are [available in the v9.0.2 milestone](https://codeberg.org/forgejo/forgejo/milestone/8610). ## 9.0.1 From 66dfb2813c05d29e8039b61c83499fa11b521184 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 16 Nov 2024 00:03:23 +0000 Subject: [PATCH 22/49] Update github.com/grafana/go-json digest to f14426c --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2b8a6cb88a..c39d66bc45 100644 --- a/go.mod +++ b/go.mod @@ -297,4 +297,4 @@ replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.22.0 replace github.com/mholt/archiver/v3 => code.forgejo.org/forgejo/archiver/v3 v3.5.1 -replace github.com/goccy/go-json => github.com/grafana/go-json v0.0.0-20241106155216-71a03f133f5c +replace github.com/goccy/go-json => github.com/grafana/go-json v0.0.0-20241115232854-f14426c40ff2 diff --git a/go.sum b/go.sum index 40a351dbe6..9c318fc8dd 100644 --- a/go.sum +++ b/go.sum @@ -381,8 +381,8 @@ github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kX github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= -github.com/grafana/go-json v0.0.0-20241106155216-71a03f133f5c h1:yKBKEC347YZpgii1KazRCfxHsTaxMqWZzoivM1OTT50= -github.com/grafana/go-json v0.0.0-20241106155216-71a03f133f5c/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/grafana/go-json v0.0.0-20241115232854-f14426c40ff2 h1:8xGrYqQ1GM4aaMk7pNDfecBdL/VGhEbpvvGBoqO6BIY= +github.com/grafana/go-json v0.0.0-20241115232854-f14426c40ff2/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE= github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= From cf323a3d55e11f2eb5834e36a5af69157e6b45de Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sat, 16 Nov 2024 10:53:46 +0100 Subject: [PATCH 23/49] fix: extend `forgejo_auth_token` table (part two) Add the default value of the purpose field to both the table and the migration. The table in v9 and v7 backport already have the default value. ALTER TABLE `forgejo_auth_token` ADD `purpose` TEXT NOT NULL [] - Cannot add a NOT NULL column with default value NULL --- models/auth/auth_token.go | 2 +- models/forgejo_migrations/v24.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/models/auth/auth_token.go b/models/auth/auth_token.go index 8955b57a4f..c64af3e41f 100644 --- a/models/auth/auth_token.go +++ b/models/auth/auth_token.go @@ -39,7 +39,7 @@ type AuthorizationToken struct { UID int64 `xorm:"INDEX"` LookupKey string `xorm:"INDEX UNIQUE"` HashedValidator string - Purpose AuthorizationPurpose `xorm:"NOT NULL"` + Purpose AuthorizationPurpose `xorm:"NOT NULL DEFAULT 'long_term_authorization'"` Expiry timeutil.TimeStamp } diff --git a/models/forgejo_migrations/v24.go b/models/forgejo_migrations/v24.go index 8c751e3f5f..ebfb5fc1c4 100644 --- a/models/forgejo_migrations/v24.go +++ b/models/forgejo_migrations/v24.go @@ -8,7 +8,7 @@ import "xorm.io/xorm" func AddPurposeToForgejoAuthToken(x *xorm.Engine) error { type ForgejoAuthToken struct { ID int64 `xorm:"pk autoincr"` - Purpose string `xorm:"NOT NULL"` + Purpose string `xorm:"NOT NULL DEFAULT 'long_term_authorization'"` } if err := x.Sync(new(ForgejoAuthToken)); err != nil { return err From 2cccc02e76e694546265e1ef9bbeedf49c9acb69 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 16 Nov 2024 02:28:38 +0100 Subject: [PATCH 24/49] feat: improve `GetLatestCommitStatusForPairs` - Simplify the function into a single SQL query. This may or may not help with a monster query we are seeing in Codeberg that is using 400MiB and takes 50MiB to simply log the query. The result is now capped to the actual latest index, - Add unit test. --- models/fixtures/commit_status.yml | 32 +++++- models/git/commit_status.go | 43 ++------- models/git/commit_status_test.go | 155 +++++++++++++++++++++++++++++- 3 files changed, 191 insertions(+), 39 deletions(-) diff --git a/models/fixtures/commit_status.yml b/models/fixtures/commit_status.yml index 0ba6caafe9..c568e89cea 100644 --- a/models/fixtures/commit_status.yml +++ b/models/fixtures/commit_status.yml @@ -7,6 +7,7 @@ target_url: https://example.com/builds/ description: My awesome CI-service context: ci/awesomeness + context_hash: c65f4d64a3b14a3eced0c9b36799e66e1bd5ced7 creator_id: 2 - @@ -18,6 +19,7 @@ target_url: https://example.com/coverage/ description: My awesome Coverage service context: cov/awesomeness + context_hash: 3929ac7bccd3fa1bf9b38ddedb77973b1b9a8cfe creator_id: 2 - @@ -29,6 +31,7 @@ target_url: https://example.com/coverage/ description: My awesome Coverage service context: cov/awesomeness + context_hash: 3929ac7bccd3fa1bf9b38ddedb77973b1b9a8cfe creator_id: 2 - @@ -40,6 +43,7 @@ target_url: https://example.com/builds/ description: My awesome CI-service context: ci/awesomeness + context_hash: c65f4d64a3b14a3eced0c9b36799e66e1bd5ced7 creator_id: 2 - @@ -51,15 +55,41 @@ target_url: https://example.com/builds/ description: My awesome deploy service context: deploy/awesomeness + context_hash: ae9547713a6665fc4261d0756904932085a41cf2 creator_id: 2 - id: 6 - index: 6 + index: 1 repo_id: 62 state: "failure" sha: "774f93df12d14931ea93259ae93418da4482fcc1" target_url: "/user2/test_workflows/actions" description: My awesome deploy service context: deploy/awesomeness + context_hash: ae9547713a6665fc4261d0756904932085a41cf2 + creator_id: 2 + +- + id: 7 + index: 6 + repo_id: 1 + state: "pending" + sha: "1234123412341234123412341234123412341234" + target_url: https://example.com/builds/ + description: My awesome deploy service + context: deploy/awesomeness + context_hash: ae9547713a6665fc4261d0756904932085a41cf2 + creator_id: 2 + +- + id: 8 + index: 2 + repo_id: 62 + state: "error" + sha: "774f93df12d14931ea93259ae93418da4482fcc1" + target_url: "/user2/test_workflows/actions" + description: "My awesome deploy service - v2" + context: deploy/awesomeness + context_hash: ae9547713a6665fc4261d0756904932085a41cf2 creator_id: 2 diff --git a/models/git/commit_status.go b/models/git/commit_status.go index 53d1ddc8c3..438eefe81b 100644 --- a/models/git/commit_status.go +++ b/models/git/commit_status.go @@ -288,27 +288,18 @@ func GetLatestCommitStatus(ctx context.Context, repoID int64, sha string, listOp // GetLatestCommitStatusForPairs returns all statuses with a unique context for a given list of repo-sha pairs func GetLatestCommitStatusForPairs(ctx context.Context, repoSHAs []RepoSHA) (map[int64][]*CommitStatus, error) { - type result struct { - Index int64 - RepoID int64 - SHA string - } - - results := make([]result, 0, len(repoSHAs)) - - getBase := func() *xorm.Session { - return db.GetEngine(ctx).Table(&CommitStatus{}) - } + results := []*CommitStatus{} // Create a disjunction of conditions for each repoID and SHA pair conds := make([]builder.Cond, 0, len(repoSHAs)) for _, repoSHA := range repoSHAs { conds = append(conds, builder.Eq{"repo_id": repoSHA.RepoID, "sha": repoSHA.SHA}) } - sess := getBase().Where(builder.Or(conds...)). - Select("max( `index` ) as `index`, repo_id, sha"). - GroupBy("context_hash, repo_id, sha").OrderBy("max( `index` ) desc") + sess := db.GetEngine(ctx).Table(&CommitStatus{}). + Select("MAX(`index`) AS `index`, *"). + Where(builder.Or(conds...)). + GroupBy("context_hash, repo_id, sha").OrderBy("MAX(`index`) DESC") err := sess.Find(&results) if err != nil { return nil, err @@ -316,27 +307,9 @@ func GetLatestCommitStatusForPairs(ctx context.Context, repoSHAs []RepoSHA) (map repoStatuses := make(map[int64][]*CommitStatus) - if len(results) > 0 { - statuses := make([]*CommitStatus, 0, len(results)) - - conds = make([]builder.Cond, 0, len(results)) - for _, result := range results { - cond := builder.Eq{ - "`index`": result.Index, - "repo_id": result.RepoID, - "sha": result.SHA, - } - conds = append(conds, cond) - } - err = getBase().Where(builder.Or(conds...)).Find(&statuses) - if err != nil { - return nil, err - } - - // Group the statuses by repo ID - for _, status := range statuses { - repoStatuses[status.RepoID] = append(repoStatuses[status.RepoID], status) - } + // Group the statuses by repo ID + for _, status := range results { + repoStatuses[status.RepoID] = append(repoStatuses[status.RepoID], status) } return repoStatuses, nil diff --git a/models/git/commit_status_test.go b/models/git/commit_status_test.go index 1014ee1e13..0f799f3507 100644 --- a/models/git/commit_status_test.go +++ b/models/git/commit_status_test.go @@ -35,8 +35,8 @@ func TestGetCommitStatuses(t *testing.T) { SHA: sha1, }) require.NoError(t, err) - assert.Equal(t, 5, int(maxResults)) - assert.Len(t, statuses, 5) + assert.EqualValues(t, 6, maxResults) + assert.Len(t, statuses, 6) assert.Equal(t, "ci/awesomeness", statuses[0].Context) assert.Equal(t, structs.CommitStatusPending, statuses[0].State) @@ -58,13 +58,17 @@ func TestGetCommitStatuses(t *testing.T) { assert.Equal(t, structs.CommitStatusError, statuses[4].State) assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[4].APIURL(db.DefaultContext)) + assert.Equal(t, "deploy/awesomeness", statuses[5].Context) + assert.Equal(t, structs.CommitStatusPending, statuses[5].State) + assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[5].APIURL(db.DefaultContext)) + statuses, maxResults, err = db.FindAndCount[git_model.CommitStatus](db.DefaultContext, &git_model.CommitStatusOptions{ ListOptions: db.ListOptions{Page: 2, PageSize: 50}, RepoID: repo1.ID, SHA: sha1, }) require.NoError(t, err) - assert.Equal(t, 5, int(maxResults)) + assert.EqualValues(t, 6, maxResults) assert.Empty(t, statuses) } @@ -265,3 +269,148 @@ func TestCommitStatusesHideActionsURL(t *testing.T) { assert.Empty(t, statuses[0].TargetURL) assert.Equal(t, "https://mycicd.org/1", statuses[1].TargetURL) } + +func TestGetLatestCommitStatusForPairs(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + t.Run("All", func(t *testing.T) { + pairs, err := git_model.GetLatestCommitStatusForPairs(db.DefaultContext, nil) + require.NoError(t, err) + + assert.EqualValues(t, map[int64][]*git_model.CommitStatus{ + 1: { + { + ID: 7, + Index: 6, + RepoID: 1, + State: structs.CommitStatusPending, + SHA: "1234123412341234123412341234123412341234", + TargetURL: "https://example.com/builds/", + Description: "My awesome deploy service", + ContextHash: "ae9547713a6665fc4261d0756904932085a41cf2", + Context: "deploy/awesomeness", + CreatorID: 2, + }, + { + ID: 4, + Index: 4, + State: structs.CommitStatusFailure, + TargetURL: "https://example.com/builds/", + Description: "My awesome CI-service", + Context: "ci/awesomeness", + CreatorID: 2, + RepoID: 1, + SHA: "1234123412341234123412341234123412341234", + ContextHash: "c65f4d64a3b14a3eced0c9b36799e66e1bd5ced7", + }, + { + ID: 3, + Index: 3, + State: structs.CommitStatusSuccess, + TargetURL: "https://example.com/coverage/", + Description: "My awesome Coverage service", + Context: "cov/awesomeness", + CreatorID: 2, + RepoID: 1, + SHA: "1234123412341234123412341234123412341234", + ContextHash: "3929ac7bccd3fa1bf9b38ddedb77973b1b9a8cfe", + }, + }, + 62: { + { + ID: 8, + Index: 2, + RepoID: 62, + State: structs.CommitStatusError, + TargetURL: "/user2/test_workflows/actions", + Description: "My awesome deploy service - v2", + Context: "deploy/awesomeness", + SHA: "774f93df12d14931ea93259ae93418da4482fcc1", + ContextHash: "ae9547713a6665fc4261d0756904932085a41cf2", + CreatorID: 2, + }, + }, + }, pairs) + }) + + t.Run("Repo 1", func(t *testing.T) { + pairs, err := git_model.GetLatestCommitStatusForPairs(db.DefaultContext, []git_model.RepoSHA{{1, "1234123412341234123412341234123412341234"}}) + require.NoError(t, err) + + assert.EqualValues(t, map[int64][]*git_model.CommitStatus{ + 1: { + { + ID: 7, + Index: 6, + RepoID: 1, + State: structs.CommitStatusPending, + SHA: "1234123412341234123412341234123412341234", + TargetURL: "https://example.com/builds/", + Description: "My awesome deploy service", + ContextHash: "ae9547713a6665fc4261d0756904932085a41cf2", + Context: "deploy/awesomeness", + CreatorID: 2, + }, + { + ID: 4, + Index: 4, + State: structs.CommitStatusFailure, + TargetURL: "https://example.com/builds/", + Description: "My awesome CI-service", + Context: "ci/awesomeness", + CreatorID: 2, + RepoID: 1, + SHA: "1234123412341234123412341234123412341234", + ContextHash: "c65f4d64a3b14a3eced0c9b36799e66e1bd5ced7", + }, + { + ID: 3, + Index: 3, + State: structs.CommitStatusSuccess, + TargetURL: "https://example.com/coverage/", + Description: "My awesome Coverage service", + Context: "cov/awesomeness", + CreatorID: 2, + RepoID: 1, + SHA: "1234123412341234123412341234123412341234", + ContextHash: "3929ac7bccd3fa1bf9b38ddedb77973b1b9a8cfe", + }, + }, + }, pairs) + }) + t.Run("Repo 62", func(t *testing.T) { + pairs, err := git_model.GetLatestCommitStatusForPairs(db.DefaultContext, []git_model.RepoSHA{{62, "774f93df12d14931ea93259ae93418da4482fcc1"}}) + require.NoError(t, err) + + assert.EqualValues(t, map[int64][]*git_model.CommitStatus{ + 62: { + { + ID: 8, + Index: 2, + RepoID: 62, + State: structs.CommitStatusError, + TargetURL: "/user2/test_workflows/actions", + Description: "My awesome deploy service - v2", + Context: "deploy/awesomeness", + SHA: "774f93df12d14931ea93259ae93418da4482fcc1", + ContextHash: "ae9547713a6665fc4261d0756904932085a41cf2", + CreatorID: 2, + }, + }, + }, pairs) + }) + + t.Run("Repo 62 nonexistant sha", func(t *testing.T) { + pairs, err := git_model.GetLatestCommitStatusForPairs(db.DefaultContext, []git_model.RepoSHA{{62, "774f93df12d14931ea93259ae93418da4482fcc"}}) + require.NoError(t, err) + + assert.EqualValues(t, map[int64][]*git_model.CommitStatus{}, pairs) + }) + + t.Run("SHA with non existant repo id", func(t *testing.T) { + pairs, err := git_model.GetLatestCommitStatusForPairs(db.DefaultContext, []git_model.RepoSHA{{1, "774f93df12d14931ea93259ae93418da4482fcc1"}}) + require.NoError(t, err) + + assert.EqualValues(t, map[int64][]*git_model.CommitStatus{}, pairs) + }) +} From 013cc1dee430b45ac06adab1c7a05c6b51c9fe3a Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 4 Nov 2024 23:13:52 -0800 Subject: [PATCH 25/49] Only query team tables if repository is under org when getting assignees (#32414) It's unnecessary to query the team table if the repository is not under organization when getting assignees. (cherry picked from commit 1887c75c35c1d16372b1dbe2b792e374b558ce1f) --- models/repo/user_repo.go | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/models/repo/user_repo.go b/models/repo/user_repo.go index 6790ee1da9..781a75730a 100644 --- a/models/repo/user_repo.go +++ b/models/repo/user_repo.go @@ -75,26 +75,28 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us return nil, err } - additionalUserIDs := make([]int64, 0, 10) - if err = e.Table("team_user"). - Join("INNER", "team_repo", "`team_repo`.team_id = `team_user`.team_id"). - Join("INNER", "team_unit", "`team_unit`.team_id = `team_user`.team_id"). - Where("`team_repo`.repo_id = ? AND (`team_unit`.access_mode >= ? OR (`team_unit`.access_mode = ? AND `team_unit`.`type` = ?))", - repo.ID, perm.AccessModeWrite, perm.AccessModeRead, unit.TypePullRequests). - Distinct("`team_user`.uid"). - Select("`team_user`.uid"). - Find(&additionalUserIDs); err != nil { - return nil, err - } - uniqueUserIDs := make(container.Set[int64]) uniqueUserIDs.AddMultiple(userIDs...) - uniqueUserIDs.AddMultiple(additionalUserIDs...) + + if repo.Owner.IsOrganization() { + additionalUserIDs := make([]int64, 0, 10) + if err = e.Table("team_user"). + Join("INNER", "team_repo", "`team_repo`.team_id = `team_user`.team_id"). + Join("INNER", "team_unit", "`team_unit`.team_id = `team_user`.team_id"). + Where("`team_repo`.repo_id = ? AND (`team_unit`.access_mode >= ? OR (`team_unit`.access_mode = ? AND `team_unit`.`type` = ?))", + repo.ID, perm.AccessModeWrite, perm.AccessModeRead, unit.TypePullRequests). + Distinct("`team_user`.uid"). + Select("`team_user`.uid"). + Find(&additionalUserIDs); err != nil { + return nil, err + } + uniqueUserIDs.AddMultiple(additionalUserIDs...) + } // Leave a seat for owner itself to append later, but if owner is an organization // and just waste 1 unit is cheaper than re-allocate memory once. users := make([]*user_model.User, 0, len(uniqueUserIDs)+1) - if len(userIDs) > 0 { + if len(uniqueUserIDs) > 0 { if err = e.In("id", uniqueUserIDs.Values()). Where(builder.Eq{"`user`.is_active": true}). OrderBy(user_model.GetOrderByName()). From 02a2dbef69553595b8e12385da1209cba94fde49 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 16 Nov 2024 15:03:28 +0100 Subject: [PATCH 26/49] feat: default to generating EdDSA for OAuth JWT signing key --- modules/setting/oauth2.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/setting/oauth2.go b/modules/setting/oauth2.go index 49288e2639..c989460e5d 100644 --- a/modules/setting/oauth2.go +++ b/modules/setting/oauth2.go @@ -106,7 +106,7 @@ var OAuth2 = struct { AccessTokenExpirationTime: 3600, RefreshTokenExpirationTime: 730, InvalidateRefreshTokens: true, - JWTSigningAlgorithm: "RS256", + JWTSigningAlgorithm: "EdDSA", JWTSigningPrivateKeyFile: "jwt/private.pem", MaxTokenLength: math.MaxInt16, DefaultApplications: []string{"git-credential-oauth", "git-credential-manager", "tea"}, From fc26becba4b08877a726f2e7e453992310245fe5 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Fri, 8 Nov 2024 10:53:06 +0800 Subject: [PATCH 27/49] Fix broken releases when re-pushing tags (#32435) Fix #32427 (cherry picked from commit 35bcd667b23de29a7b0d0bf1090fb10961d3aca3) Conflicts: - tests/integration/repo_tag_test.go Resolved by manually copying the added test, and also manually adjusting the imported Go modules. --- services/repository/push.go | 19 +++++++----- tests/integration/repo_tag_test.go | 47 ++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/services/repository/push.go b/services/repository/push.go index 0f24295e89..a8e7d0f3b6 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -307,9 +307,10 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo } releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{ - RepoID: repo.ID, - TagNames: tags, - IncludeTags: true, + RepoID: repo.ID, + TagNames: tags, + IncludeDrafts: true, + IncludeTags: true, }) if err != nil { return fmt.Errorf("db.Find[repo_model.Release]: %w", err) @@ -394,13 +395,17 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo } newReleases = append(newReleases, rel) } else { - rel.Title = parts[0] - rel.Note = note rel.Sha1 = commit.ID.String() rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix()) rel.NumCommits = commitsCount - if rel.IsTag && author != nil { - rel.PublisherID = author.ID + if rel.IsTag { + rel.Title = parts[0] + rel.Note = note + if author != nil { + rel.PublisherID = author.ID + } + } else { + rel.IsDraft = false } if err = repo_model.UpdateRelease(ctx, rel); err != nil { return fmt.Errorf("Update: %w", err) diff --git a/tests/integration/repo_tag_test.go b/tests/integration/repo_tag_test.go index 06b6c30383..cb2d0964f0 100644 --- a/tests/integration/repo_tag_test.go +++ b/tests/integration/repo_tag_test.go @@ -5,17 +5,20 @@ package integration import ( + "fmt" "net/http" "net/url" "strings" "testing" "code.gitea.io/gitea/models" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" repo_module "code.gitea.io/gitea/modules/repository" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/services/release" "code.gitea.io/gitea/tests" @@ -159,3 +162,47 @@ func TestSyncRepoTags(t *testing.T) { }) }) } + +func TestRepushTag(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + session := loginUser(t, owner.LowerName) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + + httpContext := NewAPITestContext(t, owner.Name, repo.Name) + + dstPath := t.TempDir() + + u.Path = httpContext.GitPath() + u.User = url.UserPassword(owner.Name, userPassword) + + doGitClone(dstPath, u)(t) + + // create and push a tag + _, _, err := git.NewCommand(git.DefaultContext, "tag", "v2.0").RunStdString(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) + _, _, err = git.NewCommand(git.DefaultContext, "push", "origin", "--tags", "v2.0").RunStdString(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) + // create a release for the tag + createdRelease := createNewReleaseUsingAPI(t, token, owner, repo, "v2.0", "", "Release of v2.0", "desc") + assert.False(t, createdRelease.IsDraft) + // delete the tag + _, _, err = git.NewCommand(git.DefaultContext, "push", "origin", "--delete", "v2.0").RunStdString(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) + // query the release by API and it should be a draft + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s", owner.Name, repo.Name, "v2.0")) + resp := MakeRequest(t, req, http.StatusOK) + var respRelease *api.Release + DecodeJSON(t, resp, &respRelease) + assert.True(t, respRelease.IsDraft) + // re-push the tag + _, _, err = git.NewCommand(git.DefaultContext, "push", "origin", "--tags", "v2.0").RunStdString(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) + // query the release by API and it should not be a draft + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s", owner.Name, repo.Name, "v2.0")) + resp = MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &respRelease) + assert.False(t, respRelease.IsDraft) + }) +} From 308812a82ec6b0a372e0881bb786d2dc8cff3919 Mon Sep 17 00:00:00 2001 From: silverwind Date: Sat, 9 Nov 2024 19:03:55 +0100 Subject: [PATCH 28/49] Fix mermaid diagram height when initially hidden (#32457) In a hidden iframe, `document.body.clientHeight` is not reliable. Use `IntersectionObserver` to detect the visibility change and update the height there. Fixes: https://github.com/go-gitea/gitea/issues/32392 image (cherry picked from commit b55a31eb6a894feb5508e350ff5e9548b2531bd6) --- web_src/js/markup/mermaid.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/web_src/js/markup/mermaid.js b/web_src/js/markup/mermaid.js index 0549fb3e31..e420ff12a2 100644 --- a/web_src/js/markup/mermaid.js +++ b/web_src/js/markup/mermaid.js @@ -56,10 +56,21 @@ export async function renderMermaid() { btn.setAttribute('data-clipboard-text', source); mermaidBlock.append(btn); + const updateIframeHeight = () => { + iframe.style.height = `${iframe.contentWindow.document.body.clientHeight}px`; + }; + + // update height when element's visibility state changes, for example when the diagram is inside + // a
+ block and the
block becomes visible upon user interaction, it + // would initially set a incorrect height and the correct height is set during this callback. + (new IntersectionObserver(() => { + updateIframeHeight(); + }, {root: document.documentElement})).observe(iframe); + iframe.addEventListener('load', () => { pre.replaceWith(mermaidBlock); mermaidBlock.classList.remove('tw-hidden'); - iframe.style.height = `${iframe.contentWindow.document.body.clientHeight}px`; + updateIframeHeight(); setTimeout(() => { // avoid flash of iframe background mermaidBlock.classList.remove('is-loading'); iframe.classList.remove('tw-invisible'); From 7d59060dc6bc2b999bfec2ae71a8e3898cce0f0b Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 16 Nov 2024 14:57:02 +0100 Subject: [PATCH 29/49] bug: correctly generate oauth2 jwt signing key - When RS256, RS384, ES384, ES512 was specified as the JWT signing algorithm they would generate RS512 and ES256 respectively. - Added unit test. --- services/auth/source/oauth2/jwtsigningkey.go | 22 +++- .../auth/source/oauth2/jwtsigningkey_test.go | 116 ++++++++++++++++++ 2 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 services/auth/source/oauth2/jwtsigningkey_test.go diff --git a/services/auth/source/oauth2/jwtsigningkey.go b/services/auth/source/oauth2/jwtsigningkey.go index 070fffe60f..92adfc4d84 100644 --- a/services/auth/source/oauth2/jwtsigningkey.go +++ b/services/auth/source/oauth2/jwtsigningkey.go @@ -347,12 +347,30 @@ func loadOrCreateAsymmetricKey() (any, error) { key, err := func() (any, error) { switch { case strings.HasPrefix(setting.OAuth2.JWTSigningAlgorithm, "RS"): - return rsa.GenerateKey(rand.Reader, 4096) + var bits int + switch setting.OAuth2.JWTSigningAlgorithm { + case "RS256": + bits = 2048 + case "RS384": + bits = 3072 + case "RS512": + bits = 4096 + } + return rsa.GenerateKey(rand.Reader, bits) case setting.OAuth2.JWTSigningAlgorithm == "EdDSA": _, pk, err := ed25519.GenerateKey(rand.Reader) return pk, err default: - return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + var curve elliptic.Curve + switch setting.OAuth2.JWTSigningAlgorithm { + case "ES256": + curve = elliptic.P256() + case "ES384": + curve = elliptic.P384() + case "ES512": + curve = elliptic.P521() + } + return ecdsa.GenerateKey(curve, rand.Reader) } }() if err != nil { diff --git a/services/auth/source/oauth2/jwtsigningkey_test.go b/services/auth/source/oauth2/jwtsigningkey_test.go new file mode 100644 index 0000000000..4db538b0e8 --- /dev/null +++ b/services/auth/source/oauth2/jwtsigningkey_test.go @@ -0,0 +1,116 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package oauth2 + +import ( + "crypto/ecdsa" + "crypto/ed25519" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "os" + "path/filepath" + "testing" + + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLoadOrCreateAsymmetricKey(t *testing.T) { + loadKey := func(t *testing.T) any { + t.Helper() + loadOrCreateAsymmetricKey() + + fileContent, err := os.ReadFile(setting.OAuth2.JWTSigningPrivateKeyFile) + require.NoError(t, err) + + block, _ := pem.Decode(fileContent) + assert.NotNil(t, block) + assert.EqualValues(t, "PRIVATE KEY", block.Type) + + parsedKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) + require.NoError(t, err) + + return parsedKey + } + t.Run("RSA-2048", func(t *testing.T) { + defer test.MockVariableValue(&setting.OAuth2.JWTSigningPrivateKeyFile, filepath.Join(t.TempDir(), "jwt-rsa-2048.priv"))() + defer test.MockVariableValue(&setting.OAuth2.JWTSigningAlgorithm, "RS256")() + + parsedKey := loadKey(t) + + rsaPrivateKey := parsedKey.(*rsa.PrivateKey) + assert.EqualValues(t, 2048, rsaPrivateKey.N.BitLen()) + + t.Run("Load key with differ specified algorithm", func(t *testing.T) { + defer test.MockVariableValue(&setting.OAuth2.JWTSigningAlgorithm, "EdDSA")() + + parsedKey := loadKey(t) + rsaPrivateKey := parsedKey.(*rsa.PrivateKey) + assert.EqualValues(t, 2048, rsaPrivateKey.N.BitLen()) + }) + }) + + t.Run("RSA-3072", func(t *testing.T) { + defer test.MockVariableValue(&setting.OAuth2.JWTSigningPrivateKeyFile, filepath.Join(t.TempDir(), "jwt-rsa-3072.priv"))() + defer test.MockVariableValue(&setting.OAuth2.JWTSigningAlgorithm, "RS384")() + + parsedKey := loadKey(t) + + rsaPrivateKey := parsedKey.(*rsa.PrivateKey) + assert.EqualValues(t, 3072, rsaPrivateKey.N.BitLen()) + }) + + t.Run("RSA-4096", func(t *testing.T) { + defer test.MockVariableValue(&setting.OAuth2.JWTSigningPrivateKeyFile, filepath.Join(t.TempDir(), "jwt-rsa-4096.priv"))() + defer test.MockVariableValue(&setting.OAuth2.JWTSigningAlgorithm, "RS512")() + + parsedKey := loadKey(t) + + rsaPrivateKey := parsedKey.(*rsa.PrivateKey) + assert.EqualValues(t, 4096, rsaPrivateKey.N.BitLen()) + }) + + t.Run("ECDSA-256", func(t *testing.T) { + defer test.MockVariableValue(&setting.OAuth2.JWTSigningPrivateKeyFile, filepath.Join(t.TempDir(), "jwt-ecdsa-256.priv"))() + defer test.MockVariableValue(&setting.OAuth2.JWTSigningAlgorithm, "ES256")() + + parsedKey := loadKey(t) + + ecdsaPrivateKey := parsedKey.(*ecdsa.PrivateKey) + assert.EqualValues(t, 256, ecdsaPrivateKey.Params().BitSize) + }) + + t.Run("ECDSA-384", func(t *testing.T) { + defer test.MockVariableValue(&setting.OAuth2.JWTSigningPrivateKeyFile, filepath.Join(t.TempDir(), "jwt-ecdsa-384.priv"))() + defer test.MockVariableValue(&setting.OAuth2.JWTSigningAlgorithm, "ES384")() + + parsedKey := loadKey(t) + + ecdsaPrivateKey := parsedKey.(*ecdsa.PrivateKey) + assert.EqualValues(t, 384, ecdsaPrivateKey.Params().BitSize) + }) + + t.Run("ECDSA-512", func(t *testing.T) { + defer test.MockVariableValue(&setting.OAuth2.JWTSigningPrivateKeyFile, filepath.Join(t.TempDir(), "jwt-ecdsa-512.priv"))() + defer test.MockVariableValue(&setting.OAuth2.JWTSigningAlgorithm, "ES512")() + + parsedKey := loadKey(t) + + ecdsaPrivateKey := parsedKey.(*ecdsa.PrivateKey) + assert.EqualValues(t, 521, ecdsaPrivateKey.Params().BitSize) + }) + + t.Run("EdDSA", func(t *testing.T) { + defer test.MockVariableValue(&setting.OAuth2.JWTSigningPrivateKeyFile, filepath.Join(t.TempDir(), "jwt-eddsa.priv"))() + defer test.MockVariableValue(&setting.OAuth2.JWTSigningAlgorithm, "EdDSA")() + + parsedKey := loadKey(t) + + assert.NotNil(t, parsedKey.(ed25519.PrivateKey)) + }) +} From 969a6ab24a800e8446b6222d3fd6466538e11bb5 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sat, 16 Nov 2024 15:25:37 +0100 Subject: [PATCH 30/49] chore(release-notes): notes for the week 2024-46 weekly cherry pick --- release-notes/5988.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 release-notes/5988.md diff --git a/release-notes/5988.md b/release-notes/5988.md new file mode 100644 index 0000000000..52add6347e --- /dev/null +++ b/release-notes/5988.md @@ -0,0 +1 @@ +fix: [commit](https://codeberg.org/forgejo/forgejo/commit/fc26becba4b08877a726f2e7e453992310245fe5) when a tag was removed and a release existed for that tag, it would be broken. The release is no longer broken the tag can be added again. From e4eb82b7382a9ef110fe9c6f970d26eae9394c13 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 16 Nov 2024 15:51:59 +0100 Subject: [PATCH 31/49] fix: use better code to group UID and stopwatches - Instead of having code that relied on the result being sorted (which wasn't specified in the query and therefore not safe to assume so). Use a map where it doesn't care if the result that we get from the database is sorted or not. - Added unit test. --- .../TestGetUIDsAndStopwatch/stopwatch.yml | 11 +++++ models/issues/stopwatch.go | 23 ++--------- models/issues/stopwatch_test.go | 40 +++++++++++++++++++ modules/eventsource/manager_run.go | 6 +-- 4 files changed, 58 insertions(+), 22 deletions(-) create mode 100644 models/issues/TestGetUIDsAndStopwatch/stopwatch.yml diff --git a/models/issues/TestGetUIDsAndStopwatch/stopwatch.yml b/models/issues/TestGetUIDsAndStopwatch/stopwatch.yml new file mode 100644 index 0000000000..f564e4b389 --- /dev/null +++ b/models/issues/TestGetUIDsAndStopwatch/stopwatch.yml @@ -0,0 +1,11 @@ +- + id: 3 + user_id: 1 + issue_id: 2 + created_unix: 1500988004 + +- + id: 4 + user_id: 3 + issue_id: 0 + created_unix: 1500988003 diff --git a/models/issues/stopwatch.go b/models/issues/stopwatch.go index fd9c7d7875..93eaf8845d 100644 --- a/models/issues/stopwatch.go +++ b/models/issues/stopwatch.go @@ -60,34 +60,19 @@ func getStopwatch(ctx context.Context, userID, issueID int64) (sw *Stopwatch, ex return sw, exists, err } -// UserIDCount is a simple coalition of UserID and Count -type UserStopwatch struct { - UserID int64 - StopWatches []*Stopwatch -} - // GetUIDsAndNotificationCounts between the two provided times -func GetUIDsAndStopwatch(ctx context.Context) ([]*UserStopwatch, error) { +func GetUIDsAndStopwatch(ctx context.Context) (map[int64][]*Stopwatch, error) { sws := []*Stopwatch{} if err := db.GetEngine(ctx).Where("issue_id != 0").Find(&sws); err != nil { return nil, err } + res := map[int64][]*Stopwatch{} if len(sws) == 0 { - return []*UserStopwatch{}, nil + return res, nil } - lastUserID := int64(-1) - res := []*UserStopwatch{} for _, sw := range sws { - if lastUserID == sw.UserID { - lastUserStopwatch := res[len(res)-1] - lastUserStopwatch.StopWatches = append(lastUserStopwatch.StopWatches, sw) - } else { - res = append(res, &UserStopwatch{ - UserID: sw.UserID, - StopWatches: []*Stopwatch{sw}, - }) - } + res[sw.UserID] = append(res[sw.UserID], sw) } return res, nil } diff --git a/models/issues/stopwatch_test.go b/models/issues/stopwatch_test.go index 68a11acd96..af86e8b1d8 100644 --- a/models/issues/stopwatch_test.go +++ b/models/issues/stopwatch_test.go @@ -4,12 +4,14 @@ package issues_test import ( + "path/filepath" "testing" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" "github.com/stretchr/testify/assert" @@ -77,3 +79,41 @@ func TestCreateOrStopIssueStopwatch(t *testing.T) { unittest.AssertNotExistsBean(t, &issues_model.Stopwatch{UserID: 2, IssueID: 2}) unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{UserID: 2, IssueID: 2}) } + +func TestGetUIDsAndStopwatch(t *testing.T) { + defer unittest.OverrideFixtures( + unittest.FixturesOptions{ + Dir: filepath.Join(setting.AppWorkPath, "models/fixtures/"), + Base: setting.AppWorkPath, + Dirs: []string{"models/issues/TestGetUIDsAndStopwatch/"}, + }, + )() + require.NoError(t, unittest.PrepareTestDatabase()) + + uidStopwatches, err := issues_model.GetUIDsAndStopwatch(db.DefaultContext) + require.NoError(t, err) + assert.EqualValues(t, map[int64][]*issues_model.Stopwatch{ + 1: { + { + ID: 1, + UserID: 1, + IssueID: 1, + CreatedUnix: timeutil.TimeStamp(1500988001), + }, + { + ID: 3, + UserID: 1, + IssueID: 2, + CreatedUnix: timeutil.TimeStamp(1500988004), + }, + }, + 2: { + { + ID: 2, + UserID: 2, + IssueID: 2, + CreatedUnix: timeutil.TimeStamp(1500988002), + }, + }, + }, uidStopwatches) +} diff --git a/modules/eventsource/manager_run.go b/modules/eventsource/manager_run.go index f66dc78c7e..d4d95ae72a 100644 --- a/modules/eventsource/manager_run.go +++ b/modules/eventsource/manager_run.go @@ -90,8 +90,8 @@ loop: return } - for _, userStopwatches := range usersStopwatches { - apiSWs, err := convert.ToStopWatches(ctx, userStopwatches.StopWatches) + for uid, stopwatches := range usersStopwatches { + apiSWs, err := convert.ToStopWatches(ctx, stopwatches) if err != nil { if !issues_model.IsErrIssueNotExist(err) { log.Error("Unable to APIFormat stopwatches: %v", err) @@ -103,7 +103,7 @@ loop: log.Error("Unable to marshal stopwatches: %v", err) continue } - m.SendMessage(userStopwatches.UserID, &Event{ + m.SendMessage(uid, &Event{ Name: "stopwatches", Data: string(dataBs), }) From e434ecdacaa0ea8004317fae1b355ad9bd8f274e Mon Sep 17 00:00:00 2001 From: angelnu Date: Sat, 16 Nov 2024 18:12:40 +0100 Subject: [PATCH 32/49] check IsCommitExist --- routers/api/v1/repo/pull.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index b4749cac37..0238e18ace 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1111,7 +1111,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) ctx.Repo.PullRequest.SameRepo = isSameRepo log.Trace("Repo path: %q, base branch: %q, head branch: %q", ctx.Repo.GitRepo.Path, baseBranch, headBranch) // Check if base branch is valid. - if !ctx.Repo.GitRepo.IsBranchExist(baseBranch) && !ctx.Repo.GitRepo.IsTagExist(baseBranch) { + if !ctx.Repo.GitRepo.IsCommitExist(baseBranch) && !ctx.Repo.GitRepo.IsBranchExist(baseBranch) && !ctx.Repo.GitRepo.IsTagExist(baseBranch) { ctx.NotFound("BaseNotExist") return nil, nil, nil, "", "" } @@ -1186,7 +1186,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) } // Check if head branch is valid. - if !headGitRepo.IsBranchExist(headBranch) && !headGitRepo.IsTagExist(headBranch) { + if !ctx.Repo.GitRepo.IsCommitExist(baseBranch) && !headGitRepo.IsBranchExist(headBranch) && !headGitRepo.IsTagExist(headBranch) { headGitRepo.Close() ctx.NotFound() return nil, nil, nil, "", "" From d2dc4fae3ae8d4810f9f9b537189c897361b58ab Mon Sep 17 00:00:00 2001 From: angelnu Date: Sat, 16 Nov 2024 18:12:40 +0100 Subject: [PATCH 33/49] review changes --- routers/api/v1/repo/pull.go | 48 ++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 0238e18ace..6ca23f1e6d 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1110,10 +1110,19 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) ctx.Repo.PullRequest.SameRepo = isSameRepo log.Trace("Repo path: %q, base branch: %q, head branch: %q", ctx.Repo.GitRepo.Path, baseBranch, headBranch) + // Check if base branch is valid. - if !ctx.Repo.GitRepo.IsCommitExist(baseBranch) && !ctx.Repo.GitRepo.IsBranchExist(baseBranch) && !ctx.Repo.GitRepo.IsTagExist(baseBranch) { - ctx.NotFound("BaseNotExist") - return nil, nil, nil, "", "" + baseIsCommit := ctx.Repo.GitRepo.IsCommitExist(baseBranch) + baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(baseBranch) + baseIsTag := ctx.Repo.GitRepo.IsTagExist(baseBranch) + if !baseIsCommit && !baseIsBranch && !baseIsTag { + // Check for short SHA usage + if baseCommit, _ := ctx.Repo.GitRepo.GetCommit(baseBranch); baseCommit != nil { + baseBranch = baseCommit.ID.String() + } else { + ctx.NotFound("BaseNotExist") + return nil, nil, nil, "", "" + } } // Check if current user has fork of repository or in the same repository. @@ -1186,13 +1195,34 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) } // Check if head branch is valid. - if !ctx.Repo.GitRepo.IsCommitExist(baseBranch) && !headGitRepo.IsBranchExist(headBranch) && !headGitRepo.IsTagExist(headBranch) { - headGitRepo.Close() - ctx.NotFound() - return nil, nil, nil, "", "" - } + headIsCommit := headGitRepo.IsBranchExist(headBranch) + headIsBranch := headGitRepo.IsTagExist(headBranch) + headIsTag := headGitRepo.IsCommitExist(baseBranch) + if !headIsCommit && !headIsBranch && !headIsTag { + // Check if headBranch is short sha commit hash + if headCommit, _ := headGitRepo.GetCommit(headBranch); headCommit != nil { + headBranch = headCommit.ID.String() + } else { + headGitRepo.Close() + ctx.NotFound("IsRefExist", nil) + return nil, nil, nil, "", "" + } + } - compareInfo, err := headGitRepo.GetCompareInfo(repo_model.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch, false, false) + baseBranchRef := baseBranch + if baseIsBranch { + baseBranchRef = git.BranchPrefix + baseBranch + } else if baseIsTag { + baseBranchRef = git.TagPrefix + baseBranch + } + headBranchRef := headBranch + if headIsBranch { + headBranchRef = headBranch + } else if headIsTag { + headBranchRef = headBranch + } + + compareInfo, err := headGitRepo.GetCompareInfo(repo_model.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranchRef, headBranchRef, false, false) if err != nil { headGitRepo.Close() ctx.Error(http.StatusInternalServerError, "GetCompareInfo", err) From 1b9d1240eb713c0e5ee8754a1d9bfd1f0926d44c Mon Sep 17 00:00:00 2001 From: angelnu Date: Sat, 16 Nov 2024 18:12:40 +0100 Subject: [PATCH 34/49] add test --- tests/integration/api_repo_compare_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/integration/api_repo_compare_test.go b/tests/integration/api_repo_compare_test.go index f3188eb49f..17847ec360 100644 --- a/tests/integration/api_repo_compare_test.go +++ b/tests/integration/api_repo_compare_test.go @@ -36,3 +36,24 @@ func TestAPICompareBranches(t *testing.T) { assert.Equal(t, 2, apiResp.TotalCommits) assert.Len(t, apiResp.Commits, 2) } + +func TestAPICompareCommits(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + // Login as User2. + session := loginUser(t, user.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + + repoName := "repo20" + + req := NewRequestf(t, "GET", "/api/v1/repos/user2/%s/compare/c8e31bc...8babce9", repoName). + AddTokenAuth(token) + resp := MakeRequest(t, req, http.StatusOK) + + var apiResp *api.Compare + DecodeJSON(t, resp, &apiResp) + + assert.Equal(t, 2, apiResp.TotalCommits) + assert.Len(t, apiResp.Commits, 2) +} \ No newline at end of file From 01c9c1953690f227dea5d60f814643280497ea46 Mon Sep 17 00:00:00 2001 From: Angel Nunez Mencias Date: Sat, 16 Nov 2024 18:12:40 +0100 Subject: [PATCH 35/49] fmt --- routers/api/v1/repo/pull.go | 66 +++++++++++----------- tests/integration/api_repo_compare_test.go | 2 +- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 6ca23f1e6d..1a791e8dd5 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1113,16 +1113,16 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) // Check if base branch is valid. baseIsCommit := ctx.Repo.GitRepo.IsCommitExist(baseBranch) - baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(baseBranch) - baseIsTag := ctx.Repo.GitRepo.IsTagExist(baseBranch) - if !baseIsCommit && !baseIsBranch && !baseIsTag { - // Check for short SHA usage - if baseCommit, _ := ctx.Repo.GitRepo.GetCommit(baseBranch); baseCommit != nil { - baseBranch = baseCommit.ID.String() - } else { - ctx.NotFound("BaseNotExist") - return nil, nil, nil, "", "" - } + baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(baseBranch) + baseIsTag := ctx.Repo.GitRepo.IsTagExist(baseBranch) + if !baseIsCommit && !baseIsBranch && !baseIsTag { + // Check for short SHA usage + if baseCommit, _ := ctx.Repo.GitRepo.GetCommit(baseBranch); baseCommit != nil { + baseBranch = baseCommit.ID.String() + } else { + ctx.NotFound("BaseNotExist") + return nil, nil, nil, "", "" + } } // Check if current user has fork of repository or in the same repository. @@ -1197,30 +1197,30 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) // Check if head branch is valid. headIsCommit := headGitRepo.IsBranchExist(headBranch) headIsBranch := headGitRepo.IsTagExist(headBranch) - headIsTag := headGitRepo.IsCommitExist(baseBranch) - if !headIsCommit && !headIsBranch && !headIsTag { - // Check if headBranch is short sha commit hash - if headCommit, _ := headGitRepo.GetCommit(headBranch); headCommit != nil { - headBranch = headCommit.ID.String() - } else { - headGitRepo.Close() - ctx.NotFound("IsRefExist", nil) - return nil, nil, nil, "", "" - } - } + headIsTag := headGitRepo.IsCommitExist(baseBranch) + if !headIsCommit && !headIsBranch && !headIsTag { + // Check if headBranch is short sha commit hash + if headCommit, _ := headGitRepo.GetCommit(headBranch); headCommit != nil { + headBranch = headCommit.ID.String() + } else { + headGitRepo.Close() + ctx.NotFound("IsRefExist", nil) + return nil, nil, nil, "", "" + } + } - baseBranchRef := baseBranch - if baseIsBranch { - baseBranchRef = git.BranchPrefix + baseBranch - } else if baseIsTag { - baseBranchRef = git.TagPrefix + baseBranch - } - headBranchRef := headBranch - if headIsBranch { - headBranchRef = headBranch - } else if headIsTag { - headBranchRef = headBranch - } + baseBranchRef := baseBranch + if baseIsBranch { + baseBranchRef = git.BranchPrefix + baseBranch + } else if baseIsTag { + baseBranchRef = git.TagPrefix + baseBranch + } + headBranchRef := headBranch + if headIsBranch { + headBranchRef = headBranch + } else if headIsTag { + headBranchRef = headBranch + } compareInfo, err := headGitRepo.GetCompareInfo(repo_model.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranchRef, headBranchRef, false, false) if err != nil { diff --git a/tests/integration/api_repo_compare_test.go b/tests/integration/api_repo_compare_test.go index 17847ec360..bd800b9e79 100644 --- a/tests/integration/api_repo_compare_test.go +++ b/tests/integration/api_repo_compare_test.go @@ -56,4 +56,4 @@ func TestAPICompareCommits(t *testing.T) { assert.Equal(t, 2, apiResp.TotalCommits) assert.Len(t, apiResp.Commits, 2) -} \ No newline at end of file +} From ca0cd42d7aea9df3bde3a29e249bf178a5ef8557 Mon Sep 17 00:00:00 2001 From: Angel Nunez Mencias Date: Sat, 16 Nov 2024 22:31:14 +0100 Subject: [PATCH 36/49] simplify test based on feedback --- tests/integration/api_repo_compare_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/integration/api_repo_compare_test.go b/tests/integration/api_repo_compare_test.go index bd800b9e79..765d0cef08 100644 --- a/tests/integration/api_repo_compare_test.go +++ b/tests/integration/api_repo_compare_test.go @@ -45,9 +45,7 @@ func TestAPICompareCommits(t *testing.T) { session := loginUser(t, user.Name) token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - repoName := "repo20" - - req := NewRequestf(t, "GET", "/api/v1/repos/user2/%s/compare/c8e31bc...8babce9", repoName). + req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo20/compare/c8e31bc...8babce9"). AddTokenAuth(token) resp := MakeRequest(t, req, http.StatusOK) From da40383cf4c351610df0d5a944376cda7102047e Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 17 Nov 2024 00:42:31 +0000 Subject: [PATCH 37/49] Revert defaulting to EdDSA - Apparently JWT actually checks when doing a JWT operation if the key type is valid and not on startup, this caused errors unfortunately. --- modules/setting/oauth2.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/setting/oauth2.go b/modules/setting/oauth2.go index c989460e5d..49288e2639 100644 --- a/modules/setting/oauth2.go +++ b/modules/setting/oauth2.go @@ -106,7 +106,7 @@ var OAuth2 = struct { AccessTokenExpirationTime: 3600, RefreshTokenExpirationTime: 730, InvalidateRefreshTokens: true, - JWTSigningAlgorithm: "EdDSA", + JWTSigningAlgorithm: "RS256", JWTSigningPrivateKeyFile: "jwt/private.pem", MaxTokenLength: math.MaxInt16, DefaultApplications: []string{"git-credential-oauth", "git-credential-manager", "tea"}, From 9701e5e0ff9d895d83dd09a6d32c388047f7d9bf Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 17 Nov 2024 02:06:51 +0100 Subject: [PATCH 38/49] fix: remember fuzzy for open/close state - Remember if fuzzy was set or not for the open/close/all states. - Use `fuzzy=false` for test, as `fuzzy=true` is the default (this is the opposite of all the other values). - Remove `ctx.Link` prefix for open/close states, this makes them suspectible to the existing tests (the other filter links are also in the format of simply having `?xx=xx&yy=yy`). - Fix typo in test name. --- routers/web/repo/issue.go | 14 +++++++------- tests/integration/repo_test.go | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 6a485b0066..e67980a9a9 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -457,16 +457,16 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt ctx.Data["OpenCount"] = issueStats.OpenCount ctx.Data["ClosedCount"] = issueStats.ClosedCount ctx.Data["AllCount"] = issueStats.AllCount - linkStr := "%s?q=%s&type=%s&sort=%s&state=%s&labels=%s&milestone=%d&project=%d&assignee=%d&poster=%d&archived=%t" - ctx.Data["AllStatesLink"] = fmt.Sprintf(linkStr, ctx.Link, + linkStr := "?q=%s&type=%s&sort=%s&state=%s&labels=%s&milestone=%d&project=%d&assignee=%d&poster=%d&fuzzy=%t&archived=%t" + ctx.Data["AllStatesLink"] = fmt.Sprintf(linkStr, url.QueryEscape(keyword), url.QueryEscape(viewType), url.QueryEscape(sortType), "all", url.QueryEscape(selectLabels), - milestoneID, projectID, assigneeID, posterID, archived) - ctx.Data["OpenLink"] = fmt.Sprintf(linkStr, ctx.Link, + milestoneID, projectID, assigneeID, posterID, isFuzzy, archived) + ctx.Data["OpenLink"] = fmt.Sprintf(linkStr, url.QueryEscape(keyword), url.QueryEscape(viewType), url.QueryEscape(sortType), "open", url.QueryEscape(selectLabels), - milestoneID, projectID, assigneeID, posterID, archived) - ctx.Data["ClosedLink"] = fmt.Sprintf(linkStr, ctx.Link, + milestoneID, projectID, assigneeID, posterID, isFuzzy, archived) + ctx.Data["ClosedLink"] = fmt.Sprintf(linkStr, url.QueryEscape(keyword), url.QueryEscape(viewType), url.QueryEscape(sortType), "closed", url.QueryEscape(selectLabels), - milestoneID, projectID, assigneeID, posterID, archived) + milestoneID, projectID, assigneeID, posterID, isFuzzy, archived) ctx.Data["SelLabelIDs"] = labelIDs ctx.Data["SelectLabels"] = selectLabels ctx.Data["ViewType"] = viewType diff --git a/tests/integration/repo_test.go b/tests/integration/repo_test.go index 85a5002cec..a424863346 100644 --- a/tests/integration/repo_test.go +++ b/tests/integration/repo_test.go @@ -1140,7 +1140,7 @@ func TestRepoIssueFilterLinks(t *testing.T) { t.Run("Fuzzy", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", "/user2/repo1/issues?fuzzy=true") + req := NewRequest(t, "GET", "/user2/repo1/issues?fuzzy=false") resp := MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) @@ -1157,7 +1157,7 @@ func TestRepoIssueFilterLinks(t *testing.T) { assert.Contains(t, href, "&project=") assert.Contains(t, href, "&assignee=") assert.Contains(t, href, "&poster=") - assert.Contains(t, href, "&fuzzy=true") + assert.Contains(t, href, "&fuzzy=false") }) assert.True(t, called) }) @@ -1237,7 +1237,7 @@ func TestRepoIssueFilterLinks(t *testing.T) { assert.True(t, called) }) - t.Run("Miilestone", func(t *testing.T) { + t.Run("Milestone", func(t *testing.T) { defer tests.PrintCurrentTest(t)() req := NewRequest(t, "GET", "/user2/repo1/issues?milestone=1") From b5161325ef8b1eba306f97f168fb0d59853d8600 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 17 Nov 2024 13:33:21 +0100 Subject: [PATCH 39/49] chore(ci): make end-to-end job copy/pastable Refs: forgejo/forgejo#5999 --- .../workflows/cascade-setup-end-to-end.yml | 54 ++++++------------- 1 file changed, 16 insertions(+), 38 deletions(-) diff --git a/.forgejo/workflows/cascade-setup-end-to-end.yml b/.forgejo/workflows/cascade-setup-end-to-end.yml index 28400730f5..1c12031b56 100644 --- a/.forgejo/workflows/cascade-setup-end-to-end.yml +++ b/.forgejo/workflows/cascade-setup-end-to-end.yml @@ -1,60 +1,38 @@ # Copyright 2024 The Forgejo Authors # SPDX-License-Identifier: MIT # -# To modify this workflow: -# -# - push it to the wip-ci-end-to-end branch on the forgejo repository -# otherwise it will not have access to the secrets required to push -# the cascading PR -# -# - once it works, open a pull request for the sake of keeping track -# of the change even if the PR won't run it because it will use -# whatever is in the default branch instead -# -# - after it is merged, double check it works by setting the -# run-end-to-end-test on a pull request (any pull request will do) -# -name: end-to-end +name: issue-labels on: - push: - branches: - - 'wip-ci-end-to-end' pull_request_target: types: - labeled jobs: - info: - if: vars.ROLE == 'forgejo-coding' - runs-on: docker - container: - image: code.forgejo.org/oci/node:20-bookworm - steps: - - name: event - run: | - echo github.event.pull_request.head.repo.fork = ${{ github.event.pull_request.head.repo.fork }} - echo github.event.action = ${{ github.event.action }} - echo github.event.label - cat <<'EOF' - ${{ toJSON(github.event.label) }} - EOF - cat <<'EOF' - ${{ toJSON(github.event) }} - EOF - - cascade: + end-to-end: if: > - vars.ROLE == 'forgejo-coding' && ( + vars.ROLE == 'forgejo-coding' && + + secrets.END_TO_END_CASCADING_PR_DESTINATION != '' && + secrets.END_TO_END_CASCADING_PR_ORIGIN != '' && + + ( github.event_name == 'push' || ( - github.event.action == 'label_updated' && github.event.label.name == 'run-end-to-end-tests' + github.event_name == 'pull_request_target' && + github.event.action == 'label_updated' && + github.event.label.name == 'run-end-to-end-tests' ) ) runs-on: docker container: image: code.forgejo.org/oci/node:20-bookworm steps: + - name: Debug info + run: | + cat <<'EOF' + ${{ toJSON(github) }} + EOF - uses: actions/checkout@v4 with: fetch-depth: '0' From 64a89c8d3307fb4e44c5cab3ea93154aabaf943d Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 17 Nov 2024 14:40:36 +0100 Subject: [PATCH 40/49] chore(ci): make merge-conditions job copy/pastable Refs: forgejo/forgejo#5999 --- .forgejo/workflows/merge-requirements.yml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.forgejo/workflows/merge-requirements.yml b/.forgejo/workflows/merge-requirements.yml index 7835825fec..fe18aa1139 100644 --- a/.forgejo/workflows/merge-requirements.yml +++ b/.forgejo/workflows/merge-requirements.yml @@ -1,7 +1,7 @@ # Copyright 2024 The Forgejo Authors # SPDX-License-Identifier: MIT -name: requirements +name: issue-labels on: pull_request: @@ -9,19 +9,26 @@ on: - labeled - edited - opened - - synchronize jobs: merge-conditions: - if: vars.ROLE == 'forgejo-coding' + if: > + vars.ROLE == 'forgejo-coding' && + + github.event_name == 'pull_request' && + ( + github.event.action == 'label_updated' || + github.event.action == 'edited' || + github.event.action == 'opened' + ) runs-on: docker container: image: 'code.forgejo.org/oci/node:20-bookworm' steps: - - name: Debug output + - name: Debug info run: | cat <<'EOF' - ${{ toJSON(github.event) }} + ${{ toJSON(github) }} EOF - name: Missing test label if: > From b6869d643ecd79d5781a0a9bec9b81a385833396 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 17 Nov 2024 19:13:14 +0100 Subject: [PATCH 41/49] chore(ci): make backporting job copy/pastable Refs: forgejo/forgejo#5999 --- .forgejo/workflows/backport.yml | 35 ++++++++++----------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/.forgejo/workflows/backport.yml b/.forgejo/workflows/backport.yml index f2b8520a9b..614a2099af 100644 --- a/.forgejo/workflows/backport.yml +++ b/.forgejo/workflows/backport.yml @@ -1,27 +1,8 @@ # Copyright 2024 The Forgejo Authors # SPDX-License-Identifier: MIT # -# To modify this workflow: -# -# - change pull_request_target: to pull_request: -# so that it runs from a pull request instead of the default branch -# -# - push it to the wip-ci-backport branch on the forgejo repository -# otherwise it will not have access to the secrets required to push -# the PR -# -# - open a pull request targetting wip-ci-backport that includes a change -# that can be backported without conflict in v1.21 and set the -# `backport/v1.21` label. -# -# - once it works, open a pull request for the sake of keeping track -# of the change even if the PR won't run it because it will use -# whatever is in the default branch instead -# -# - after it is merged, double check it works by setting a -# `backport/v1.21` label on a merged pull request that can be backported -# without conflict. -# +name: issue-labels + on: pull_request_target: types: @@ -31,16 +12,20 @@ on: jobs: backporting: if: > - ( vars.ROLE == 'forgejo-coding' ) && ( - github.event.pull_request.merged - && + vars.ROLE == 'forgejo-coding' && + + secrets.BACKPORT_TOKEN != '' && + + github.event_name == 'pull_request_target' && + ( + github.event.pull_request.merged && contains(toJSON(github.event.pull_request.labels), 'backport/v') ) runs-on: docker container: image: 'code.forgejo.org/oci/node:20-bookworm' steps: - - name: event info + - name: Debug info run: | cat <<'EOF' ${{ toJSON(github) }} From 693f7731f9e8d9bb786e024d9f2239941767501d Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 17 Nov 2024 02:29:10 +0100 Subject: [PATCH 42/49] fix: check read permissions for code owner review requests - Only send a review request based on the code owner file if the code owner user has read permissions to the pull requests of that repository. - This avoids leaking title of PRs from private repository when a CODEOWNER file is present which contains users that do not have access to the private repository. - Found by @oliverpool. - Integration test added. --- services/issue/pull.go | 8 +++++- tests/integration/codeowner_test.go | 38 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/services/issue/pull.go b/services/issue/pull.go index 896802108d..3b61c00afa 100644 --- a/services/issue/pull.go +++ b/services/issue/pull.go @@ -10,6 +10,8 @@ import ( issues_model "code.gitea.io/gitea/models/issues" org_model "code.gitea.io/gitea/models/organization" + access_model "code.gitea.io/gitea/models/perm/access" + "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" @@ -117,7 +119,11 @@ func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue, } for _, u := range uniqUsers { - if u.ID != issue.Poster.ID { + permission, err := access_model.GetUserRepoPermission(ctx, issue.Repo, u) + if err != nil { + return nil, fmt.Errorf("GetUserRepoPermission: %w", err) + } + if u.ID != issue.Poster.ID && permission.CanRead(unit.TypePullRequests) { comment, err := issues_model.AddReviewRequest(ctx, issue, u, issue.Poster) if err != nil { log.Warn("Failed add assignee user: %s to PR review: %s#%d, error: %s", u.Name, pr.BaseRepo.Name, pr.ID, err) diff --git a/tests/integration/codeowner_test.go b/tests/integration/codeowner_test.go index b324711cb5..6ef354650b 100644 --- a/tests/integration/codeowner_test.go +++ b/tests/integration/codeowner_test.go @@ -14,6 +14,7 @@ import ( "testing" "time" + "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" @@ -159,5 +160,42 @@ func TestCodeOwner(t *testing.T) { pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "branch"}) unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 4}) }) + + t.Run("Codeowner user with no permission", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Make repository private, only user2 (owner of repository) has now access to this repository. + repo.IsPrivate = true + _, err := db.GetEngine(db.DefaultContext).Cols("is_private").Update(repo) + require.NoError(t, err) + + err = os.WriteFile(path.Join(dstPath, "README.md"), []byte("## very senstive info"), 0o666) + require.NoError(t, err) + + err = git.AddChanges(dstPath, true) + require.NoError(t, err) + + err = git.CommitChanges(dstPath, git.CommitChangesOptions{ + Committer: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Author: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Message: "Add secrets to the README.", + }) + require.NoError(t, err) + + err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/main", "-o", "topic=codeowner-private").Run(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) + + // In CODEOWNERS file the codeowner for README.md is user5, but does not have access to this private repository. + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-private"}) + unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) + }) }) } From 662e9c53d9ec1059190a0d5ab296b4cdc1f834b2 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 17 Nov 2024 21:48:29 +0100 Subject: [PATCH 43/49] fix: vertical center the date on GPG keys - Ensure the 'added on' and the date are centered vertically the same. - Regression #5796 --- templates/user/settings/keys_gpg.tmpl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/templates/user/settings/keys_gpg.tmpl b/templates/user/settings/keys_gpg.tmpl index 05a4161661..23cd86fce7 100644 --- a/templates/user/settings/keys_gpg.tmpl +++ b/templates/user/settings/keys_gpg.tmpl @@ -63,9 +63,11 @@ {{ctx.Locale.Tr "settings.subkeys"}}: {{range .SubsKey}} {{.PaddedKeyID}} {{end}}
-

{{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .AddedUnix)}}

+

+ {{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .AddedUnix)}} - -

{{if not .ExpiredUnix.IsZero}}{{ctx.Locale.Tr "settings.valid_until_date" (DateUtils.AbsoluteShort .ExpiredUnix)}}{{else}}{{ctx.Locale.Tr "settings.valid_forever"}}{{end}}

+ {{if not .ExpiredUnix.IsZero}}{{ctx.Locale.Tr "settings.valid_until_date" (DateUtils.AbsoluteShort .ExpiredUnix)}}{{else}}{{ctx.Locale.Tr "settings.valid_forever"}}{{end}} +

From 4163402f5ec88e75aa0144c62b209afc3ff8d5dc Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 17 Nov 2024 21:23:23 +0000 Subject: [PATCH 44/49] Update dependency vue to v3.5.13 (forgejo) (#5981) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 110 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/package-lock.json b/package-lock.json index a7f66ead2f..aadac8aa79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,7 +51,7 @@ "tributejs": "5.1.3", "uint8-to-base64": "0.2.0", "vanilla-colorful": "0.7.2", - "vue": "3.5.12", + "vue": "3.5.13", "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", "vue3-calendar-heatmap": "2.0.5", @@ -5326,42 +5326,42 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.12.tgz", - "integrity": "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz", + "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==", "license": "MIT", "dependencies": { "@babel/parser": "^7.25.3", - "@vue/shared": "3.5.12", + "@vue/shared": "3.5.13", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz", - "integrity": "sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz", + "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.12", - "@vue/shared": "3.5.12" + "@vue/compiler-core": "3.5.13", + "@vue/shared": "3.5.13" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz", - "integrity": "sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", + "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==", "license": "MIT", "dependencies": { "@babel/parser": "^7.25.3", - "@vue/compiler-core": "3.5.12", - "@vue/compiler-dom": "3.5.12", - "@vue/compiler-ssr": "3.5.12", - "@vue/shared": "3.5.12", + "@vue/compiler-core": "3.5.13", + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13", "estree-walker": "^2.0.2", "magic-string": "^0.30.11", - "postcss": "^8.4.47", + "postcss": "^8.4.48", "source-map-js": "^1.2.0" } }, @@ -5375,63 +5375,63 @@ } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz", - "integrity": "sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz", + "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.12", - "@vue/shared": "3.5.12" + "@vue/compiler-dom": "3.5.13", + "@vue/shared": "3.5.13" } }, "node_modules/@vue/reactivity": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.12.tgz", - "integrity": "sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz", + "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==", "license": "MIT", "dependencies": { - "@vue/shared": "3.5.12" + "@vue/shared": "3.5.13" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.12.tgz", - "integrity": "sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz", + "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.12", - "@vue/shared": "3.5.12" + "@vue/reactivity": "3.5.13", + "@vue/shared": "3.5.13" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.12.tgz", - "integrity": "sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz", + "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.12", - "@vue/runtime-core": "3.5.12", - "@vue/shared": "3.5.12", + "@vue/reactivity": "3.5.13", + "@vue/runtime-core": "3.5.13", + "@vue/shared": "3.5.13", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.12.tgz", - "integrity": "sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz", + "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==", "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.5.12", - "@vue/shared": "3.5.12" + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13" }, "peerDependencies": { - "vue": "3.5.12" + "vue": "3.5.13" } }, "node_modules/@vue/shared": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.12.tgz", - "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz", + "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==", "license": "MIT" }, "node_modules/@vue/test-utils": { @@ -16405,16 +16405,16 @@ "license": "MIT" }, "node_modules/vue": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.12.tgz", - "integrity": "sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", + "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.12", - "@vue/compiler-sfc": "3.5.12", - "@vue/runtime-dom": "3.5.12", - "@vue/server-renderer": "3.5.12", - "@vue/shared": "3.5.12" + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-sfc": "3.5.13", + "@vue/runtime-dom": "3.5.13", + "@vue/server-renderer": "3.5.13", + "@vue/shared": "3.5.13" }, "peerDependencies": { "typescript": "*" diff --git a/package.json b/package.json index a3e012d673..068459be14 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "tributejs": "5.1.3", "uint8-to-base64": "0.2.0", "vanilla-colorful": "0.7.2", - "vue": "3.5.12", + "vue": "3.5.13", "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", "vue3-calendar-heatmap": "2.0.5", From 387ed7e072955664d42044ee5caa0c74d39076db Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 18 Nov 2024 00:05:26 +0000 Subject: [PATCH 45/49] Lock file maintenance --- package-lock.json | 741 +++++++++++++++-------------- poetry.lock | 214 +++++---- web_src/fomantic/package-lock.json | 24 +- 3 files changed, 498 insertions(+), 481 deletions(-) diff --git a/package-lock.json b/package-lock.json index aadac8aa79..9eae33a5f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -153,9 +153,9 @@ } }, "node_modules/@asyncapi/specs": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-4.3.1.tgz", - "integrity": "sha512-EfexhJu/lwF8OdQDm28NKLJHFkx0Gb6O+rcezhZYLPIoNYKXJMh2J1vFGpwmfAcTTh+ffK44Oc2Hs1Q4sLBp+A==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.8.0.tgz", + "integrity": "sha512-1i6xs8+IOh6U5T7yH+bCMGQBF+m7kP/NpwyAlt++XaDQutoGCgACf24mQBgcDVqDWWoY81evQv+9ABvw0BviVg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -411,9 +411,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz", + "integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==", "dev": true, "license": "MIT", "dependencies": { @@ -2910,9 +2910,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", - "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, "license": "MIT", "engines": { @@ -2969,9 +2969,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", - "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", "dev": true, "license": "MIT", "dependencies": { @@ -3074,9 +3074,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz", - "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", + "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3144,6 +3144,20 @@ "node": ">=18.18.0" } }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -3159,9 +3173,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -3648,9 +3662,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.3.tgz", - "integrity": "sha512-ufb2CH2KfBWPJok95frEZZ82LtDl0A6QKTa8MoM+cWwDZvVGl5/jNb79pIhRvAalUu+7LD91VYR0nwRD799HkQ==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.2.tgz", + "integrity": "sha512-Tj+j7Pyzd15wAdSJswvs5CJzJNV+qqSUcr/aCD+jpQSBtXvGnV0pnrjoc8zFTe9fcKCatkpFpOO7yAzpO998HA==", "cpu": [ "arm" ], @@ -3662,9 +3676,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.3.tgz", - "integrity": "sha512-iAHpft/eQk9vkWIV5t22V77d90CRofgR2006UiCjHcHJFVI1E0oBkQIAbz+pLtthFw3hWEmVB4ilxGyBf48i2Q==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.2.tgz", + "integrity": "sha512-xsPeJgh2ThBpUqlLgRfiVYBEf/P1nWlWvReG+aBWfNv3XEBpa6ZCmxSVnxJgLgkNz4IbxpLy64h2gCmAAQLneQ==", "cpu": [ "arm64" ], @@ -3676,9 +3690,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.3.tgz", - "integrity": "sha512-QPW2YmkWLlvqmOa2OwrfqLJqkHm7kJCIMq9kOz40Zo9Ipi40kf9ONG5Sz76zszrmIZZ4hgRIkez69YnTHgEz1w==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.2.tgz", + "integrity": "sha512-KnXU4m9MywuZFedL35Z3PuwiTSn/yqRIhrEA9j+7OSkji39NzVkgxuxTYg5F8ryGysq4iFADaU5osSizMXhU2A==", "cpu": [ "arm64" ], @@ -3690,9 +3704,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.3.tgz", - "integrity": "sha512-KO0pN5x3+uZm1ZXeIfDqwcvnQ9UEGN8JX5ufhmgH5Lz4ujjZMAnxQygZAVGemFWn+ZZC0FQopruV4lqmGMshow==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.2.tgz", + "integrity": "sha512-Hj77A3yTvUeCIx/Vi+4d4IbYhyTwtHj07lVzUgpUq9YpJSEiGJj4vXMKwzJ3w5zp5v3PFvpJNgc/J31smZey6g==", "cpu": [ "x64" ], @@ -3704,9 +3718,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.3.tgz", - "integrity": "sha512-CsC+ZdIiZCZbBI+aRlWpYJMSWvVssPuWqrDy/zi9YfnatKKSLFCe6fjna1grHuo/nVaHG+kiglpRhyBQYRTK4A==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.2.tgz", + "integrity": "sha512-RjgKf5C3xbn8gxvCm5VgKZ4nn0pRAIe90J0/fdHUsgztd3+Zesb2lm2+r6uX4prV2eUByuxJNdt647/1KPRq5g==", "cpu": [ "arm64" ], @@ -3718,9 +3732,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.3.tgz", - "integrity": "sha512-F0nqiLThcfKvRQhZEzMIXOQG4EeX61im61VYL1jo4eBxv4aZRmpin6crnBJQ/nWnCsjH5F6J3W6Stdm0mBNqBg==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.2.tgz", + "integrity": "sha512-duq21FoXwQtuws+V9H6UZ+eCBc7fxSpMK1GQINKn3fAyd9DFYKPJNcUhdIKOrMFjLEJgQskoMoiuizMt+dl20g==", "cpu": [ "x64" ], @@ -3732,9 +3746,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.3.tgz", - "integrity": "sha512-KRSFHyE/RdxQ1CSeOIBVIAxStFC/hnBgVcaiCkQaVC+EYDtTe4X7z5tBkFyRoBgUGtB6Xg6t9t2kulnX6wJc6A==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.2.tgz", + "integrity": "sha512-6npqOKEPRZkLrMcvyC/32OzJ2srdPzCylJjiTJT2c0bwwSGm7nz2F9mNQ1WrAqCBZROcQn91Fno+khFhVijmFA==", "cpu": [ "arm" ], @@ -3746,9 +3760,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.3.tgz", - "integrity": "sha512-h6Q8MT+e05zP5BxEKz0vi0DhthLdrNEnspdLzkoFqGwnmOzakEHSlXfVyA4HJ322QtFy7biUAVFPvIDEDQa6rw==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.2.tgz", + "integrity": "sha512-V9Xg6eXtgBtHq2jnuQwM/jr2mwe2EycnopO8cbOvpzFuySCGtKlPCI3Hj9xup/pJK5Q0388qfZZy2DqV2J8ftw==", "cpu": [ "arm" ], @@ -3760,9 +3774,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.3.tgz", - "integrity": "sha512-fKElSyXhXIJ9pqiYRqisfirIo2Z5pTTve5K438URf08fsypXrEkVmShkSfM8GJ1aUyvjakT+fn2W7Czlpd/0FQ==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.2.tgz", + "integrity": "sha512-uCFX9gtZJoQl2xDTpRdseYuNqyKkuMDtH6zSrBTA28yTfKyjN9hQ2B04N5ynR8ILCoSDOrG/Eg+J2TtJ1e/CSA==", "cpu": [ "arm64" ], @@ -3774,9 +3788,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.3.tgz", - "integrity": "sha512-YlddZSUk8G0px9/+V9PVilVDC6ydMz7WquxozToozSnfFK6wa6ne1ATUjUvjin09jp34p84milxlY5ikueoenw==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.2.tgz", + "integrity": "sha512-/PU9P+7Rkz8JFYDHIi+xzHabOu9qEWR07L5nWLIUsvserrxegZExKCi2jhMZRd0ATdboKylu/K5yAXbp7fYFvA==", "cpu": [ "arm64" ], @@ -3788,9 +3802,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.3.tgz", - "integrity": "sha512-yNaWw+GAO8JjVx3s3cMeG5Esz1cKVzz8PkTJSfYzE5u7A+NvGmbVFEHP+BikTIyYWuz0+DX9kaA3pH9Sqxp69g==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.2.tgz", + "integrity": "sha512-eCHmol/dT5odMYi/N0R0HC8V8QE40rEpkyje/ZAXJYNNoSfrObOvG/Mn+s1F/FJyB7co7UQZZf6FuWnN6a7f4g==", "cpu": [ "ppc64" ], @@ -3802,9 +3816,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.3.tgz", - "integrity": "sha512-lWKNQfsbpv14ZCtM/HkjCTm4oWTKTfxPmr7iPfp3AHSqyoTz5AgLemYkWLwOBWc+XxBbrU9SCokZP0WlBZM9lA==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.2.tgz", + "integrity": "sha512-DEP3Njr9/ADDln3kNi76PXonLMSSMiCir0VHXxmGSHxCxDfQ70oWjHcJGfiBugzaqmYdTC7Y+8Int6qbnxPBIQ==", "cpu": [ "riscv64" ], @@ -3816,9 +3830,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.3.tgz", - "integrity": "sha512-HoojGXTC2CgCcq0Woc/dn12wQUlkNyfH0I1ABK4Ni9YXyFQa86Fkt2Q0nqgLfbhkyfQ6003i3qQk9pLh/SpAYw==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.2.tgz", + "integrity": "sha512-NHGo5i6IE/PtEPh5m0yw5OmPMpesFnzMIS/lzvN5vknnC1sXM5Z/id5VgcNPgpD+wHmIcuYYgW+Q53v+9s96lQ==", "cpu": [ "s390x" ], @@ -3830,9 +3844,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.3.tgz", - "integrity": "sha512-mnEOh4iE4USSccBOtcrjF5nj+5/zm6NcNhbSEfR3Ot0pxBwvEn5QVUXcuOwwPkapDtGZ6pT02xLoPaNv06w7KQ==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.2.tgz", + "integrity": "sha512-PaW2DY5Tan+IFvNJGHDmUrORadbe/Ceh8tQxi8cmdQVCCYsLoQo2cuaSj+AU+YRX8M4ivS2vJ9UGaxfuNN7gmg==", "cpu": [ "x64" ], @@ -3844,9 +3858,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.3.tgz", - "integrity": "sha512-rMTzawBPimBQkG9NKpNHvquIUTQPzrnPxPbCY1Xt+mFkW7pshvyIS5kYgcf74goxXOQk0CP3EoOC1zcEezKXhw==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.2.tgz", + "integrity": "sha512-dOlWEMg2gI91Qx5I/HYqOD6iqlJspxLcS4Zlg3vjk1srE67z5T2Uz91yg/qA8sY0XcwQrFzWWiZhMNERylLrpQ==", "cpu": [ "x64" ], @@ -3858,9 +3872,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.3.tgz", - "integrity": "sha512-2lg1CE305xNvnH3SyiKwPVsTVLCg4TmNCF1z7PSHX2uZY2VbUpdkgAllVoISD7JO7zu+YynpWNSKAtOrX3AiuA==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.2.tgz", + "integrity": "sha512-euMIv/4x5Y2/ImlbGl88mwKNXDsvzbWUlT7DFky76z2keajCtcbAsN9LUdmk31hAoVmJJYSThgdA0EsPeTr1+w==", "cpu": [ "arm64" ], @@ -3872,9 +3886,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.3.tgz", - "integrity": "sha512-9SjYp1sPyxJsPWuhOCX6F4jUMXGbVVd5obVpoVEi8ClZqo52ViZewA6eFz85y8ezuOA+uJMP5A5zo6Oz4S5rVQ==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.2.tgz", + "integrity": "sha512-RsnE6LQkUHlkC10RKngtHNLxb7scFykEbEwOFDjr3CeCMG+Rr+cKqlkKc2/wJ1u4u990urRHCbjz31x84PBrSQ==", "cpu": [ "ia32" ], @@ -3886,9 +3900,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.3.tgz", - "integrity": "sha512-HGZgRFFYrMrP3TJlq58nR1xy8zHKId25vhmm5S9jETEfDf6xybPxsavFTJaufe2zgOGYJBskGlj49CwtEuFhWQ==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.2.tgz", + "integrity": "sha512-foJM5vv+z2KQmn7emYdDLyTbkoO5bkHZE1oth2tWbQNGW7mX32d46Hz6T0MqXdWS2vBZhaEtHqdy9WYwGfiliA==", "cpu": [ "x64" ], @@ -4069,9 +4083,9 @@ } }, "node_modules/@stoplight/spectral-core": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-core/-/spectral-core-1.19.2.tgz", - "integrity": "sha512-Yx1j7d0VGEbsOCimPgl+L8w7ZuuOaxqGvXSUXgm9weoGR5idLQjPaTuHLdfdziR1gjqQdVTCEk/dN0cFfUKhow==", + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-core/-/spectral-core-1.19.3.tgz", + "integrity": "sha512-tc7B7EBKf1jBiA0iPpPspJahSBrTQ7PC8Jh4wYwquQHlrtc9VSJZJpXWKNetDlW6coKARLIoCDVaBBNBCloUeg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4080,7 +4094,7 @@ "@stoplight/path": "1.3.2", "@stoplight/spectral-parsers": "^1.0.0", "@stoplight/spectral-ref-resolver": "^1.0.4", - "@stoplight/spectral-runtime": "^1.0.0", + "@stoplight/spectral-runtime": "^1.1.2", "@stoplight/types": "~13.6.0", "@types/es-aggregate-error": "^1.0.2", "@types/json-schema": "^7.0.11", @@ -4093,9 +4107,9 @@ "lodash.topath": "^4.5.2", "minimatch": "3.1.2", "nimma": "0.2.3", - "pony-cause": "^1.0.0", + "pony-cause": "^1.1.1", "simple-eval": "1.0.1", - "tslib": "^2.3.0" + "tslib": "^2.8.1" }, "engines": { "node": "^16.20 || ^18.18 || >= 20.17" @@ -4140,31 +4154,31 @@ } }, "node_modules/@stoplight/spectral-formats": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-formats/-/spectral-formats-1.7.0.tgz", - "integrity": "sha512-vJ1vIkA2s96fdJp0d3AJBGuPAW3sj8yMamyzR+dquEFO6ZAoYBo/BVsKKQskYzZi/nwljlRqUmGVmcf2PncIaA==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-formats/-/spectral-formats-1.8.2.tgz", + "integrity": "sha512-c06HB+rOKfe7tuxg0IdKDEA5XnjL2vrn/m/OVIIxtINtBzphZrOgtRn7epQ5bQF5SWp84Ue7UJWaGgDwVngMFw==", "dev": true, "license": "Apache-2.0", "dependencies": { "@stoplight/json": "^3.17.0", - "@stoplight/spectral-core": "^1.8.0", + "@stoplight/spectral-core": "^1.19.2", "@types/json-schema": "^7.0.7", - "tslib": "^2.3.1" + "tslib": "^2.8.1" }, "engines": { - "node": ">=12" + "node": "^16.20 || ^18.18 || >= 20.17" } }, "node_modules/@stoplight/spectral-formatters": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-formatters/-/spectral-formatters-1.4.0.tgz", - "integrity": "sha512-nxYQldDzVg32pxQ4cMX27eMtB1A39ea+GSf0wIJ20mqkSBfIgLnRZ+GKkBxhgF9JzSolc4YtweydsubGQGd7ag==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-formatters/-/spectral-formatters-1.4.2.tgz", + "integrity": "sha512-3EwNpymMwxCeXr0ZTV6OPFU1ZPfg6SnKVtpbyhCCc9WY3VOgSFPpA5EwcUmZTu8hqLW9i7FimlE0xfdo+qYcJg==", "dev": true, "license": "Apache-2.0", "dependencies": { "@stoplight/path": "^1.3.2", - "@stoplight/spectral-core": "^1.15.1", - "@stoplight/spectral-runtime": "^1.1.0", + "@stoplight/spectral-core": "^1.19.2", + "@stoplight/spectral-runtime": "^1.1.2", "@stoplight/types": "^13.15.0", "@types/markdown-escape": "^1.1.3", "chalk": "4.1.2", @@ -4174,49 +4188,49 @@ "node-sarif-builder": "^2.0.3", "strip-ansi": "6.0", "text-table": "^0.2.0", - "tslib": "^2.5.0" + "tslib": "^2.8.1" }, "engines": { - "node": "^12.20 || >=14.13" + "node": "^16.20 || ^18.18 || >= 20.17" } }, "node_modules/@stoplight/spectral-functions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-functions/-/spectral-functions-1.9.0.tgz", - "integrity": "sha512-T+xl93ji8bpus4wUsTq8Qr2DSu2X9PO727rbxW61tTCG0s17CbsXOLYI+Ezjg5P6aaQlgXszGX8khtc57xk8Yw==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-functions/-/spectral-functions-1.9.2.tgz", + "integrity": "sha512-E+eh4IcYF73y8IKs4H8krvmeXiwBzTuoMfoTsoqGkdE5+1Vlfhm/1zm+jaZzSEwyWsl//8j3+gD+9RNpNlpa0Q==", "dev": true, "license": "Apache-2.0", "dependencies": { "@stoplight/better-ajv-errors": "1.0.3", "@stoplight/json": "^3.17.1", - "@stoplight/spectral-core": "^1.7.0", - "@stoplight/spectral-formats": "^1.7.0", - "@stoplight/spectral-runtime": "^1.1.0", + "@stoplight/spectral-core": "^1.19.2", + "@stoplight/spectral-formats": "^1.8.1", + "@stoplight/spectral-runtime": "^1.1.2", "ajv": "^8.17.1", "ajv-draft-04": "~1.0.0", "ajv-errors": "~3.0.0", "ajv-formats": "~2.1.0", "lodash": "~4.17.21", - "tslib": "^2.3.0" + "tslib": "^2.8.1" }, "engines": { - "node": ">=12" + "node": "^16.20 || ^18.18 || >= 20.17" } }, "node_modules/@stoplight/spectral-parsers": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-parsers/-/spectral-parsers-1.0.4.tgz", - "integrity": "sha512-nCTVvtX6q71M8o5Uvv9kxU31Gk1TRmgD6/k8HBhdCmKG6FWcwgjiZouA/R3xHLn/VwTI/9k8SdG5Mkdy0RBqbQ==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-parsers/-/spectral-parsers-1.0.5.tgz", + "integrity": "sha512-ANDTp2IHWGvsQDAY85/jQi9ZrF4mRrA5bciNHX+PUxPr4DwS6iv4h+FVWJMVwcEYdpyoIdyL+SRmHdJfQEPmwQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "@stoplight/json": "~3.21.0", "@stoplight/types": "^14.1.1", "@stoplight/yaml": "~4.3.0", - "tslib": "^2.3.1" + "tslib": "^2.8.1" }, "engines": { - "node": "^12.20 || >=14.13" + "node": "^16.20 || ^18.18 || >= 20.17" } }, "node_modules/@stoplight/spectral-parsers/node_modules/@stoplight/types": { @@ -4234,9 +4248,9 @@ } }, "node_modules/@stoplight/spectral-ref-resolver": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-ref-resolver/-/spectral-ref-resolver-1.0.4.tgz", - "integrity": "sha512-5baQIYL0NJTSVy8v6RxOR4U51xOUYM8wJri1YvlAT6bPN8m0EIxMwfVYi0xUZEMVeHcWx869nIkoqyWmOutF2A==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-ref-resolver/-/spectral-ref-resolver-1.0.5.tgz", + "integrity": "sha512-gj3TieX5a9zMW29z3mBlAtDOCgN3GEc1VgZnCVlr5irmR4Qi5LuECuFItAq4pTn5Zu+sW5bqutsCH7D4PkpyAA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4244,52 +4258,52 @@ "@stoplight/json-ref-resolver": "~3.1.6", "@stoplight/spectral-runtime": "^1.1.2", "dependency-graph": "0.11.0", - "tslib": "^2.3.1" + "tslib": "^2.8.1" }, "engines": { - "node": ">=12" + "node": "^16.20 || ^18.18 || >= 20.17" } }, "node_modules/@stoplight/spectral-ruleset-bundler": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-bundler/-/spectral-ruleset-bundler-1.6.0.tgz", - "integrity": "sha512-8CU7e4aEGdfU9ncVDtlnJSawg/6epzAHrQTjuNu1QfKAOoiwyG7oUk2XUTHWcvq6Q67iUctb0vjOokR+MPVg0Q==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-bundler/-/spectral-ruleset-bundler-1.6.1.tgz", + "integrity": "sha512-Pk0OVqyHXc/grFtaOWXF268UNRjwAnSGf9idBXO1XZJbieUyrYRJ44v5/E1UVxRMvzVkQ6/As/Ggi8hsEybKZw==", "dev": true, "license": "Apache-2.0", "dependencies": { "@rollup/plugin-commonjs": "~22.0.2", "@stoplight/path": "1.3.2", "@stoplight/spectral-core": ">=1", - "@stoplight/spectral-formats": "^1.7.0", + "@stoplight/spectral-formats": "^1.8.1", "@stoplight/spectral-functions": ">=1", "@stoplight/spectral-parsers": ">=1", "@stoplight/spectral-ref-resolver": "^1.0.4", "@stoplight/spectral-ruleset-migrator": "^1.9.6", "@stoplight/spectral-rulesets": ">=1", - "@stoplight/spectral-runtime": "^1.1.0", + "@stoplight/spectral-runtime": "^1.1.2", "@stoplight/types": "^13.6.0", "@types/node": "*", "pony-cause": "1.1.1", - "rollup": "~2.79.0", - "tslib": "^2.3.1", + "rollup": "~2.79.2", + "tslib": "^2.8.1", "validate-npm-package-name": "3.0.0" }, "engines": { - "node": "^12.20 || >= 14.13" + "node": "^16.20 || ^18.18 || >= 20.17" } }, "node_modules/@stoplight/spectral-ruleset-migrator": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-migrator/-/spectral-ruleset-migrator-1.11.0.tgz", - "integrity": "sha512-FHxc/C/RhEYXW8zcp9mO50/jt+0Of6p6ZFVoV84l9y7agQchc9RGFjN6src4kO7bg6eUWQK6+5rUIV6yFKhBgg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-migrator/-/spectral-ruleset-migrator-1.11.1.tgz", + "integrity": "sha512-z2A1Ual3bU7zLDxYqdHaxYgyirb7TVDaWXc9ONEBAo5W1isio0EHV59ujAUEOUHCLcY5ubd0eYeqgSjqPIQe8w==", "dev": true, "license": "Apache-2.0", "dependencies": { "@stoplight/json": "~3.21.0", "@stoplight/ordered-object-literal": "~1.0.4", "@stoplight/path": "1.3.2", - "@stoplight/spectral-functions": "^1.0.0", - "@stoplight/spectral-runtime": "^1.1.0", + "@stoplight/spectral-functions": "^1.9.1", + "@stoplight/spectral-runtime": "^1.1.2", "@stoplight/types": "^13.6.0", "@stoplight/yaml": "~4.2.3", "@types/node": "*", @@ -4297,7 +4311,7 @@ "ast-types": "0.14.2", "astring": "^1.9.0", "reserved": "0.1.2", - "tslib": "^2.3.1", + "tslib": "^2.8.1", "validate-npm-package-name": "3.0.0" }, "engines": { @@ -4328,19 +4342,19 @@ "license": "Apache-2.0" }, "node_modules/@stoplight/spectral-rulesets": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-rulesets/-/spectral-rulesets-1.20.2.tgz", - "integrity": "sha512-7Y8orZuNyGyeHr9n50rMfysgUJ+/zzIEHMptt66jiy82GUWl+0nr865DkMuXdC5GryfDYhtjoRTUCVsXu80Nkg==", + "version": "1.21.2", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-rulesets/-/spectral-rulesets-1.21.2.tgz", + "integrity": "sha512-8i2e6LANHn7cw7i2KUq/7bWjZfb+XNo9MEw1jSKVniime0UIgJKtN2kbG8F57WeXFip+XGScKTigyXOoUD9YTw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@asyncapi/specs": "^4.1.0", + "@asyncapi/specs": "^6.8.0", "@stoplight/better-ajv-errors": "1.0.3", "@stoplight/json": "^3.17.0", - "@stoplight/spectral-core": "^1.8.1", - "@stoplight/spectral-formats": "^1.7.0", - "@stoplight/spectral-functions": "^1.5.1", - "@stoplight/spectral-runtime": "^1.1.1", + "@stoplight/spectral-core": "^1.19.2", + "@stoplight/spectral-formats": "^1.8.1", + "@stoplight/spectral-functions": "^1.9.1", + "@stoplight/spectral-runtime": "^1.1.2", "@stoplight/types": "^13.6.0", "@types/json-schema": "^7.0.7", "ajv": "^8.17.1", @@ -4348,43 +4362,29 @@ "json-schema-traverse": "^1.0.0", "leven": "3.1.0", "lodash": "~4.17.21", - "tslib": "^2.3.0" + "tslib": "^2.8.1" }, "engines": { - "node": ">=12" + "node": "^16.20 || ^18.18 || >= 20.17" } }, "node_modules/@stoplight/spectral-runtime": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-runtime/-/spectral-runtime-1.1.2.tgz", - "integrity": "sha512-fr5zRceXI+hrl82yAVoME+4GvJie8v3wmOe9tU+ZLRRNonizthy8qDi0Z/z4olE+vGreSDcuDOZ7JjRxFW5kTw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-runtime/-/spectral-runtime-1.1.3.tgz", + "integrity": "sha512-uoKSVX/OYXOEBRQN7EtAaVefl8MlyhBkDcU2aDYEGALwYXHAH+vmF3ljhZrueMA3fSWLHTL3RxWqsjeeCor6lw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@stoplight/json": "^3.17.0", + "@stoplight/json": "^3.20.1", "@stoplight/path": "^1.3.2", - "@stoplight/types": "^12.3.0", + "@stoplight/types": "^13.6.0", "abort-controller": "^3.0.0", "lodash": "^4.17.21", "node-fetch": "^2.6.7", - "tslib": "^2.3.1" + "tslib": "^2.8.1" }, "engines": { - "node": ">=12" - } - }, - "node_modules/@stoplight/spectral-runtime/node_modules/@stoplight/types": { - "version": "12.5.0", - "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-12.5.0.tgz", - "integrity": "sha512-dwqYcDrGmEyUv5TWrDam5TGOxU72ufyQ7hnOIIDdmW5ezOwZaBFoR5XQ9AsH49w7wgvOqB2Bmo799pJPWnpCbg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.4", - "utility-types": "^3.10.0" - }, - "engines": { - "node": ">=8" + "node": "^16.20 || ^18.18 || >= 20.17" } }, "node_modules/@stoplight/types": { @@ -4834,9 +4834,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.8.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.7.tgz", - "integrity": "sha512-LidcG+2UeYIWcMuMUpBKOnryBWG/rnmOHQR5apjn8myTQcx3rinFRn7DcIFhMnS0PPFSC6OafdIKEad0lj6U0Q==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "license": "MIT", "dependencies": { "undici-types": "~6.19.8" @@ -5446,148 +5446,148 @@ } }, "node_modules/@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "license": "MIT", "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "license": "MIT", "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", - "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "license": "MIT", "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "license": "Apache-2.0", "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", - "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", - "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", - "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", - "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", - "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, @@ -6134,14 +6134,14 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", - "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz", + "integrity": "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==", "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.2", + "@babel/helper-define-polyfill-provider": "^0.6.3", "semver": "^6.3.1" }, "peerDependencies": { @@ -6173,13 +6173,13 @@ } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz", + "integrity": "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2" + "@babel/helper-define-polyfill-provider": "^0.6.3" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -6401,9 +6401,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001677", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz", - "integrity": "sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==", + "version": "1.0.30001680", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", + "integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==", "funding": [ { "type": "opencollective", @@ -6572,9 +6572,9 @@ } }, "node_modules/ci-info": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", - "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz", + "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==", "dev": true, "funding": [ { @@ -6825,9 +6825,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", + "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -6913,13 +6913,13 @@ } }, "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.0.1.tgz", + "integrity": "sha512-8Fxxv+tGhORlshCdCwnNJytvlvq46sOLSYEx2ZIGurahWvMucSRnyjPA3AmrMq4VPRYbHVpWj5VkiVasrM2H4Q==", "dev": true, "license": "MIT", "dependencies": { - "mdn-data": "2.0.30", + "mdn-data": "2.12.1", "source-map-js": "^1.0.1" }, "engines": { @@ -7888,9 +7888,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.50", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz", - "integrity": "sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==", + "version": "1.5.62", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.62.tgz", + "integrity": "sha512-t8c+zLmJHa9dJy96yBZRXGQYoiCEnHYgFwn1asvSPZSUdVxnB62A4RASd7k41ytG3ErFBA0TpHlKg9D9SQBmLg==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -7964,9 +7964,9 @@ } }, "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "version": "1.23.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.5.tgz", + "integrity": "sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7985,7 +7985,7 @@ "function.prototype.name": "^1.1.6", "get-intrinsic": "^1.2.4", "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", + "globalthis": "^1.0.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.3", @@ -8001,10 +8001,10 @@ "is-string": "^1.0.7", "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", + "object-inspect": "^1.13.3", "object-keys": "^1.1.1", "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", + "regexp.prototype.flags": "^1.5.3", "safe-array-concat": "^1.1.2", "safe-regex-test": "^1.0.3", "string.prototype.trim": "^1.2.9", @@ -8092,9 +8092,9 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.1.0.tgz", - "integrity": "sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz", + "integrity": "sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q==", "dev": true, "license": "MIT", "dependencies": { @@ -8106,6 +8106,7 @@ "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "globalthis": "^1.0.4", + "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.3", "has-symbols": "^1.0.3", @@ -8773,6 +8774,16 @@ "eslint": "^8.0.0 || ^9.0.0" } }, + "node_modules/eslint-plugin-sonarjs/node_modules/@eslint-community/regexpp": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/eslint-plugin-sonarjs/node_modules/@typescript-eslint/eslint-plugin": { "version": "7.16.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.1.tgz", @@ -9500,30 +9511,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/@humanwhocodes/retry": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.0.tgz", - "integrity": "sha512-xnRgu9DxZbkWak/te3fcytNyp8MTbuiZIaueg2rgEvBuN55n04nwLYLU9TX/VVlusc9L2ZNXi99nUFNkHXtr5g==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, "node_modules/eslint/node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -12131,9 +12118,9 @@ } }, "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.1.tgz", + "integrity": "sha512-rsfnCbOHjqrhWxwt5/wtSLzpoKTzW7OXdT5lLOIH1OTYhWu9rRJveGq0sKvDZODABH7RX+uoR+DYcpFnq4Tf6Q==", "dev": true, "license": "CC0-1.0" }, @@ -12325,14 +12312,14 @@ } }, "node_modules/mlly": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.2.tgz", - "integrity": "sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.3.tgz", + "integrity": "sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==", "license": "MIT", "dependencies": { - "acorn": "^8.12.1", + "acorn": "^8.14.0", "pathe": "^1.1.2", - "pkg-types": "^1.2.0", + "pkg-types": "^1.2.1", "ufo": "^1.5.4" } }, @@ -12562,9 +12549,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", "dev": true, "license": "MIT", "engines": { @@ -12763,9 +12750,9 @@ "license": "BlueOak-1.0.0" }, "node_modules/package-manager-detector": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.2.tgz", - "integrity": "sha512-VgXbyrSNsml4eHWIvxxG/nTL4wgybMTXCV2Un/+yEc3aDKKU6nQBZjbeP3Pl3qm9Qg92X/1ng4ffvCeD/zwHgg==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.4.tgz", + "integrity": "sha512-H/OUu9/zUfP89z1APcBf2X8Us0tt8dUK4lUmKqz12QNXif3DxAs1/YqjGtcutZi1zQqeNQRWr9C+EbQnnvSSFA==", "license": "MIT" }, "node_modules/parent-module": { @@ -13271,13 +13258,13 @@ } }, "node_modules/postcss-modules-local-by-default": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", - "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.1.0.tgz", + "integrity": "sha512-rm0bdSv4jC3BDma3s9H19ZddW0aHX6EoqwDYU2IfZhRN+53QrufTRo2IdkAbRqLx4R2IYbZnbjKKxg4VN5oU9Q==", "license": "MIT", "dependencies": { "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", + "postcss-selector-parser": "^7.0.0", "postcss-value-parser": "^4.1.0" }, "engines": { @@ -13287,13 +13274,26 @@ "postcss": "^8.1.0" } }, + "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/postcss-modules-scope": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", - "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", "license": "ISC", "dependencies": { - "postcss-selector-parser": "^6.0.4" + "postcss-selector-parser": "^7.0.0" }, "engines": { "node": "^10 || ^12 || >= 14" @@ -13302,6 +13302,19 @@ "postcss": "^8.1.0" } }, + "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/postcss-modules-values": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", @@ -14725,9 +14738,9 @@ } }, "node_modules/std-env": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", - "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", + "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", "dev": true, "license": "MIT" }, @@ -15071,20 +15084,6 @@ "dev": true, "license": "MIT" }, - "node_modules/stylelint/node_modules/css-tree": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.0.1.tgz", - "integrity": "sha512-8Fxxv+tGhORlshCdCwnNJytvlvq46sOLSYEx2ZIGurahWvMucSRnyjPA3AmrMq4VPRYbHVpWj5VkiVasrM2H4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "mdn-data": "2.12.1", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, "node_modules/stylelint/node_modules/file-entry-cache": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.1.0.tgz", @@ -15122,13 +15121,6 @@ "node": ">= 4" } }, - "node_modules/stylelint/node_modules/mdn-data": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.1.tgz", - "integrity": "sha512-rsfnCbOHjqrhWxwt5/wtSLzpoKTzW7OXdT5lLOIH1OTYhWu9rRJveGq0sKvDZODABH7RX+uoR+DYcpFnq4Tf6Q==", - "dev": true, - "license": "CC0-1.0" - }, "node_modules/stylelint/node_modules/postcss-safe-parser": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", @@ -15352,6 +15344,27 @@ "node": ">= 10" } }, + "node_modules/svgo/node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/svgo/node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/swagger-ui-dist": { "version": "5.17.14", "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.14.tgz", @@ -15650,9 +15663,9 @@ "license": "MIT" }, "node_modules/tinypool": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz", - "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", + "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", "dev": true, "license": "MIT", "engines": { @@ -16131,9 +16144,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "5.4.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", - "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", "dev": true, "license": "MIT", "dependencies": { @@ -16242,9 +16255,9 @@ } }, "node_modules/vite/node_modules/rollup": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.3.tgz", - "integrity": "sha512-HBW896xR5HGmoksbi3JBDtmVzWiPAYqp7wip50hjQ67JbDz61nyoMPdqu1DvVW9asYb2M65Z20ZHsyJCMqMyDg==", + "version": "4.27.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.2.tgz", + "integrity": "sha512-KreA+PzWmk2yaFmZVwe6GB2uBD86nXl86OsDkt1bJS9p3vqWuEQ6HnJJ+j/mZi/q0920P99/MVRlB4L3crpF5w==", "dev": true, "license": "MIT", "dependencies": { @@ -16258,24 +16271,24 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.3", - "@rollup/rollup-android-arm64": "4.24.3", - "@rollup/rollup-darwin-arm64": "4.24.3", - "@rollup/rollup-darwin-x64": "4.24.3", - "@rollup/rollup-freebsd-arm64": "4.24.3", - "@rollup/rollup-freebsd-x64": "4.24.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.3", - "@rollup/rollup-linux-arm-musleabihf": "4.24.3", - "@rollup/rollup-linux-arm64-gnu": "4.24.3", - "@rollup/rollup-linux-arm64-musl": "4.24.3", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.3", - "@rollup/rollup-linux-riscv64-gnu": "4.24.3", - "@rollup/rollup-linux-s390x-gnu": "4.24.3", - "@rollup/rollup-linux-x64-gnu": "4.24.3", - "@rollup/rollup-linux-x64-musl": "4.24.3", - "@rollup/rollup-win32-arm64-msvc": "4.24.3", - "@rollup/rollup-win32-ia32-msvc": "4.24.3", - "@rollup/rollup-win32-x64-msvc": "4.24.3", + "@rollup/rollup-android-arm-eabi": "4.27.2", + "@rollup/rollup-android-arm64": "4.27.2", + "@rollup/rollup-darwin-arm64": "4.27.2", + "@rollup/rollup-darwin-x64": "4.27.2", + "@rollup/rollup-freebsd-arm64": "4.27.2", + "@rollup/rollup-freebsd-x64": "4.27.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.27.2", + "@rollup/rollup-linux-arm-musleabihf": "4.27.2", + "@rollup/rollup-linux-arm64-gnu": "4.27.2", + "@rollup/rollup-linux-arm64-musl": "4.27.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.27.2", + "@rollup/rollup-linux-riscv64-gnu": "4.27.2", + "@rollup/rollup-linux-s390x-gnu": "4.27.2", + "@rollup/rollup-linux-x64-gnu": "4.27.2", + "@rollup/rollup-linux-x64-musl": "4.27.2", + "@rollup/rollup-win32-arm64-msvc": "4.27.2", + "@rollup/rollup-win32-ia32-msvc": "4.27.2", + "@rollup/rollup-win32-x64-msvc": "4.27.2", "fsevents": "~2.3.2" } }, diff --git a/poetry.lock b/poetry.lock index e431693b14..0bf8d224a8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -126,15 +126,18 @@ six = ">=1.13.0" [[package]] name = "json5" -version = "0.9.25" +version = "0.9.28" description = "A Python implementation of the JSON5 data format." optional = false -python-versions = ">=3.8" +python-versions = ">=3.8.0" files = [ - {file = "json5-0.9.25-py3-none-any.whl", hash = "sha256:34ed7d834b1341a86987ed52f3f76cd8ee184394906b6e22a1e0deb9ab294e8f"}, - {file = "json5-0.9.25.tar.gz", hash = "sha256:548e41b9be043f9426776f05df8635a00fe06104ea51ed24b67f908856e151ae"}, + {file = "json5-0.9.28-py3-none-any.whl", hash = "sha256:29c56f1accdd8bc2e037321237662034a7e07921e2b7223281a5ce2c46f0c4df"}, + {file = "json5-0.9.28.tar.gz", hash = "sha256:1f82f36e615bc5b42f1bbd49dbc94b12563c56408c6ffa06414ea310890e9a6e"}, ] +[package.extras] +dev = ["build (==1.2.2.post1)", "coverage (==7.5.3)", "mypy (==1.13.0)", "pip (==24.3.1)", "pylint (==3.2.3)", "ruff (==0.7.3)", "twine (==5.1.1)", "uv (==0.5.1)"] + [[package]] name = "pathspec" version = "0.12.1" @@ -210,105 +213,105 @@ files = [ [[package]] name = "regex" -version = "2024.9.11" +version = "2024.11.6" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" files = [ - {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408"}, - {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d"}, - {file = "regex-2024.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a"}, - {file = "regex-2024.9.11-cp310-cp310-win32.whl", hash = "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0"}, - {file = "regex-2024.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1"}, - {file = "regex-2024.9.11-cp311-cp311-win32.whl", hash = "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9"}, - {file = "regex-2024.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a"}, - {file = "regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776"}, - {file = "regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8"}, - {file = "regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8"}, - {file = "regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f"}, - {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:35f4a6f96aa6cb3f2f7247027b07b15a374f0d5b912c0001418d1d55024d5cb4"}, - {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:55b96e7ce3a69a8449a66984c268062fbaa0d8ae437b285428e12797baefce7e"}, - {file = "regex-2024.9.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb130fccd1a37ed894824b8c046321540263013da72745d755f2d35114b81a60"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:323c1f04be6b2968944d730e5c2091c8c89767903ecaa135203eec4565ed2b2b"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be1c8ed48c4c4065ecb19d882a0ce1afe0745dfad8ce48c49586b90a55f02366"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5b029322e6e7b94fff16cd120ab35a253236a5f99a79fb04fda7ae71ca20ae8"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6fff13ef6b5f29221d6904aa816c34701462956aa72a77f1f151a8ec4f56aeb"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:587d4af3979376652010e400accc30404e6c16b7df574048ab1f581af82065e4"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:079400a8269544b955ffa9e31f186f01d96829110a3bf79dc338e9910f794fca"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f9268774428ec173654985ce55fc6caf4c6d11ade0f6f914d48ef4719eb05ebb"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:23f9985c8784e544d53fc2930fc1ac1a7319f5d5332d228437acc9f418f2f168"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2941333154baff9838e88aa71c1d84f4438189ecc6021a12c7573728b5838e"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e93f1c331ca8e86fe877a48ad64e77882c0c4da0097f2212873a69bbfea95d0c"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:846bc79ee753acf93aef4184c040d709940c9d001029ceb7b7a52747b80ed2dd"}, - {file = "regex-2024.9.11-cp38-cp38-win32.whl", hash = "sha256:c94bb0a9f1db10a1d16c00880bdebd5f9faf267273b8f5bd1878126e0fbde771"}, - {file = "regex-2024.9.11-cp38-cp38-win_amd64.whl", hash = "sha256:2b08fce89fbd45664d3df6ad93e554b6c16933ffa9d55cb7e01182baaf971508"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35"}, - {file = "regex-2024.9.11-cp39-cp39-win32.whl", hash = "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142"}, - {file = "regex-2024.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919"}, - {file = "regex-2024.9.11.tar.gz", hash = "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"}, + {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"}, + {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"}, + {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"}, + {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"}, + {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"}, + {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"}, + {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"}, + {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"}, + {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"}, + {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"}, + {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"}, + {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"}, + {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"}, ] [[package]] @@ -324,24 +327,24 @@ files = [ [[package]] name = "tomli" -version = "2.0.2" +version = "2.1.0" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, - {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, + {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"}, + {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"}, ] [[package]] name = "tqdm" -version = "4.66.6" +version = "4.67.0" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.6-py3-none-any.whl", hash = "sha256:223e8b5359c2efc4b30555531f09e9f2f3589bcd7fdd389271191031b49b7a63"}, - {file = "tqdm-4.66.6.tar.gz", hash = "sha256:4bdd694238bef1485ce839d67967ab50af8f9272aab687c0d7702a01da0be090"}, + {file = "tqdm-4.67.0-py3-none-any.whl", hash = "sha256:0cd8af9d56911acab92182e88d763100d4788bdf421d251616040cc4d44863be"}, + {file = "tqdm-4.67.0.tar.gz", hash = "sha256:fe5a6f95e6fe0b9755e9469b77b9c3cf850048224ecaa8293d7d2d31f97d869a"}, ] [package.dependencies] @@ -349,6 +352,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +discord = ["requests"] notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] diff --git a/web_src/fomantic/package-lock.json b/web_src/fomantic/package-lock.json index 91e09c75fd..f1959bcda5 100644 --- a/web_src/fomantic/package-lock.json +++ b/web_src/fomantic/package-lock.json @@ -492,9 +492,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.8.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.7.tgz", - "integrity": "sha512-LidcG+2UeYIWcMuMUpBKOnryBWG/rnmOHQR5apjn8myTQcx3rinFRn7DcIFhMnS0PPFSC6OafdIKEad0lj6U0Q==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "license": "MIT", "dependencies": { "undici-types": "~6.19.8" @@ -1219,9 +1219,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001677", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz", - "integrity": "sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==", + "version": "1.0.30001680", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", + "integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==", "funding": [ { "type": "opencollective", @@ -1961,9 +1961,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.50", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz", - "integrity": "sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==", + "version": "1.5.62", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.62.tgz", + "integrity": "sha512-t8c+zLmJHa9dJy96yBZRXGQYoiCEnHYgFwn1asvSPZSUdVxnB62A4RASd7k41ytG3ErFBA0TpHlKg9D9SQBmLg==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -2497,9 +2497,9 @@ } }, "node_modules/foreground-child/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", + "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", "license": "MIT", "dependencies": { "path-key": "^3.1.0", From 6553148de9823f6351bea7583a3e6e0987633330 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 18 Nov 2024 05:52:58 +0000 Subject: [PATCH 46/49] Update renovate to v39.19.1 (forgejo) (#6008) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .forgejo/workflows/renovate.yml | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index bb2fc5ff72..edcfdd30af 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -25,7 +25,7 @@ jobs: runs-on: docker-runner-one container: - image: code.forgejo.org/forgejo-contrib/renovate:39.9.1 + image: code.forgejo.org/forgejo-contrib/renovate:39.19.1 steps: - name: Load renovate repo cache diff --git a/Makefile b/Makefile index 670cb9452a..e128fdc459 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasour DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.26.0 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.4.0 # renovate: datasource=go GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.16.2 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@39.9.1 # renovate: datasource=docker packageName=code.forgejo.org/forgejo-contrib/renovate +RENOVATE_NPM_PACKAGE ?= renovate@39.19.1 # renovate: datasource=docker packageName=code.forgejo.org/forgejo-contrib/renovate ifeq ($(HAS_GO), yes) CGO_EXTRA_CFLAGS := -DSQLITE_MAX_VARIABLE_NUMBER=32766 From b9697f52271e8b8a9a866ed63ed51f9d98670936 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Mon, 18 Nov 2024 08:20:10 +0100 Subject: [PATCH 47/49] fix(test): TestGitAttributeCheckerError must allow broken pipe Early cancelation can lead to two kinds of error. Either canceled or broken pipe, depending on when the goroutine stops. Fixes: forgejo/forgejo#6012 --- modules/git/repo_attribute_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/git/repo_attribute_test.go b/modules/git/repo_attribute_test.go index fa34164816..8b832e7221 100644 --- a/modules/git/repo_attribute_test.go +++ b/modules/git/repo_attribute_test.go @@ -254,7 +254,7 @@ func TestGitAttributeCheckerError(t *testing.T) { require.NoError(t, err) _, err = ac.CheckPath("i-am-a-python.p") - require.ErrorIs(t, err, context.Canceled) + require.Error(t, err) }) t.Run("Cancelled/DuringRun", func(t *testing.T) { From 18cecf124ffb7860f976f4244c7fbe3be6ec2d3d Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Mon, 18 Nov 2024 17:23:33 +0100 Subject: [PATCH 48/49] chore(ci): make release-notes-assistant job copy/pastable Refs: forgejo/forgejo#5999 --- .../workflows/release-notes-assistant.yml | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/.forgejo/workflows/release-notes-assistant.yml b/.forgejo/workflows/release-notes-assistant.yml index b1e2578fcf..bd668d82b6 100644 --- a/.forgejo/workflows/release-notes-assistant.yml +++ b/.forgejo/workflows/release-notes-assistant.yml @@ -1,3 +1,5 @@ +name: issue-labels + on: pull_request_target: types: @@ -7,22 +9,31 @@ on: jobs: release-notes: - if: ( vars.ROLE == 'forgejo-coding' ) && contains(github.event.pull_request.labels.*.name, 'worth a release-note') + if: > + vars.ROLE == 'forgejo-coding' && + + secrets.RELEASE_NOTES_ASSISTANT_TOKEN != '' && + + github.event_name == 'pull_request' && + contains(github.event.pull_request.labels.*.name, 'worth a release-note') && + ( + github.event.action == 'label_updated' || + github.event.action == 'edited' || + github.event.action == 'synchronized' + ) + runs-on: docker container: image: 'code.forgejo.org/oci/node:20-bookworm' steps: - - uses: https://code.forgejo.org/actions/checkout@v4 - - - name: event + - name: Debug info run: | cat <<'EOF' - ${{ toJSON(github.event.pull_request.labels.*.name) }} - EOF - cat <<'EOF' - ${{ toJSON(github.event) }} + ${{ toJSON(github) }} EOF + - uses: https://code.forgejo.org/actions/checkout@v4 + - uses: https://code.forgejo.org/actions/setup-go@v5 with: go-version-file: "go.mod" From 73cb6c9204aefce1502a1d1016cc080b6b53c519 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Mon, 18 Nov 2024 18:11:07 +0100 Subject: [PATCH 49/49] chore(ci): make release-notes-assistant job copy/pastable (part two) The event is pull_request_target Refs: forgejo/forgejo#5999 --- .forgejo/workflows/release-notes-assistant.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/release-notes-assistant.yml b/.forgejo/workflows/release-notes-assistant.yml index bd668d82b6..71c8d5f8da 100644 --- a/.forgejo/workflows/release-notes-assistant.yml +++ b/.forgejo/workflows/release-notes-assistant.yml @@ -14,7 +14,7 @@ jobs: secrets.RELEASE_NOTES_ASSISTANT_TOKEN != '' && - github.event_name == 'pull_request' && + github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'worth a release-note') && ( github.event.action == 'label_updated' ||