Compare commits

...

12 Commits

Author SHA1 Message Date
Joe Chen
5dcb6c64bd release: update version to 0.14.2
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 19:23:48 -05:00
JSS
b4bdb270d1 Fix git reset --end-of-options error on file upload and edit (#8184) 2026-02-18 19:13:57 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
0120bf0c86 js: use safe DOM construction for milestone and assignee selection (#8178)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 19:12:35 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
094b632182 context: reject access tokens passed via URL query parameters (#8177) 2026-02-18 19:12:23 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
a5c2cc0c6e template: escape untrusted names in locale strings piped through Safe (#8176)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-18 19:11:55 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
41b186cbfd database: use safe git-module API for tag deletion (#8175)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-18 19:11:43 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
51cf4cbe7e markup: restrict data URI scheme to safe image MIME types (#8174)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 19:10:56 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
5e6014c421 lfs: verify content hash and prevent object overwrite (#8166)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
2026-02-18 19:10:41 -05:00
Joe Chen
f5c8030c1f Fix up tests 2026-01-31 22:28:11 -05:00
Joe Chen
8c5c0125c4 release: update version to 0.14.1 2026-01-31 22:23:40 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
3f03530042 fix(ssh): git clone via built-in SSH server hangs (#8135)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 22:22:03 -05:00
Joe Chen
36c26c4ccc Update version to 0.14.0 2026-01-31 16:32:58 -05:00
24 changed files with 197 additions and 114 deletions

13
.claude/commands/ghsa.md Normal file
View File

@@ -0,0 +1,13 @@
Analyze and help fix the GitHub Security Advisory (GHSA) at: $ARGUMENTS
Steps:
1. Fetch the GHSA page using `gh api repos/gogs/gogs/security-advisories` and understand the vulnerability details (description, severity, affected versions, CWE).
2. Verify the reported vulnerability actually exists, and why.
3. Identify the affected code in this repository.
4. Propose a fix with a clear explanation of the root cause and how the fix addresses it. Check for prior art in the codebase to stay consistent with existing patterns.
5. Implement the fix. Only add tests when there is something meaningful to test at our layer.
6. Run all the usual build and test commands.
7. If a changelog entry is warranted (user will specify), add it to CHANGELOG.md with a placeholder for the PR link.
8. Create a branch named after the GHSA ID, commit, and push.
9. Create a pull request with a proper title and description, do not reveal too much detail and link the GHSA.
10. If a changelog entry was added, update it with the PR link, then commit and push again.

View File

@@ -4,6 +4,18 @@ All notable changes to Gogs are documented in this file.
## 0.15.0+dev (`main`)
### Fixed
- _Security:_ Cross-repository LFS object overwrite via missing content hash verification. [#8166](https://github.com/gogs/gogs/pull/8166) - [GHSA-gmf8-978x-2fg2](https://github.com/gogs/gogs/security/advisories/GHSA-gmf8-978x-2fg2)
- _Security:_ DOM-based XSS via issue meta selection on the issue page. [#8178](https://github.com/gogs/gogs/pull/8178) - [GHSA-vgjm-2cpf-4g7c](https://github.com/gogs/gogs/security/advisories/GHSA-vgjm-2cpf-4g7c)
- Unable to update files via web editor and API. [#8184](https://github.com/gogs/gogs/pull/8184)
### Removed
- Support for passing API access tokens via URL query parameters (`token`, `access_token`). Use the `Authorization` header instead. [#8177](https://github.com/gogs/gogs/pull/8177) - [GHSA-x9p5-w45c-7ffc](https://github.com/gogs/gogs/security/advisories/GHSA-x9p5-w45c-7ffc)
- Git clone via the built-in SSH server hangs. [#8132](https://github.com/gogs/gogs/issues/8132)
## 0.14.0
### Added

View File

@@ -279,6 +279,8 @@ ACCESS_CONTROL_ALLOW_ORIGIN =
STORAGE = local
; The root path to store LFS objects on local file system.
OBJECTS_PATH = data/lfs-objects
; The path to temporarily store LFS objects during upload verification.
OBJECTS_TEMP_PATH = data/tmp/lfs-objects
[attachment]
; Whether to enabled upload attachments in general.

2
go.mod
View File

@@ -20,7 +20,7 @@ require (
github.com/go-macaron/toolbox v0.0.0-20190813233741-94defb8383c6
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
github.com/gogs/git-module v1.8.6
github.com/gogs/git-module v1.8.7
github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4
github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0
github.com/gogs/minwinsvc v0.0.0-20170301035411-95be6356811a

4
go.sum
View File

@@ -146,8 +146,8 @@ github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561 h1:aBzukfDxQlCTVS0NBU
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 h1:yXtpJr/LV6PFu4nTLgfjQdcMdzjbqqXMEnHfq0Or6p8=
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14/go.mod h1:jPoNZLWDAqA5N3G5amEoiNbhVrmM+ZQEcnQvNQ2KaZk=
github.com/gogs/git-module v1.8.6 h1:4Io9vWZYQyIjdIPxfKgeYZXnDKNgydc6OZTxII5xCH4=
github.com/gogs/git-module v1.8.6/go.mod h1:IiMSJqi8XH62Kjqjt5Rw8IawSo+DHfM2dDjkSzWLjhs=
github.com/gogs/git-module v1.8.7 h1:GDyfzB1Z8ytld3LajTfUE4PuIcGcuCHpWB6j8/oD7Tk=
github.com/gogs/git-module v1.8.7/go.mod h1:IiMSJqi8XH62Kjqjt5Rw8IawSo+DHfM2dDjkSzWLjhs=
github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4 h1:C7NryI/RQhsIWwC2bHN601P1wJKeuQ6U/UCOYTn3Cic=
github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU=
github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0 h1:K02vod+sn3M1OOkdqi2tPxN2+xESK4qyITVQ3JkGEv4=

View File

@@ -14,7 +14,7 @@ import (
)
func init() {
conf.App.Version = "0.14.0+dev"
conf.App.Version = "0.14.2"
}
func main() {

View File

@@ -70,7 +70,7 @@ func Init(customConf string) error {
if err = File.Append(customConf); err != nil {
return errors.Wrapf(err, "append %q", customConf)
}
} else {
} else if !HookMode {
log.Warn("Custom config %q not found. Ignore this warning if you're running for the first time", customConf)
}
@@ -142,9 +142,11 @@ func Init(customConf string) error {
}
if IsWindowsRuntime() || semverutil.Compare(sshVersion, "<", "5.1") {
log.Warn(`SSH minimum key size check is forced to be disabled because server is not eligible:
if !HookMode {
log.Warn(`SSH minimum key size check is forced to be disabled because server is not eligible:
1. Windows server
2. OpenSSH version is lower than 5.1`)
}
} else {
SSH.MinimumKeySizes = map[string]int{}
for _, key := range File.Section("ssh.minimum_key_sizes").Keys() {
@@ -344,10 +346,13 @@ func Init(customConf string) error {
return errors.Wrap(err, "mapping [lfs] section")
}
LFS.ObjectsPath = ensureAbs(LFS.ObjectsPath)
LFS.ObjectsTempPath = ensureAbs(LFS.ObjectsTempPath)
handleDeprecated()
for _, warning := range checkInvalidOptions(File) {
log.Warn("%s", warning)
if !HookMode {
for _, warning := range checkInvalidOptions(File) {
log.Warn("%s", warning)
}
}
if err = File.Section("cache").MapTo(&Cache); err != nil {

View File

@@ -361,8 +361,9 @@ type DatabaseOpts struct {
var Database DatabaseOpts
type LFSOpts struct {
Storage string
ObjectsPath string
Storage string
ObjectsPath string
ObjectsTempPath string
}
// LFS settings
@@ -444,7 +445,7 @@ func checkInvalidOptions(config *ini.File) (warnings []string) {
"service": "auth",
}
for oldSection, newSection := range renamedSections {
if config.Section(oldSection).KeyStrings() != nil {
if len(config.Section(oldSection).KeyStrings()) > 0 {
warnings = append(warnings, fmt.Sprintf("section [%s] is invalid, use [%s] instead", oldSection, newSection))
}
}

View File

@@ -68,7 +68,6 @@ func TestCheckInvalidOptions(t *testing.T) {
"section [service] is invalid, use [auth] instead",
"option [server] ROOT_URL is invalid, use [server] EXTERNAL_URL instead",
"option [server] LANDING_PAGE is invalid, use [server] LANDING_URL instead",
"option [server] NONEXISTENT_OPTION is invalid",
}

View File

@@ -146,18 +146,12 @@ func authenticatedUserID(store AuthStore, c *macaron.Context, sess session.Store
// Check access token.
if isAPIPath(c.Req.URL.Path) {
tokenSHA := c.Query("token")
if len(tokenSHA) <= 0 {
tokenSHA = c.Query("access_token")
}
if tokenSHA == "" {
// Well, check with header again.
auHead := c.Req.Header.Get("Authorization")
if len(auHead) > 0 {
auths := strings.Fields(auHead)
if len(auths) == 2 && auths[0] == "token" {
tokenSHA = auths[1]
}
var tokenSHA string
auHead := c.Req.Header.Get("Authorization")
if auHead != "" {
auths := strings.Fields(auHead)
if len(auths) == 2 && auths[0] == "token" {
tokenSHA = auths[1]
}
}

View File

@@ -14,7 +14,6 @@ import (
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/errutil"
"gogs.io/gogs/internal/process"
)
// Release represents a release of repository.
@@ -359,11 +358,13 @@ func DeleteReleaseOfRepoByID(repoID, id int64) error {
return errors.Newf("GetRepositoryByID: %v", err)
}
_, stderr, err := process.ExecDir(-1, repo.RepoPath(),
fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID),
"git", "tag", "-d", rel.TagName)
if err != nil && !strings.Contains(stderr, "not found") {
return errors.Newf("git tag -d: %v - %s", err, stderr)
gitRepo, err := git.Open(repo.RepoPath())
if err != nil {
return errors.Newf("open repository: %v", err)
}
err = gitRepo.DeleteTag(rel.TagName)
if err != nil && !strings.Contains(err.Error(), "not found") {
return errors.Newf("delete tag: %v", err)
}
if _, err = x.Id(rel.ID).Delete(new(Release)); err != nil {

View File

@@ -158,6 +158,7 @@ func NewRepoContext() {
}
RemoveAllWithNotice("Clean up repository temporary data", filepath.Join(conf.Server.AppDataPath, "tmp"))
RemoveAllWithNotice("Clean up LFS temporary data", conf.LFS.ObjectsTempPath)
}
// Repository contains information of a repository.

View File

@@ -1,6 +1,8 @@
package lfsutil
import (
"crypto/sha256"
"encoding/hex"
"io"
"os"
"path/filepath"
@@ -10,7 +12,10 @@ import (
"gogs.io/gogs/internal/osutil"
)
var ErrObjectNotExist = errors.New("Object does not exist")
var (
ErrObjectNotExist = errors.New("object does not exist")
ErrOIDMismatch = errors.New("content hash does not match OID")
)
// Storager is an storage backend for uploading and downloading LFS objects.
type Storager interface {
@@ -39,6 +44,8 @@ var _ Storager = (*LocalStorage)(nil)
type LocalStorage struct {
// The root path for storing LFS objects.
Root string
// The path for storing temporary files during upload verification.
TempDir string
}
func (*LocalStorage) Storage() Storage {
@@ -58,29 +65,50 @@ func (s *LocalStorage) Upload(oid OID, rc io.ReadCloser) (int64, error) {
return 0, ErrInvalidOID
}
var err error
fpath := s.storagePath(oid)
defer func() {
rc.Close()
dir := filepath.Dir(fpath)
if err != nil {
_ = os.Remove(fpath)
}
}()
defer rc.Close()
err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm)
if err != nil {
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
return 0, errors.Wrap(err, "create directories")
}
w, err := os.Create(fpath)
if err != nil {
return 0, errors.Wrap(err, "create file")
}
defer w.Close()
written, err := io.Copy(w, rc)
// If the object file already exists, skip the upload and return the
// existing file's size.
if fi, err := os.Stat(fpath); err == nil {
_, _ = io.Copy(io.Discard, rc)
return fi.Size(), nil
}
// Write to a temp file and verify the content hash before publishing.
// This ensures the final path always contains a complete, hash-verified
// file, even when concurrent uploads of the same OID race.
if err := os.MkdirAll(s.TempDir, os.ModePerm); err != nil {
return 0, errors.Wrap(err, "create temp directory")
}
tmp, err := os.CreateTemp(s.TempDir, "upload-*")
if err != nil {
return 0, errors.Wrap(err, "copy file")
return 0, errors.Wrap(err, "create temp file")
}
tmpPath := tmp.Name()
defer os.Remove(tmpPath)
hash := sha256.New()
written, err := io.Copy(tmp, io.TeeReader(rc, hash))
if closeErr := tmp.Close(); err == nil && closeErr != nil {
err = closeErr
}
if err != nil {
return 0, errors.Wrap(err, "write object file")
}
if computed := hex.EncodeToString(hash.Sum(nil)); computed != string(oid) {
return 0, ErrOIDMismatch
}
if err := os.Rename(tmpPath, fpath); err != nil && !os.IsExist(err) {
return 0, errors.Wrap(err, "publish object file")
}
return written, nil
}

View File

@@ -10,6 +10,9 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gogs.io/gogs/internal/osutil"
)
func TestLocalStorage_storagePath(t *testing.T) {
@@ -46,50 +49,54 @@ func TestLocalStorage_storagePath(t *testing.T) {
}
func TestLocalStorage_Upload(t *testing.T) {
base := t.TempDir()
s := &LocalStorage{
Root: filepath.Join(os.TempDir(), "lfs-objects"),
Root: filepath.Join(base, "lfs-objects"),
TempDir: filepath.Join(base, "tmp", "lfs"),
}
t.Cleanup(func() {
_ = os.RemoveAll(s.Root)
const helloWorldOID = OID("c0535e4be2b79ffd93291305436bf889314e4a3faec05ecffcbb7df31ad9e51a") // "Hello world!"
t.Run("invalid OID", func(t *testing.T) {
written, err := s.Upload("bad_oid", io.NopCloser(strings.NewReader("")))
assert.Equal(t, int64(0), written)
assert.Equal(t, ErrInvalidOID, err)
})
tests := []struct {
name string
oid OID
content string
expWritten int64
expErr error
}{
{
name: "invalid oid",
oid: "bad_oid",
expErr: ErrInvalidOID,
},
t.Run("valid OID", func(t *testing.T) {
written, err := s.Upload(helloWorldOID, io.NopCloser(strings.NewReader("Hello world!")))
require.NoError(t, err)
assert.Equal(t, int64(12), written)
})
{
name: "valid oid",
oid: "ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f",
content: "Hello world!",
expWritten: 12,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
written, err := s.Upload(test.oid, io.NopCloser(strings.NewReader(test.content)))
assert.Equal(t, test.expWritten, written)
assert.Equal(t, test.expErr, err)
})
}
t.Run("valid OID but wrong content", func(t *testing.T) {
oid := OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f")
written, err := s.Upload(oid, io.NopCloser(strings.NewReader("Hello world!")))
assert.Equal(t, int64(0), written)
assert.Equal(t, ErrOIDMismatch, err)
// File should have been cleaned up.
assert.False(t, osutil.IsFile(s.storagePath(oid)))
})
t.Run("duplicate upload returns existing size", func(t *testing.T) {
written, err := s.Upload(helloWorldOID, io.NopCloser(strings.NewReader("should be ignored")))
require.NoError(t, err)
assert.Equal(t, int64(12), written)
// Verify original content is preserved.
var buf bytes.Buffer
err = s.Download(helloWorldOID, &buf)
require.NoError(t, err)
assert.Equal(t, "Hello world!", buf.String())
})
}
func TestLocalStorage_Download(t *testing.T) {
oid := OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f")
s := &LocalStorage{
Root: filepath.Join(os.TempDir(), "lfs-objects"),
Root: filepath.Join(t.TempDir(), "lfs-objects"),
}
t.Cleanup(func() {
_ = os.RemoveAll(s.Root)
})
fpath := s.storagePath(oid)
err := os.MkdirAll(filepath.Dir(fpath), os.ModePerm)

View File

@@ -1,6 +1,8 @@
package markup
import (
"net/url"
"strings"
"sync"
"github.com/microcosm-cc/bluemonday"
@@ -32,14 +34,28 @@ func NewSanitizer() {
sanitizer.policy.AllowAttrs("type").Matching(lazyregexp.New(`^checkbox$`).Regexp()).OnElements("input")
sanitizer.policy.AllowAttrs("checked", "disabled").OnElements("input")
// Data URLs
sanitizer.policy.AllowURLSchemes("data")
// Only allow data URIs with safe image MIME types to prevent XSS via
// "data:text/html" payloads.
sanitizer.policy.AllowURLSchemeWithCustomPolicy("data", isSafeDataURI)
// Custom URL-Schemes
sanitizer.policy.AllowURLSchemes(conf.Markdown.CustomURLSchemes...)
})
}
// isSafeDataURI returns whether the given data URI uses a safe image MIME type.
func isSafeDataURI(u *url.URL) bool {
// The opaque data of a data URI has the form "mediatype;base64,data" or
// "mediatype,data". We only allow common image MIME types.
mediatype, _, _ := strings.Cut(u.Opaque, ";")
mediatype, _, _ = strings.Cut(mediatype, ",")
switch strings.TrimSpace(strings.ToLower(mediatype)) {
case "image/png", "image/jpeg", "image/gif", "image/webp", "image/x-icon":
return true
}
return false
}
// Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist.
func Sanitize(s string) string {
return sanitizer.policy.Sanitize(s)

View File

@@ -26,6 +26,20 @@ func Test_Sanitizer(t *testing.T) {
{input: `<input type="hidden">`, expVal: ``},
{input: `<input type="checkbox">`, expVal: `<input type="checkbox">`},
{input: `<input checked disabled autofocus>`, expVal: `<input checked="" disabled="">`},
// Data URIs: safe image types should be allowed
{input: `<img src="data:image/png;base64,abc">`, expVal: `<img src="data:image/png;base64,abc">`},
{input: `<img src="data:image/jpeg;base64,abc">`, expVal: `<img src="data:image/jpeg;base64,abc">`},
{input: `<img src="data:image/gif;base64,abc">`, expVal: `<img src="data:image/gif;base64,abc">`},
{input: `<img src="data:image/webp;base64,abc">`, expVal: `<img src="data:image/webp;base64,abc">`},
// Data URIs: text/html must be stripped to prevent XSS (GHSA-xrcr-gmf5-2r8j)
{input: `<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">Click</a>`, expVal: `Click`},
{input: `<a href="data:text/html,<script>alert(1)</script>">XSS</a>`, expVal: `XSS`},
{input: `<img src="data:text/html;base64,abc">`, expVal: ``},
// Data URIs: SVG must be stripped (can contain embedded JavaScript)
{input: `<img src="data:image/svg+xml;base64,abc">`, expVal: ``},
}
for _, test := range tests {
t.Run(test.input, func(t *testing.T) {

View File

@@ -91,7 +91,7 @@ func (h *basicHandler) serveUpload(c *macaron.Context, repo *database.Repository
s := h.DefaultStorager()
written, err := s.Upload(oid, c.Req.Request.Body)
if err != nil {
if err == lfsutil.ErrInvalidOID {
if err == lfsutil.ErrInvalidOID || err == lfsutil.ErrOIDMismatch {
responseJSON(c.Resp, http.StatusBadRequest, responseError{
Message: err.Error(),
})
@@ -105,8 +105,8 @@ func (h *basicHandler) serveUpload(c *macaron.Context, repo *database.Repository
err = h.store.CreateLFSObject(c.Req.Context(), repo.ID, oid, written, s.Storage())
if err != nil {
// NOTE: It is OK to leave the file when the whole operation failed
// with a DB error, a retry on client side can safely overwrite the
// same file as OID is seen as unique to every file.
// with a DB error, a retry on client side will skip the upload as
// the file already exists on disk.
internalServerError(c.Resp)
log.Error("Failed to create object [repo_id: %d, oid: %s]: %v", repo.ID, oid, err)
return

View File

@@ -30,7 +30,7 @@ func RegisterRoutes(r *macaron.Router) {
store: store,
defaultStorage: lfsutil.Storage(conf.LFS.Storage),
storagers: map[lfsutil.Storage]lfsutil.Storager{
lfsutil.StorageLocal: &lfsutil.LocalStorage{Root: conf.LFS.ObjectsPath},
lfsutil.StorageLocal: &lfsutil.LocalStorage{Root: conf.LFS.ObjectsPath, TempDir: conf.LFS.ObjectsTempPath},
},
}
r.Combo("/:oid", verifyOID()).

View File

@@ -181,7 +181,7 @@ func renderFile(c *context.Context, entry *git.TreeEntry, treeLink, rawLink stri
output.Reset()
for i := 0; i < len(lines); i++ {
output.WriteString(fmt.Sprintf(`<span id="L%d">%d</span>`, i+1, i+1))
fmt.Fprintf(&output, `<span id="L%d">%d</span>`, i+1, i+1)
}
c.Data["LineNums"] = gotemplate.HTML(output.String())
}

View File

@@ -87,6 +87,7 @@ func handleServerConn(keyID string, chans <-chan ssh.NewChannel) {
_ = req.Reply(true, nil)
go func() {
_, _ = io.Copy(input, ch)
input.Close()
}()
_, _ = io.Copy(ch, stdout)
_, _ = io.Copy(ch.Stderr(), stderr)
@@ -187,7 +188,6 @@ func setupHostKeys(appDataPath string, algorithms []string) ([]ssh.Signer, error
conf.SSH.KeygenPath,
"-t", algo,
"-f", keyPath,
"-m", "PEM",
"-N", run.Arg(""),
}
err = run.Cmd(context.Background(), args...).Run().Wait()

View File

@@ -240,29 +240,19 @@ function initCommentForm() {
}
switch (input_id) {
case "#milestone_id":
$list
.find(".selected")
.html(
'<a class="item" href=' +
$(this).data("href") +
">" +
$(this).text() +
"</a>"
);
var $milestoneAnchor = $('<a class="item"></a>');
$milestoneAnchor.attr("href", $(this).data("href"));
$milestoneAnchor.text($(this).text());
$list.find(".selected").empty().append($milestoneAnchor);
break;
case "#assignee_id":
$list
.find(".selected")
.html(
'<a class="item" href=' +
$(this).data("href") +
">" +
'<img class="ui avatar image" src=' +
$(this).data("avatar") +
">" +
$(this).text() +
"</a>"
);
var $assigneeAnchor = $('<a class="item"></a>');
$assigneeAnchor.attr("href", $(this).data("href"));
$assigneeAnchor.append(
$('<img class="ui avatar image">').attr("src", $(this).data("avatar"))
);
$assigneeAnchor.append($("<span></span>").text($(this).text()));
$list.find(".selected").empty().append($assigneeAnchor);
}
$(".ui" + select_id + ".list .no-select").addClass("hide");
$(input_id).val($(this).data("id"));

View File

@@ -14,7 +14,7 @@
<div class="ui eleven wide column">
{{if .IsProtected}}<i class="octicon octicon-shield"></i> {{end}}<a class="markdown" href="{{$.RepoLink}}/src/{{EscapePound .Name}}"><code>{{.Name}}</code></a>
{{$timeSince := TimeSince .Commit.Committer.When $.Lang}}
<span class="ui text light grey">{{$.i18n.Tr "repo.branches.updated_by" $timeSince .Commit.Committer.Name | Safe}}</span>
<span class="ui text light grey">{{$.i18n.Tr "repo.branches.updated_by" $timeSince (Sanitize .Commit.Committer.Name) | Safe}}</span>
</div>
<div class="ui four wide column">
{{if and (and (eq $.BranchName .Name) $.IsRepositoryAdmin) (not $.Repository.IsMirror)}}

View File

@@ -13,7 +13,7 @@
<div class="ui eleven wide column">
{{if .DefaultBranch.IsProtected}}<i class="octicon octicon-shield"></i> {{end}}<a class="markdown" href="{{$.RepoLink}}/src/{{EscapePound .DefaultBranch.Name}}"><code>{{.DefaultBranch.Name}}</code></a>
{{$timeSince := TimeSince .DefaultBranch.Commit.Committer.When $.Lang}}
<span class="ui text light grey">{{$.i18n.Tr "repo.branches.updated_by" $timeSince .DefaultBranch.Commit.Committer.Name | Safe}}</span>
<span class="ui text light grey">{{$.i18n.Tr "repo.branches.updated_by" $timeSince (Sanitize .DefaultBranch.Commit.Committer.Name) | Safe}}</span>
</div>
{{if and $.IsRepositoryAdmin (not $.Repository.IsMirror)}}
<div class="ui four wide column">
@@ -33,7 +33,7 @@
<div class="ui eleven wide column">
{{if .IsProtected}}<i class="octicon octicon-shield"></i> {{end}}<a class="markdown" href="{{$.RepoLink}}/src/{{EscapePound .Name}}"><code>{{.Name}}</code></a>
{{$timeSince := TimeSince .Commit.Committer.When $.Lang}}
<span class="ui text light grey">{{$.i18n.Tr "repo.branches.updated_by" $timeSince .Commit.Committer.Name | Safe}}</span>
<span class="ui text light grey">{{$.i18n.Tr "repo.branches.updated_by" $timeSince (Sanitize .Commit.Committer.Name) | Safe}}</span>
</div>
{{if and $.IsRepositoryWriter $.AllowPullRequest}}
<div class="ui four wide column">
@@ -55,7 +55,7 @@
<div class="ui eleven wide column">
{{if .IsProtected}}<i class="octicon octicon-shield"></i> {{end}}<a class="markdown" href="{{$.RepoLink}}/src/{{EscapePound .Name}}"><code>{{.Name}}</code></a>
{{$timeSince := TimeSince .Commit.Committer.When $.Lang}}
<span class="ui text light grey">{{$.i18n.Tr "repo.branches.updated_by" $timeSince .Commit.Committer.Name | Safe}}</span>
<span class="ui text light grey">{{$.i18n.Tr "repo.branches.updated_by" $timeSince (Sanitize .Commit.Committer.Name) | Safe}}</span>
</div>
{{if and $.IsRepositoryWriter $.AllowPullRequest}}
<div class="ui four wide column">

View File

@@ -58,7 +58,7 @@
{{end}}
<div class="ui sub header">
{{$timeSince := TimeSince .Author.When $.Lang}}
{{.i18n.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince | Safe}}
{{.i18n.Tr "repo.wiki.last_commit_info" (Sanitize .Author.Name) $timeSince | Safe}}
</div>
</div>
<div class="markdown has-emoji">