diff --git a/cmd/admin_user_create.go b/cmd/admin_user_create.go index cbdb5f90e2..7e5675cf58 100644 --- a/cmd/admin_user_create.go +++ b/cmd/admin_user_create.go @@ -151,6 +151,7 @@ func runCreateUser(ctx context.Context, c *cli.Command) error { if err != nil { return err } + // codeql[disable-next-line=go/clear-text-logging] fmt.Printf("generated random password is '%s'\n", password) } else if userType == user_model.UserTypeIndividual { return errors.New("must set either password or random-password flag") diff --git a/cmd/admin_user_must_change_password.go b/cmd/admin_user_must_change_password.go index 8521853dc1..468d462b74 100644 --- a/cmd/admin_user_must_change_password.go +++ b/cmd/admin_user_must_change_password.go @@ -58,6 +58,7 @@ func runMustChangePassword(ctx context.Context, c *cli.Command) error { return err } + // codeql[disable-next-line=go/clear-text-logging] fmt.Printf("Updated %d users setting MustChangePassword to %t\n", n, mustChangePassword) return nil } diff --git a/cmd/generate.go b/cmd/generate.go index cf491604ef..9cb4cf3917 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -91,6 +91,7 @@ func runGenerateSecretKey(_ context.Context, c *cli.Command) error { return err } + // codeql[disable-next-line=go/clear-text-logging] fmt.Printf("%s", secretKey) if isatty.IsTerminal(os.Stdout.Fd()) { diff --git a/cmd/hook.go b/cmd/hook.go index 2f866dd396..ceadb09f63 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -186,7 +186,7 @@ Gitea or set your environment appropriately.`, "") userID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64) prID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPRID), 10, 64) deployKeyID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvDeployKeyID), 10, 64) - actionPerm, _ := strconv.ParseInt(os.Getenv(repo_module.EnvActionPerm), 10, 64) + actionPerm, _ := strconv.Atoi(os.Getenv(repo_module.EnvActionPerm)) hookOptions := private.HookOptions{ UserID: userID, @@ -196,7 +196,7 @@ Gitea or set your environment appropriately.`, "") GitPushOptions: pushOptions(), PullRequestID: prID, DeployKeyID: deployKeyID, - ActionPerm: int(actionPerm), + ActionPerm: actionPerm, } scanner := bufio.NewScanner(os.Stdin) diff --git a/models/repo/repo.go b/models/repo/repo.go index 8237a429e5..401775047b 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -605,7 +605,7 @@ func (repo *Repository) IsGenerated() bool { // RepoPath returns repository path by given user and repository name. func RepoPath(userName, repoName string) string { //revive:disable-line:exported - return filepath.Join(user_model.UserPath(userName), strings.ToLower(repoName)+".git") + return filepath.Join(setting.RepoRootPath, filepath.Clean(strings.ToLower(userName)), filepath.Clean(strings.ToLower(repoName)+".git")) } // RepoPath returns the repository path diff --git a/models/user/user.go b/models/user/user.go index 6143992a25..80d5eb5ec4 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -980,7 +980,7 @@ func GetInactiveUsers(ctx context.Context, olderThan time.Duration) ([]*User, er // UserPath returns the path absolute path of user repositories. func UserPath(userName string) string { //revive:disable-line:exported - return filepath.Join(setting.RepoRootPath, strings.ToLower(userName)) + return filepath.Join(setting.RepoRootPath, filepath.Clean(strings.ToLower(userName))) } // GetUserByID returns the user object by given ID if exists. diff --git a/modules/auth/password/hash/argon2.go b/modules/auth/password/hash/argon2.go index 0cd6472fa1..f4a7497df6 100644 --- a/modules/auth/password/hash/argon2.go +++ b/modules/auth/password/hash/argon2.go @@ -61,17 +61,11 @@ func NewArgon2Hasher(config string) *Argon2Hasher { return nil } - parsed, err := parseUIntParam(vals[0], "time", "argon2", config, nil) - hasher.time = uint32(parsed) - - parsed, err = parseUIntParam(vals[1], "memory", "argon2", config, err) - hasher.memory = uint32(parsed) - - parsed, err = parseUIntParam(vals[2], "threads", "argon2", config, err) - hasher.threads = uint8(parsed) - - parsed, err = parseUIntParam(vals[3], "keyLen", "argon2", config, err) - hasher.keyLen = uint32(parsed) + var err error + hasher.time, err = parseUintParam[uint32](vals[0], "time", "argon2", config, nil) + hasher.memory, err = parseUintParam[uint32](vals[1], "memory", "argon2", config, err) + hasher.threads, err = parseUintParam[uint8](vals[2], "threads", "argon2", config, err) + hasher.keyLen, err = parseUintParam[uint32](vals[3], "keyLen", "argon2", config, err) if err != nil { return nil } diff --git a/modules/auth/password/hash/common.go b/modules/auth/password/hash/common.go index d5e2c34314..1fafc289ed 100644 --- a/modules/auth/password/hash/common.go +++ b/modules/auth/password/hash/common.go @@ -7,6 +7,7 @@ import ( "strconv" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/util" ) func parseIntParam(value, param, algorithmName, config string, previousErr error) (int, error) { @@ -18,11 +19,12 @@ func parseIntParam(value, param, algorithmName, config string, previousErr error return parsed, previousErr // <- Keep the previous error as this function should still return an error once everything has been checked if any call failed } -func parseUIntParam(value, param, algorithmName, config string, previousErr error) (uint64, error) { //nolint:unparam // algorithmName is always argon2 - parsed, err := strconv.ParseUint(value, 10, 64) +func parseUintParam[T uint32 | uint8](value, param, algorithmName, config string, previousErr error) (ret T, _ error) { + _, isUint32 := any(ret).(uint32) + parsed, err := strconv.ParseUint(value, 10, util.Iif(isUint32, 32, 8)) if err != nil { log.Error("invalid integer for %s representation in %s hash spec %s", param, algorithmName, config) return 0, err } - return parsed, previousErr // <- Keep the previous error as this function should still return an error once everything has been checked if any call failed + return T(parsed), previousErr // <- Keep the previous error as this function should still return an error once everything has been checked if any call failed } diff --git a/modules/auth/password/pwn/pwn.go b/modules/auth/password/pwn/pwn.go index 99a6ca6cea..d5ea96c4af 100644 --- a/modules/auth/password/pwn/pwn.go +++ b/modules/auth/password/pwn/pwn.go @@ -72,7 +72,7 @@ func newRequest(ctx context.Context, method, url string, body io.ReadCloser) (*h // Adding padding will make requests more secure, however is also slower // because artificial responses will be added to the response // For more information, see https://www.troyhunt.com/enhancing-pwned-passwords-privacy-with-padding/ -func (c *Client) CheckPassword(pw string, padding bool) (int, error) { +func (c *Client) CheckPassword(pw string, padding bool) (int64, error) { if pw == "" { return -1, ErrEmptyPassword } @@ -111,7 +111,7 @@ func (c *Client) CheckPassword(pw string, padding bool) (int, error) { if err != nil { return -1, err } - return int(count), nil + return count, nil } } return 0, nil diff --git a/modules/auth/password/pwn/pwn_test.go b/modules/auth/password/pwn/pwn_test.go index ae03fabc57..4b760fdf32 100644 --- a/modules/auth/password/pwn/pwn_test.go +++ b/modules/auth/password/pwn/pwn_test.go @@ -37,25 +37,25 @@ func TestPassword(t *testing.T) { count, err := client.CheckPassword("", false) assert.ErrorIs(t, err, ErrEmptyPassword, "blank input should return ErrEmptyPassword") - assert.Equal(t, -1, count) + assert.EqualValues(t, -1, count) count, err = client.CheckPassword("pwned", false) assert.NoError(t, err) - assert.Equal(t, 1, count) + assert.EqualValues(t, 1, count) count, err = client.CheckPassword("notpwned", false) assert.NoError(t, err) - assert.Equal(t, 0, count) + assert.EqualValues(t, 0, count) count, err = client.CheckPassword("paddedpwned", true) assert.NoError(t, err) - assert.Equal(t, 1, count) + assert.EqualValues(t, 1, count) count, err = client.CheckPassword("paddednotpwned", true) assert.NoError(t, err) - assert.Equal(t, 0, count) + assert.EqualValues(t, 0, count) count, err = client.CheckPassword("paddednotpwnedzero", true) assert.NoError(t, err) - assert.Equal(t, 0, count) + assert.EqualValues(t, 0, count) } diff --git a/modules/git/hook.go b/modules/git/hook.go index 548a59971d..361aa53100 100644 --- a/modules/git/hook.go +++ b/modules/git/hook.go @@ -45,7 +45,7 @@ func GetHook(repoPath, name string) (*Hook, error) { } h := &Hook{ name: name, - path: filepath.Join(repoPath, "hooks", name+".d", name), + path: filepath.Join(repoPath, filepath.Join("hooks", name+".d", name)), } isFile, err := util.IsFile(h.path) if err != nil { diff --git a/modules/log/logger_global.go b/modules/log/logger_global.go index 07c25cd62f..2bc8c4f449 100644 --- a/modules/log/logger_global.go +++ b/modules/log/logger_global.go @@ -18,6 +18,7 @@ func GetLevel() Level { } func Log(skip int, level Level, format string, v ...any) { + // codeql[disable-next-line=go/clear-text-logging] GetLogger(DEFAULT).Log(skip+1, &Event{Level: level}, format, v...) } diff --git a/modules/log/misc.go b/modules/log/misc.go index c9d230e4ac..a58b3757da 100644 --- a/modules/log/misc.go +++ b/modules/log/misc.go @@ -20,6 +20,7 @@ func BaseLoggerToGeneralLogger(b BaseLogger) Logger { var _ Logger = (*baseToLogger)(nil) func (s *baseToLogger) Log(skip int, event *Event, format string, v ...any) { + // codeql[disable-next-line=go/clear-text-logging] s.base.Log(skip+1, event, format, v...) } diff --git a/modules/setting/config_env.go b/modules/setting/config_env.go index 409588dc44..8b204e5c7c 100644 --- a/modules/setting/config_env.go +++ b/modules/setting/config_env.go @@ -65,7 +65,7 @@ func decodeEnvSectionKey(encoded string) (ok bool, section, key string) { decodedBytes := make([]byte, len(toDecode)/2) for i := 0; i < len(toDecode)/2; i++ { // Can ignore error here as we know these should be hexadecimal from the regexp - byteInt, _ := strconv.ParseInt(toDecode[2*i:2*i+2], 16, 0) + byteInt, _ := strconv.ParseInt(toDecode[2*i:2*i+2], 16, 8) decodedBytes[i] = byte(byteInt) } if inKey { diff --git a/modules/tempdir/tempdir.go b/modules/tempdir/tempdir.go index 22c2e4ea16..e5e928e449 100644 --- a/modules/tempdir/tempdir.go +++ b/modules/tempdir/tempdir.go @@ -19,7 +19,7 @@ type TempDir struct { } func (td *TempDir) JoinPath(elems ...string) string { - return filepath.Join(append([]string{td.base, td.sub}, elems...)...) + return filepath.Join(append([]string{td.base, td.sub}, filepath.Join(elems...))...) } // MkdirAllSub works like os.MkdirAll, but the base directory must exist diff --git a/modules/translation/i18n/i18n_test.go b/modules/translation/i18n/i18n_test.go index b364992dfe..3cf3f71294 100644 --- a/modules/translation/i18n/i18n_test.go +++ b/modules/translation/i18n/i18n_test.go @@ -62,6 +62,9 @@ sub = Changed Sub String found := lang1.HasKey("no-such") assert.False(t, found) assert.NoError(t, ls.Close()) + + res := lang1.TrHTML("") + assert.Equal(t, "<no-such>", string(res)) } func TestLocaleStoreMoreSource(t *testing.T) { diff --git a/modules/translation/i18n/localestore.go b/modules/translation/i18n/localestore.go index 4f1ae7e13d..c540b5bf47 100644 --- a/modules/translation/i18n/localestore.go +++ b/modules/translation/i18n/localestore.go @@ -6,6 +6,7 @@ package i18n import ( "errors" "fmt" + "html" "html/template" "slices" @@ -109,8 +110,7 @@ func (store *localeStore) Close() error { } func (l *locale) TrString(trKey string, trArgs ...any) string { - format := trKey - + var format string idx, ok := l.store.trKeyToIdxMap[trKey] if ok { if msg, ok := l.idxToMsgMap[idx]; ok { @@ -122,7 +122,9 @@ func (l *locale) TrString(trKey string, trArgs ...any) string { } } } - + if format == "" { + format = html.EscapeString(trKey) + } msg, err := Format(format, trArgs...) if err != nil { log.Error("Error whilst formatting %q in %s: %v", trKey, l.langName, err) diff --git a/modules/util/color.go b/modules/util/color.go index 8fffc91ac4..4d2c251726 100644 --- a/modules/util/color.go +++ b/modules/util/color.go @@ -26,13 +26,14 @@ func HexToRBGColor(colorString string) (float64, float64, float64) { if len(hexString) == 8 { hexString = hexString[0:6] } - color, err := strconv.ParseUint(hexString, 16, 64) + color, err := strconv.ParseUint(hexString, 16, 32) + color32 := uint32(color) if err != nil { return 0, 0, 0 } - r := float64(uint8(0xFF & (uint32(color) >> 16))) - g := float64(uint8(0xFF & (uint32(color) >> 8))) - b := float64(uint8(0xFF & uint32(color))) + r := float64(uint8(0xFF & (color32 >> 16))) + g := float64(uint8(0xFF & (color32 >> 8))) + b := float64(uint8(0xFF & color32)) return r, g, b } diff --git a/routers/api/v1/repo/issue_pin.go b/routers/api/v1/repo/issue_pin.go index 71985ac765..45bdd875d2 100644 --- a/routers/api/v1/repo/issue_pin.go +++ b/routers/api/v1/repo/issue_pin.go @@ -169,7 +169,7 @@ func MoveIssuePin(ctx *context.APIContext) { return } - err = issues_model.MovePin(ctx, issue, int(ctx.PathParamInt64("position"))) + err = issues_model.MovePin(ctx, issue, ctx.PathParamInt("position")) if err != nil { ctx.APIErrorInternal(err) return diff --git a/routers/web/explore/repo.go b/routers/web/explore/repo.go index f0d7d0ce7d..1b269deb98 100644 --- a/routers/web/explore/repo.go +++ b/routers/web/explore/repo.go @@ -35,7 +35,7 @@ type RepoSearchOptions struct { // This function is also used to render the Admin Repository Management page. func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { // Sitemap index for sitemap paths - page := int(ctx.PathParamInt64("idx")) + page := ctx.PathParamInt("idx") isSitemap := ctx.PathParam("idx") != "" if page <= 1 { page = ctx.FormInt("page") diff --git a/routers/web/explore/user.go b/routers/web/explore/user.go index 3774be033d..40d3e2a060 100644 --- a/routers/web/explore/user.go +++ b/routers/web/explore/user.go @@ -34,7 +34,7 @@ func isKeywordValid(keyword string) bool { // RenderUserSearch render user search page func RenderUserSearch(ctx *context.Context, opts user_model.SearchUserOptions, tplName templates.TplName) { // Sitemap index for sitemap paths - opts.Page = int(ctx.PathParamInt64("idx")) + opts.Page = ctx.PathParamInt("idx") isSitemap := ctx.PathParam("idx") != "" if opts.Page <= 1 { opts.Page = ctx.FormInt("page") diff --git a/routers/web/repo/activity.go b/routers/web/repo/activity.go index fcede1822a..4cfe879032 100644 --- a/routers/web/repo/activity.go +++ b/routers/web/repo/activity.go @@ -25,33 +25,28 @@ func Activity(ctx *context.Context) { ctx.Data["PageIsPulse"] = true - ctx.Data["Period"] = ctx.PathParam("period") - timeUntil := time.Now() - var timeFrom time.Time - - switch ctx.Data["Period"] { + period, timeFrom := "weekly", timeUntil.Add(-time.Hour*168) + switch ctx.PathParam("period") { case "daily": - timeFrom = timeUntil.Add(-time.Hour * 24) + period, timeFrom = "daily", timeUntil.Add(-time.Hour*24) case "halfweekly": - timeFrom = timeUntil.Add(-time.Hour * 72) + period, timeFrom = "halfweekly", timeUntil.Add(-time.Hour*72) case "weekly": - timeFrom = timeUntil.Add(-time.Hour * 168) + period, timeFrom = "weekly", timeUntil.Add(-time.Hour*168) case "monthly": - timeFrom = timeUntil.AddDate(0, -1, 0) + period, timeFrom = "monthly", timeUntil.AddDate(0, -1, 0) case "quarterly": - timeFrom = timeUntil.AddDate(0, -3, 0) + period, timeFrom = "quarterly", timeUntil.AddDate(0, -3, 0) case "semiyearly": - timeFrom = timeUntil.AddDate(0, -6, 0) + period, timeFrom = "semiyearly", timeUntil.AddDate(0, -6, 0) case "yearly": - timeFrom = timeUntil.AddDate(-1, 0, 0) - default: - ctx.Data["Period"] = "weekly" - timeFrom = timeUntil.Add(-time.Hour * 168) + period, timeFrom = "yearly", timeUntil.AddDate(-1, 0, 0) } ctx.Data["DateFrom"] = timeFrom ctx.Data["DateUntil"] = timeUntil - ctx.Data["PeriodText"] = ctx.Tr("repo.activity.period." + ctx.Data["Period"].(string)) + ctx.Data["Period"] = period + ctx.Data["PeriodText"] = ctx.Tr("repo.activity.period." + period) canReadCode := ctx.Repo.CanRead(unit.TypeCode) if canReadCode { diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index ab649b6f15..829e6a5fc3 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -376,7 +376,7 @@ func (h *serviceHandler) sendFile(ctx *context.Context, contentType, file string ctx.Resp.WriteHeader(http.StatusBadRequest) return } - reqFile := filepath.Join(h.getRepoDir(), file) + reqFile := filepath.Join(h.getRepoDir(), filepath.Clean(file)) fi, err := os.Stat(reqFile) if os.IsNotExist(err) { @@ -395,13 +395,12 @@ func (h *serviceHandler) sendFile(ctx *context.Context, contentType, file string var safeGitProtocolHeader = regexp.MustCompile(`^[0-9a-zA-Z]+=[0-9a-zA-Z]+(:[0-9a-zA-Z]+=[0-9a-zA-Z]+)*$`) func prepareGitCmdWithAllowedService(service string) (*gitcmd.Command, error) { - if service == "receive-pack" { - return gitcmd.NewCommand("receive-pack"), nil + if service == ServiceTypeReceivePack { + return gitcmd.NewCommand(ServiceTypeReceivePack), nil } - if service == "upload-pack" { - return gitcmd.NewCommand("upload-pack"), nil + if service == ServiceTypeUploadPack { + return gitcmd.NewCommand(ServiceTypeUploadPack), nil } - return nil, fmt.Errorf("service %q is not allowed", service) } @@ -464,11 +463,16 @@ func serviceRPC(ctx *context.Context, h *serviceHandler, service string) { } } +const ( + ServiceTypeUploadPack = "upload-pack" + ServiceTypeReceivePack = "receive-pack" +) + // ServiceUploadPack implements Git Smart HTTP protocol func ServiceUploadPack(ctx *context.Context) { h := httpBase(ctx) if h != nil { - serviceRPC(ctx, h, "upload-pack") + serviceRPC(ctx, h, ServiceTypeUploadPack) } } @@ -476,16 +480,18 @@ func ServiceUploadPack(ctx *context.Context) { func ServiceReceivePack(ctx *context.Context) { h := httpBase(ctx) if h != nil { - serviceRPC(ctx, h, "receive-pack") + serviceRPC(ctx, h, ServiceTypeReceivePack) } } func getServiceType(ctx *context.Context) string { - serviceType := ctx.Req.FormValue("service") - if !strings.HasPrefix(serviceType, "git-") { - return "" + switch ctx.Req.FormValue("service") { + case "git-" + ServiceTypeUploadPack: + return ServiceTypeUploadPack + case "git-" + ServiceTypeReceivePack: + return ServiceTypeReceivePack } - return strings.TrimPrefix(serviceType, "git-") + return "" } func updateServerInfo(ctx gocontext.Context, dir string) []byte { diff --git a/routers/web/repo/view_home.go b/routers/web/repo/view_home.go index f475e93f60..88d9fd8fe2 100644 --- a/routers/web/repo/view_home.go +++ b/routers/web/repo/view_home.go @@ -279,7 +279,7 @@ func handleRepoViewSubmodule(ctx *context.Context, commitSubmoduleFile *git.Comm ctx.Data["NotFoundPrompt"] = redirectLink ctx.NotFound(nil) } else { - ctx.Redirect(submoduleWebLink.CommitWebLink) + ctx.RedirectToCurrentSite(redirectLink) } } diff --git a/routers/web/user/avatar.go b/routers/web/user/avatar.go index 6d3179bc48..2eca669194 100644 --- a/routers/web/user/avatar.go +++ b/routers/web/user/avatar.go @@ -31,7 +31,7 @@ func AvatarByUsernameSize(ctx *context.Context) { return } } - cacheableRedirect(ctx, user.AvatarLinkWithSize(ctx, int(ctx.PathParamInt64("size")))) + cacheableRedirect(ctx, user.AvatarLinkWithSize(ctx, ctx.PathParamInt("size"))) } // AvatarByEmailHash redirects the browser to the email avatar link diff --git a/services/context/access_log.go b/services/context/access_log.go index caade113a7..9419106a07 100644 --- a/services/context/access_log.go +++ b/services/context/access_log.go @@ -10,6 +10,7 @@ import ( "strings" "text/template" "time" + "unicode" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" @@ -37,6 +38,16 @@ const keyOfRequestIDInTemplate = ".RequestID" // So, we accept a Request ID with a maximum character length of 40 const maxRequestIDByteLength = 40 +func isSafeRequestID(id string) bool { + for _, r := range id { + safe := unicode.IsPrint(r) + if !safe { + return false + } + } + return true +} + func parseRequestIDFromRequestHeader(req *http.Request) string { requestID := "-" for _, key := range setting.Log.RequestIDHeaders { @@ -45,6 +56,9 @@ func parseRequestIDFromRequestHeader(req *http.Request) string { break } } + if !isSafeRequestID(requestID) { + return "-" + } if len(requestID) > maxRequestIDByteLength { requestID = requestID[:maxRequestIDByteLength] + "..." } diff --git a/services/context/access_log_test.go b/services/context/access_log_test.go index 139a6eb217..89ec90fd4f 100644 --- a/services/context/access_log_test.go +++ b/services/context/access_log_test.go @@ -69,3 +69,8 @@ func TestAccessLogger(t *testing.T) { recorder.record(time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC), &testAccessLoggerResponseWriterMock{}, req) assert.Equal(t, []string{`remote-addr - - [02/Jan/2000:03:04:05 +0000] "GET /path https" 200 123123 "referer" "user-agent"`}, mockLogger.logs) } + +func TestAccessLoggerRequestID(t *testing.T) { + assert.False(t, isSafeRequestID("\x00")) + assert.True(t, isSafeRequestID("a b-c")) +} diff --git a/services/context/base_path.go b/services/context/base_path.go index 3678deaff9..63e60c8654 100644 --- a/services/context/base_path.go +++ b/services/context/base_path.go @@ -37,6 +37,11 @@ func (b *Base) PathParamInt64(p string) int64 { return v } +func (b *Base) PathParamInt(p string) int { + v, _ := strconv.Atoi(b.PathParam(p)) + return v +} + // SetPathParam set request path params into routes func (b *Base) SetPathParam(name, value string) { if strings.HasPrefix(name, ":") { diff --git a/web_src/fomantic/build/components/api.js b/web_src/fomantic/build/components/api.js index a104cfbc1b..3bfe27e13c 100644 --- a/web_src/fomantic/build/components/api.js +++ b/web_src/fomantic/build/components/api.js @@ -1144,8 +1144,8 @@ $.api.settings = { }, regExp : { - required : /\{\$*[A-z0-9]+\}/g, - optional : /\{\/\$*[A-z0-9]+\}/g, + required : /\{\$*[_A-Za-z0-9]+\}/g, // GITEA-PATCH: use "_A-Za-z" instead of "A-z" for variable name matching + optional : /\{\/\$*[_A-Za-z0-9]+\}/g, // GITEA-PATCH: use "_A-Za-z" instead of "A-z" for variable name matching }, className: { diff --git a/web_src/fomantic/build/components/dropdown.js b/web_src/fomantic/build/components/dropdown.js index 3ad0984865..f40d77df13 100644 --- a/web_src/fomantic/build/components/dropdown.js +++ b/web_src/fomantic/build/components/dropdown.js @@ -66,7 +66,7 @@ $.fn.dropdown = function(parameters) { moduleNamespace = 'module-' + namespace, $module = $(this), - $context = $(settings.context), + $context = (typeof settings.context === 'string') ? $(document).find(settings.context) : $(settings.context), // GITEA-PATCH: use "jQuery.find(selector)" instead of "jQuery(selector)" $text = $module.find(selector.text), $search = $module.find(selector.search), $sizer = $module.find(selector.sizer), diff --git a/web_src/fomantic/build/components/modal.js b/web_src/fomantic/build/components/modal.js index 3f578ccfcc..0434af3deb 100644 --- a/web_src/fomantic/build/components/modal.js +++ b/web_src/fomantic/build/components/modal.js @@ -64,7 +64,7 @@ $.fn.modal = function(parameters) { moduleNamespace = 'module-' + namespace, $module = $(this), - $context = $(settings.context), + $context = (typeof settings.context === 'string') ? $(document).find(settings.context) : $(settings.context), // GITEA-PATCH: use "jQuery.find(selector)" instead of "jQuery(selector)" $close = $module.find(selector.close), $allModals, diff --git a/web_src/js/features/comp/EditorMarkdown.test.ts b/web_src/js/features/comp/EditorMarkdown.test.ts index 431994c7d3..ae3d0c9f0b 100644 --- a/web_src/js/features/comp/EditorMarkdown.test.ts +++ b/web_src/js/features/comp/EditorMarkdown.test.ts @@ -26,13 +26,13 @@ test('textareaSplitLines', () => { test('markdownHandleIndention', () => { const testInput = (input: string, expected?: string) => { const inputPos = input.indexOf('|'); - input = input.replace('|', ''); + input = input.replaceAll('|', ''); const ret = markdownHandleIndention({value: input, selStart: inputPos, selEnd: inputPos}); if (expected === null) { expect(ret).toEqual({handled: false}); } else { const expectedPos = expected.indexOf('|'); - expected = expected.replace('|', ''); + expected = expected.replaceAll('|', ''); expect(ret).toEqual({ handled: true, valueSelection: {value: expected, selStart: expectedPos, selEnd: expectedPos}, diff --git a/web_src/js/features/repo-issue.ts b/web_src/js/features/repo-issue.ts index ed9aad9adf..58274b95c0 100644 --- a/web_src/js/features/repo-issue.ts +++ b/web_src/js/features/repo-issue.ts @@ -333,7 +333,7 @@ export function initRepoPullRequestReview() { let ntr = tr.nextElementSibling; if (!ntr?.classList.contains('add-comment')) { ntr = createElementFromHTML(` - + ${isSplit ? ` diff --git a/web_src/js/modules/fomantic/base.ts b/web_src/js/modules/fomantic/base.ts index 59996b1156..a227d8123a 100644 --- a/web_src/js/modules/fomantic/base.ts +++ b/web_src/js/modules/fomantic/base.ts @@ -14,4 +14,7 @@ export function linkLabelAndInput(label: Element, input: Element) { } } -export const fomanticQuery = $; +export function fomanticQuery(s: string | Element | NodeListOf): ReturnType { + // intentionally make it only work for query selector, it isn't used for creating HTML elements (for safety) + return typeof s === 'string' ? $(document).find(s) : $(s); +} diff --git a/web_src/js/utils.ts b/web_src/js/utils.ts index 5e2f4d5106..c3db28c0e2 100644 --- a/web_src/js/utils.ts +++ b/web_src/js/utils.ts @@ -35,7 +35,12 @@ export function isDarkTheme(): boolean { /** strip from a string */ export function stripTags(text: string): string { - return text.replace(/<[^>]*>?/g, ''); + let prev = ''; + while (prev !== text) { + prev = text; + text = text.replace(/<[^>]*>?/g, ''); + } + return text; } export function parseIssueHref(href: string): IssuePathInfo {