aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwxiaoguang <wxiaoguang@gmail.com>2022-04-01 16:47:50 +0800
committerGitHub <noreply@github.com>2022-04-01 16:47:50 +0800
commit65f17bfc31f0f2659978d4d1d5ff825146c53a4d (patch)
treec039c5b332c7a4cb72ee7f92bed10c97df1fe5dd
parent5b7466053d993685939da8623fb78e94e4ee0797 (diff)
downloadgitea-65f17bfc31f0f2659978d4d1d5ff825146c53a4d.tar.gz
gitea-65f17bfc31f0f2659978d4d1d5ff825146c53a4d.zip
Refactor legacy `unknwon/com` package, improve golangci lint (#19284)
The main purpose is to refactor the legacy `unknwon/com` package. 1. Remove most imports of `unknwon/com`, only `util/legacy.go` imports the legacy `unknwon/com` 2. Use golangci's depguard to process denied packages 3. Fix some incorrect values in golangci.yml, eg, the version should be quoted string `"1.18"` 4. Use correctly escaped content for `go-import` and `go-source` meta tags 5. Refactor `com.Expand` to our stable (and the same fast) `vars.Expand`, our `vars.Expand` can still return partially rendered content even if the template is not good (eg: key mistach).
-rw-r--r--.golangci.yml14
-rw-r--r--integrations/goget_test.go3
-rw-r--r--models/migrations/migrations_test.go3
-rw-r--r--modules/cache/cache_redis.go8
-rw-r--r--modules/context/context.go6
-rw-r--r--modules/context/csrf.go14
-rw-r--r--modules/context/repo.go10
-rw-r--r--modules/json/json.go2
-rw-r--r--modules/markup/html.go11
-rw-r--r--modules/repository/init.go10
-rw-r--r--modules/setting/setting.go3
-rw-r--r--modules/sync/unique_queue.go10
-rw-r--r--modules/templates/vars/vars.go93
-rw-r--r--modules/templates/vars/vars_test.go72
-rw-r--r--modules/util/copy.go20
-rw-r--r--modules/util/io.go2
-rw-r--r--modules/util/legacy.go84
-rw-r--r--modules/util/legacy_test.go37
-rw-r--r--modules/util/path.go27
-rw-r--r--modules/web/middleware/binding.go6
-rw-r--r--routers/web/goget.go32
-rw-r--r--routers/web/repo/issue.go11
22 files changed, 397 insertions, 81 deletions
diff --git a/.golangci.yml b/.golangci.yml
index 1794b4594d..8e31d0cbc4 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -18,6 +18,7 @@ linters:
- ineffassign
- revive
- gofumpt
+ - depguard
enable-all: false
disable-all: true
fast: false
@@ -65,7 +66,15 @@ linters-settings:
- name: modifies-value-receiver
gofumpt:
extra-rules: true
- lang-version: 1.18
+ lang-version: "1.18"
+ depguard:
+ # TODO: use depguard to replace import checks in gitea-vet
+ list-type: denylist
+ # Check the list against standard lib.
+ include-go-root: true
+ packages-with-error-message:
+ - encoding/json: "use gitea's modules/json instead of encoding/json"
+ - github.com/unknwon/com: "use gitea's util and replacements"
issues:
exclude-rules:
@@ -153,5 +162,6 @@ issues:
- path: models/user/openid.go
linters:
- golint
- - linters: staticcheck
+ - linters:
+ - staticcheck
text: "strings.Title is deprecated: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead."
diff --git a/integrations/goget_test.go b/integrations/goget_test.go
index 5dc9c5e0a8..504d869990 100644
--- a/integrations/goget_test.go
+++ b/integrations/goget_test.go
@@ -29,8 +29,7 @@ func TestGoGet(t *testing.T) {
<body>
go get --insecure %[1]s:%[2]s/blah/glah
</body>
-</html>
-`, setting.Domain, setting.HTTPPort, setting.AppURL)
+</html>`, setting.Domain, setting.HTTPPort, setting.AppURL)
assert.Equal(t, expected, resp.Body.String())
}
diff --git a/models/migrations/migrations_test.go b/models/migrations/migrations_test.go
index f798d50117..a17eba54e8 100644
--- a/models/migrations/migrations_test.go
+++ b/models/migrations/migrations_test.go
@@ -24,7 +24,6 @@ import (
"code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
- "github.com/unknwon/com"
"xorm.io/xorm"
"xorm.io/xorm/names"
)
@@ -204,7 +203,7 @@ func prepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.En
deferFn := PrintCurrentTest(t, ourSkip)
assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
- assert.NoError(t, com.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"),
+ assert.NoError(t, util.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"),
setting.RepoRootPath))
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
if err != nil {
diff --git a/modules/cache/cache_redis.go b/modules/cache/cache_redis.go
index 148725ae66..e4b9a70f63 100644
--- a/modules/cache/cache_redis.go
+++ b/modules/cache/cache_redis.go
@@ -10,10 +10,10 @@ import (
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/nosql"
+ "code.gitea.io/gitea/modules/util"
"gitea.com/go-chi/cache"
"github.com/go-redis/redis/v8"
- "github.com/unknwon/com"
)
// RedisCacher represents a redis cache adapter implementation.
@@ -29,15 +29,15 @@ type RedisCacher struct {
func (c *RedisCacher) Put(key string, val interface{}, expire int64) error {
key = c.prefix + key
if expire == 0 {
- if err := c.c.Set(graceful.GetManager().HammerContext(), key, com.ToStr(val), 0).Err(); err != nil {
+ if err := c.c.Set(graceful.GetManager().HammerContext(), key, util.ToStr(val), 0).Err(); err != nil {
return err
}
} else {
- dur, err := time.ParseDuration(com.ToStr(expire) + "s")
+ dur, err := time.ParseDuration(util.ToStr(expire) + "s")
if err != nil {
return err
}
- if err = c.c.Set(graceful.GetManager().HammerContext(), key, com.ToStr(val), dur).Err(); err != nil {
+ if err = c.c.Set(graceful.GetManager().HammerContext(), key, util.ToStr(val), dur).Err(); err != nil {
return err
}
}
diff --git a/modules/context/context.go b/modules/context/context.go
index 4905e1cb80..f73b5f19c0 100644
--- a/modules/context/context.go
+++ b/modules/context/context.go
@@ -31,13 +31,13 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/translation"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/services/auth"
"gitea.com/go-chi/cache"
"gitea.com/go-chi/session"
chi "github.com/go-chi/chi/v5"
- "github.com/unknwon/com"
"github.com/unrolled/render"
"golang.org/x/crypto/pbkdf2"
)
@@ -475,7 +475,7 @@ func (ctx *Context) CookieDecrypt(secret, val string) (string, bool) {
}
key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New)
- text, err = com.AESGCMDecrypt(key, text)
+ text, err = util.AESGCMDecrypt(key, text)
return string(text), err == nil
}
@@ -489,7 +489,7 @@ func (ctx *Context) SetSuperSecureCookie(secret, name, value string, expiry int)
// CookieEncrypt encrypts a given value using the provided secret
func (ctx *Context) CookieEncrypt(secret, value string) string {
key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New)
- text, err := com.AESGCMEncrypt(key, []byte(value))
+ text, err := util.AESGCMEncrypt(key, []byte(value))
if err != nil {
panic("error encrypting cookie: " + err.Error())
}
diff --git a/modules/context/csrf.go b/modules/context/csrf.go
index 99c223c884..1fb992e2ae 100644
--- a/modules/context/csrf.go
+++ b/modules/context/csrf.go
@@ -19,13 +19,14 @@
package context
import (
+ "encoding/base32"
+ "fmt"
"net/http"
"time"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web/middleware"
-
- "github.com/unknwon/com"
)
// CSRF represents a CSRF service and is used to get the current token and validate a suspect token.
@@ -162,7 +163,12 @@ func prepareOptions(options []CsrfOptions) CsrfOptions {
// Defaults.
if len(opt.Secret) == 0 {
- opt.Secret = string(com.RandomCreateBytes(10))
+ randBytes, err := util.CryptoRandomBytes(8)
+ if err != nil {
+ // this panic can be handled by the recover() in http handlers
+ panic(fmt.Errorf("failed to generate random bytes: %w", err))
+ }
+ opt.Secret = base32.StdEncoding.EncodeToString(randBytes)
}
if len(opt.Header) == 0 {
opt.Header = "X-CSRFToken"
@@ -211,7 +217,7 @@ func Csrfer(opt CsrfOptions, ctx *Context) CSRF {
x.ID = "0"
uid := ctx.Session.Get(opt.SessionKey)
if uid != nil {
- x.ID = com.ToStr(uid)
+ x.ID = util.ToStr(uid)
}
needsNew := false
diff --git a/modules/context/repo.go b/modules/context/repo.go
index 5a9e38a1d9..a7c9a982c4 100644
--- a/modules/context/repo.go
+++ b/modules/context/repo.go
@@ -8,6 +8,7 @@ package context
import (
"context"
"fmt"
+ "html"
"io"
"net/http"
"net/url"
@@ -29,7 +30,6 @@ import (
asymkey_service "code.gitea.io/gitea/services/asymkey"
"github.com/editorconfig/editorconfig-core-go/v2"
- "github.com/unknwon/com"
)
// IssueTemplateDirCandidates issue templates directory
@@ -308,11 +308,9 @@ func EarlyResponseForGoGetMeta(ctx *Context) {
ctx.PlainText(http.StatusBadRequest, "invalid repository path")
return
}
- ctx.PlainText(http.StatusOK, com.Expand(`<meta name="go-import" content="{GoGetImport} git {CloneLink}">`,
- map[string]string{
- "GoGetImport": ComposeGoGetImport(username, reponame),
- "CloneLink": repo_model.ComposeHTTPSCloneURL(username, reponame),
- }))
+ goImportContent := fmt.Sprintf("%s git %s", ComposeGoGetImport(username, reponame), repo_model.ComposeHTTPSCloneURL(username, reponame))
+ htmlMeta := fmt.Sprintf(`<meta name="go-import" content="%s">`, html.EscapeString(goImportContent))
+ ctx.PlainText(http.StatusOK, htmlMeta)
}
// RedirectToRepo redirect to a differently-named repository
diff --git a/modules/json/json.go b/modules/json/json.go
index 3afa86023c..4361262a2f 100644
--- a/modules/json/json.go
+++ b/modules/json/json.go
@@ -8,7 +8,7 @@ package json
import (
"bytes"
"encoding/binary"
- "encoding/json"
+ "encoding/json" //nolint:depguard
"io"
jsoniter "github.com/json-iterator/go"
diff --git a/modules/markup/html.go b/modules/markup/html.go
index 6673f52bf4..c5d36e701f 100644
--- a/modules/markup/html.go
+++ b/modules/markup/html.go
@@ -21,9 +21,9 @@ import (
"code.gitea.io/gitea/modules/markup/common"
"code.gitea.io/gitea/modules/references"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/templates/vars"
"code.gitea.io/gitea/modules/util"
- "github.com/unknwon/com"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"
"mvdan.cc/xurls/v2"
@@ -838,7 +838,14 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
reftext := node.Data[ref.RefLocation.Start:ref.RefLocation.End]
if exttrack && !ref.IsPull {
ctx.Metas["index"] = ref.Issue
- link = createLink(com.Expand(ctx.Metas["format"], ctx.Metas), reftext, "ref-issue ref-external-issue")
+
+ res, err := vars.Expand(ctx.Metas["format"], ctx.Metas)
+ if err != nil {
+ // here we could just log the error and continue the rendering
+ log.Error("unable to expand template vars for ref %s, err: %v", ref.Issue, err)
+ }
+
+ link = createLink(res, reftext, "ref-issue ref-external-issue")
} else {
// Path determines the type of link that will be rendered. It's unknown at this point whether
// the linked item is actually a PR or an issue. Luckily it's of no real consequence because
diff --git a/modules/repository/init.go b/modules/repository/init.go
index d5a67df5d1..515992f97d 100644
--- a/modules/repository/init.go
+++ b/modules/repository/init.go
@@ -22,10 +22,9 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/options"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/templates/vars"
"code.gitea.io/gitea/modules/util"
asymkey_service "code.gitea.io/gitea/services/asymkey"
-
- "github.com/unknwon/com"
)
var (
@@ -250,8 +249,13 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir,
"CloneURL.HTTPS": cloneLink.HTTPS,
"OwnerName": repo.OwnerName,
}
+ res, err := vars.Expand(string(data), match)
+ if err != nil {
+ // here we could just log the error and continue the rendering
+ log.Error("unable to expand template vars for repo README: %s, err: %v", opts.Readme, err)
+ }
if err = os.WriteFile(filepath.Join(tmpDir, "README.md"),
- []byte(com.Expand(string(data), match)), 0o644); err != nil {
+ []byte(res), 0o644); err != nil {
return fmt.Errorf("write README.md: %v", err)
}
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 17a02bf5a1..ed91382de3 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -27,7 +27,6 @@ import (
"code.gitea.io/gitea/modules/user"
"code.gitea.io/gitea/modules/util"
- "github.com/unknwon/com"
gossh "golang.org/x/crypto/ssh"
ini "gopkg.in/ini.v1"
)
@@ -612,7 +611,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
Cfg.NameMapper = ini.SnackCase
- homeDir, err := com.HomeDir()
+ homeDir, err := util.HomeDir()
if err != nil {
log.Fatal("Failed to get home directory: %v", err)
}
diff --git a/modules/sync/unique_queue.go b/modules/sync/unique_queue.go
index d41726b5af..414cc50f39 100644
--- a/modules/sync/unique_queue.go
+++ b/modules/sync/unique_queue.go
@@ -5,9 +5,7 @@
package sync
-import (
- "github.com/unknwon/com"
-)
+import "code.gitea.io/gitea/modules/util"
// UniqueQueue is a queue which guarantees only one instance of same
// identity is in the line. Instances with same identity will be
@@ -73,13 +71,13 @@ func (q *UniqueQueue) Queue() <-chan string {
// Exist returns true if there is an instance with given identity
// exists in the queue.
func (q *UniqueQueue) Exist(id interface{}) bool {
- return q.table.IsRunning(com.ToStr(id))
+ return q.table.IsRunning(util.ToStr(id))
}
// AddFunc adds new instance to the queue with a custom runnable function,
// the queue is blocked until the function exits.
func (q *UniqueQueue) AddFunc(id interface{}, fn func()) {
- idStr := com.ToStr(id)
+ idStr := util.ToStr(id)
q.table.lock.Lock()
if _, ok := q.table.pool[idStr]; ok {
q.table.lock.Unlock()
@@ -105,5 +103,5 @@ func (q *UniqueQueue) Add(id interface{}) {
// Remove removes instance from the queue.
func (q *UniqueQueue) Remove(id interface{}) {
- q.table.Stop(com.ToStr(id))
+ q.table.Stop(util.ToStr(id))
}
diff --git a/modules/templates/vars/vars.go b/modules/templates/vars/vars.go
new file mode 100644
index 0000000000..a22ea4d777
--- /dev/null
+++ b/modules/templates/vars/vars.go
@@ -0,0 +1,93 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package vars
+
+import (
+ "fmt"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+// ErrWrongSyntax represents a wrong syntax with a template
+type ErrWrongSyntax struct {
+ Template string
+}
+
+func (err ErrWrongSyntax) Error() string {
+ return fmt.Sprintf("wrong syntax found in %s", err.Template)
+}
+
+// ErrVarMissing represents an error that no matched variable
+type ErrVarMissing struct {
+ Template string
+ Var string
+}
+
+func (err ErrVarMissing) Error() string {
+ return fmt.Sprintf("the variable %s is missing for %s", err.Var, err.Template)
+}
+
+// Expand replaces all variables like {var} by `vars` map, it always returns the expanded string regardless of errors
+// if error occurs, the error part doesn't change and is returned as it is.
+func Expand(template string, vars map[string]string) (string, error) {
+ // in the future, if necessary, we can introduce some escape-char,
+ // for example: it will use `#' as a reversed char, templates will use `{#{}` to do escape and output char '{'.
+ var buf strings.Builder
+ var err error
+
+ posBegin := 0
+ strLen := len(template)
+ for posBegin < strLen {
+ // find the next `{`
+ pos := strings.IndexByte(template[posBegin:], '{')
+ if pos == -1 {
+ buf.WriteString(template[posBegin:])
+ break
+ }
+
+ // copy texts between vars
+ buf.WriteString(template[posBegin : posBegin+pos])
+
+ // find the var between `{` and `}`/end
+ posBegin += pos
+ posEnd := posBegin + 1
+ for posEnd < strLen {
+ if template[posEnd] == '}' {
+ posEnd++
+ break
+ } // in the future, if we need to support escape chars, we can do: if (isEscapeChar) { posEnd+=2 }
+ posEnd++
+ }
+
+ // the var part, it can be "{", "{}", "{..." or or "{...}"
+ part := template[posBegin:posEnd]
+ posBegin = posEnd
+ if part == "{}" || part[len(part)-1] != '}' {
+ // treat "{}" or "{..." as error
+ err = ErrWrongSyntax{Template: template}
+ buf.WriteString(part)
+ } else {
+ // now we get a valid key "{...}"
+ key := part[1 : len(part)-1]
+ keyFirst, _ := utf8.DecodeRuneInString(key)
+ if unicode.IsSpace(keyFirst) || unicode.IsPunct(keyFirst) || unicode.IsControl(keyFirst) {
+ // the if key doesn't start with a letter, then we do not treat it as a var now
+ buf.WriteString(part)
+ } else {
+ // look up in the map
+ if val, ok := vars[key]; ok {
+ buf.WriteString(val)
+ } else {
+ // write the non-existing var as it is
+ buf.WriteString(part)
+ err = ErrVarMissing{Template: template, Var: key}
+ }
+ }
+ }
+ }
+
+ return buf.String(), err
+}
diff --git a/modules/templates/vars/vars_test.go b/modules/templates/vars/vars_test.go
new file mode 100644
index 0000000000..1cd7669c00
--- /dev/null
+++ b/modules/templates/vars/vars_test.go
@@ -0,0 +1,72 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package vars
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestExpandVars(t *testing.T) {
+ kases := []struct {
+ tmpl string
+ data map[string]string
+ out string
+ error bool
+ }{
+ {
+ tmpl: "{a}",
+ data: map[string]string{
+ "a": "1",
+ },
+ out: "1",
+ },
+ {
+ tmpl: "expand {a}, {b} and {c}, with non-var { } {#}",
+ data: map[string]string{
+ "a": "1",
+ "b": "2",
+ "c": "3",
+ },
+ out: "expand 1, 2 and 3, with non-var { } {#}",
+ },
+ {
+ tmpl: "中文内容 {一}, {二} 和 {三} 中文结尾",
+ data: map[string]string{
+ "一": "11",
+ "二": "22",
+ "三": "33",
+ },
+ out: "中文内容 11, 22 和 33 中文结尾",
+ },
+ {
+ tmpl: "expand {{a}, {b} and {c}",
+ data: map[string]string{
+ "a": "foo",
+ "b": "bar",
+ },
+ out: "expand {{a}, bar and {c}",
+ error: true,
+ },
+ {
+ tmpl: "expand } {} and {",
+ out: "expand } {} and {",
+ error: true,
+ },
+ }
+
+ for _, kase := range kases {
+ t.Run(kase.tmpl, func(t *testing.T) {
+ res, err := Expand(kase.tmpl, kase.data)
+ assert.EqualValues(t, kase.out, res)
+ if kase.error {
+ assert.Error(t, err)
+ } else {
+ assert.NoError(t, err)
+ }
+ })
+ }
+}
diff --git a/modules/util/copy.go b/modules/util/copy.go
deleted file mode 100644
index 46765849dc..0000000000
--- a/modules/util/copy.go
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2020 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package util
-
-import (
- "github.com/unknwon/com"
-)
-
-// CopyFile copies file from source to target path.
-func CopyFile(src, dest string) error {
- return com.Copy(src, dest)
-}
-
-// CopyDir copy files recursively from source to target directory.
-// It returns error when error occurs in underlying functions.
-func CopyDir(srcPath, destPath string) error {
- return com.CopyDir(srcPath, destPath)
-}
diff --git a/modules/util/io.go b/modules/util/io.go
index b467c0ac8a..0c677c359f 100644
--- a/modules/util/io.go
+++ b/modules/util/io.go
@@ -9,7 +9,7 @@ import (
)
// ReadAtMost reads at most len(buf) bytes from r into buf.
-// It returns the number of bytes copied. n is only less then len(buf) if r provides fewer bytes.
+// It returns the number of bytes copied. n is only less than len(buf) if r provides fewer bytes.
// If EOF occurs while reading, err will be nil.
func ReadAtMost(r io.Reader, buf []byte) (n int, err error) {
n, err = io.ReadFull(r, buf)
diff --git a/modules/util/legacy.go b/modules/util/legacy.go
new file mode 100644
index 0000000000..c7da541534
--- /dev/null
+++ b/modules/util/legacy.go
@@ -0,0 +1,84 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package util
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/rand"
+ "errors"
+
+ "github.com/unknwon/com" //nolint:depguard
+)
+
+// CopyFile copies file from source to target path.
+func CopyFile(src, dest string) error {
+ return com.Copy(src, dest)
+}
+
+// CopyDir copy files recursively from source to target directory.
+// It returns error when error occurs in underlying functions.
+func CopyDir(srcPath, destPath string) error {
+ return com.CopyDir(srcPath, destPath)
+}
+
+// ToStr converts any interface to string. should be replaced.
+func ToStr(value interface{}, args ...int) string {
+ return com.ToStr(value, args...)
+}
+
+// ToSnakeCase converts a string to snake_case. should be replaced.
+func ToSnakeCase(str string) string {
+ return com.ToSnakeCase(str)
+}
+
+// AESGCMEncrypt (from legacy package): encrypts plaintext with the given key using AES in GCM mode. should be replaced.
+func AESGCMEncrypt(key, plaintext []byte) ([]byte, error) {
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+
+ gcm, err := cipher.NewGCM(block)
+ if err != nil {
+ return nil, err
+ }
+
+ nonce := make([]byte, gcm.NonceSize())
+ if _, err := rand.Read(nonce); err != nil {
+ return nil, err
+ }
+
+ ciphertext := gcm.Seal(nil, nonce, plaintext, nil)
+ return append(nonce, ciphertext...), nil
+}
+
+// AESGCMDecrypt (from legacy package): decrypts ciphertext with the given key using AES in GCM mode. should be replaced.
+func AESGCMDecrypt(key, ciphertext []byte) ([]byte, error) {
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+
+ gcm, err := cipher.NewGCM(block)
+ if err != nil {
+ return nil, err
+ }
+
+ size := gcm.NonceSize()
+ if len(ciphertext)-size <= 0 {
+ return nil, errors.New("ciphertext is empty")
+ }
+
+ nonce := ciphertext[:size]
+ ciphertext = ciphertext[size:]
+
+ plainText, err := gcm.Open(nil, nonce, ciphertext, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return plainText, nil
+}
diff --git a/modules/util/legacy_test.go b/modules/util/legacy_test.go
new file mode 100644
index 0000000000..cfda93d3ad
--- /dev/null
+++ b/modules/util/legacy_test.go
@@ -0,0 +1,37 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package util
+
+import (
+ "crypto/aes"
+ "crypto/rand"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/unknwon/com" //nolint:depguard
+)
+
+func TestAESGCM(t *testing.T) {
+ t.Parallel()
+
+ key := make([]byte, aes.BlockSize)
+ _, err := rand.Read(key)
+ assert.NoError(t, err)
+
+ plaintext := []byte("this will be encrypted")
+
+ ciphertext, err := AESGCMEncrypt(key, plaintext)
+ assert.NoError(t, err)
+
+ decrypted, err := AESGCMDecrypt(key, ciphertext)
+ assert.NoError(t, err)
+
+ assert.Equal(t, plaintext, decrypted)
+
+ // at the moment, we make sure the result is the same as the legacy package, this assertion can be removed in next round refactoring
+ legacy, err := com.AESGCMDecrypt(key, ciphertext)
+ assert.NoError(t, err)
+ assert.Equal(t, legacy, plaintext)
+}
diff --git a/modules/util/path.go b/modules/util/path.go
index f4acf92ba9..ed7cc62699 100644
--- a/modules/util/path.go
+++ b/modules/util/path.go
@@ -154,6 +154,10 @@ func StatDir(rootPath string, includeDir ...bool) ([]string, error) {
return statDir(rootPath, "", isIncludeDir, false, false)
}
+func isOSWindows() bool {
+ return runtime.GOOS == "windows"
+}
+
// FileURLToPath extracts the path information from a file://... url.
func FileURLToPath(u *url.URL) (string, error) {
if u.Scheme != "file" {
@@ -162,7 +166,7 @@ func FileURLToPath(u *url.URL) (string, error) {
path := u.Path
- if runtime.GOOS != "windows" {
+ if !isOSWindows() {
return path, nil
}
@@ -173,3 +177,24 @@ func FileURLToPath(u *url.URL) (string, error) {
}
return path, nil
}
+
+// HomeDir returns path of '~'(in Linux) on Windows,
+// it returns error when the variable does not exist.
+func HomeDir() (home string, err error) {
+ // TODO: some users run Gitea with mismatched uid and "HOME=xxx" (they set HOME=xxx by environment manually)
+ // so at the moment we can not use `user.Current().HomeDir`
+ if isOSWindows() {
+ home = os.Getenv("USERPROFILE")
+ if home == "" {
+ home = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
+ }
+ } else {
+ home = os.Getenv("HOME")
+ }
+
+ if home == "" {
+ return "", errors.New("cannot get home directory")
+ }
+
+ return home, nil
+}
diff --git a/modules/web/middleware/binding.go b/modules/web/middleware/binding.go
index 9b0b1d7784..c9dc4a8f59 100644
--- a/modules/web/middleware/binding.go
+++ b/modules/web/middleware/binding.go
@@ -10,10 +10,10 @@ import (
"strings"
"code.gitea.io/gitea/modules/translation"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/validation"
"gitea.com/go-chi/binding"
- "github.com/unknwon/com"
)
// Form form binding interface
@@ -22,7 +22,7 @@ type Form interface {
}
func init() {
- binding.SetNameMapper(com.ToSnakeCase)
+ binding.SetNameMapper(util.ToSnakeCase)
}
// AssignForm assign form values back to the template data.
@@ -43,7 +43,7 @@ func AssignForm(form interface{}, data map[string]interface{}) {
if fieldName == "-" {
continue
} else if len(fieldName) == 0 {
- fieldName = com.ToSnakeCase(field.Name)
+ fieldName = util.ToSnakeCase(field.Name)
}
data[fieldName] = val.Field(i).Interface()
diff --git a/routers/web/goget.go b/routers/web/goget.go
index 4a31fcc2c5..a58739fe42 100644
--- a/routers/web/goget.go
+++ b/routers/web/goget.go
@@ -5,6 +5,8 @@
package web
import (
+ "fmt"
+ "html"
"net/http"
"net/url"
"path"
@@ -14,8 +16,6 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
-
- "github.com/unknwon/com"
)
func goGet(ctx *context.Context) {
@@ -65,23 +65,23 @@ func goGet(ctx *context.Context) {
if appURL.Scheme == string(setting.HTTP) {
insecure = "--insecure "
}
- ctx.RespHeader().Set("Content-Type", "text/html")
- ctx.Status(http.StatusOK)
- _, _ = ctx.Write([]byte(com.Expand(`<!doctype html>
+
+ goGetImport := context.ComposeGoGetImport(ownerName, trimmedRepoName)
+ goImportContent := fmt.Sprintf("%s git %s", goGetImport, repo_model.ComposeHTTPSCloneURL(ownerName, repoName) /*CloneLink*/)
+ goSourceContent := fmt.Sprintf("%s _ %s %s", goGetImport, prefix+"{/dir}" /*GoDocDirectory*/, prefix+"{/dir}/{file}#L{line}" /*GoDocFile*/)
+ goGetCli := fmt.Sprintf("go get %s%s", insecure, goGetImport)
+
+ res := fmt.Sprintf(`<!doctype html>
<html>
<head>
- <meta name="go-import" content="{GoGetImport} git {CloneLink}">
- <meta name="go-source" content="{GoGetImport} _ {GoDocDirectory} {GoDocFile}">
+ <meta name="go-import" content="%s">
+ <meta name="go-source" content="%s">
</head>
<body>
- go get {Insecure}{GoGetImport}
+ %s
</body>
-</html>
-`, map[string]string{
- "GoGetImport": context.ComposeGoGetImport(ownerName, trimmedRepoName),
- "CloneLink": repo_model.ComposeHTTPSCloneURL(ownerName, repoName),
- "GoDocDirectory": prefix + "{/dir}",
- "GoDocFile": prefix + "{/dir}/{file}#L{line}",
- "Insecure": insecure,
- })))
+</html>`, html.EscapeString(goImportContent), html.EscapeString(goSourceContent), html.EscapeString(goGetCli))
+
+ ctx.RespHeader().Set("Content-Type", "text/html")
+ _, _ = ctx.Write([]byte(res))
}
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index 486a63a9e1..a1a7200ba4 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -35,6 +35,7 @@ import (
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/templates/vars"
"code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
@@ -43,8 +44,6 @@ import (
"code.gitea.io/gitea/services/forms"
issue_service "code.gitea.io/gitea/services/issue"
pull_service "code.gitea.io/gitea/services/pull"
-
- "github.com/unknwon/com"
)
const (
@@ -1113,7 +1112,13 @@ func ViewIssue(ctx *context.Context) {
if extIssueUnit.ExternalTrackerConfig().ExternalTrackerStyle == markup.IssueNameStyleNumeric || extIssueUnit.ExternalTrackerConfig().ExternalTrackerStyle == "" {
metas := ctx.Repo.Repository.ComposeMetas()
metas["index"] = ctx.Params(":index")
- ctx.Redirect(com.Expand(extIssueUnit.ExternalTrackerConfig().ExternalTrackerFormat, metas))
+ res, err := vars.Expand(extIssueUnit.ExternalTrackerConfig().ExternalTrackerFormat, metas)
+ if err != nil {
+ log.Error("unable to expand template vars for issue url. issue: %s, err: %v", metas["index"], err)
+ ctx.ServerError("Expand", err)
+ return
+ }
+ ctx.Redirect(res)
return
}
} else if err != nil && !repo_model.IsErrUnitTypeNotExist(err) {