Compare commits

..

22 Commits

Author SHA1 Message Date
Jonas Franz
30560b0f9b Add changelog for 1.6.3 (#5637)
* Add changelog for 1.6.3 and 1.7.0-rc2

Signed-off-by: Jonas Franz <info@jonasfranz.software>

* Remove 1.7.0 entries

Signed-off-by: Jonas Franz <info@jonasfranz.software>
2019-01-04 19:14:28 +01:00
zeripath
6076674d3a SECURITY: protect DeleteFilePost et al with cleanUploadFileName (#5631) (#5634)
This commit wraps more of the TreePaths with cleanUploadFileName

Signed-off-by: Andrew Thornton <art27@cantab.net>
2019-01-04 17:29:36 +01:00
Harshit Bansal
28cc3bd662 Fix wrong text getting saved on editing second comment on an issue. (#5608) (#5615)
* comments: Fix an incorrent DOM element selection.

This commit fixes a bug that was causing text from previously edited
comment to get saved when two comments were edited one after other.
Text area with id of `#content` isn't unique on the page but it was
being treated as unique by the event handling code.

Fixes: #5581.

* templates: Remove `id` from textarea in commit edit form.

An element is assigned an `id` only if it is unique for the whole page
but in this case there can be multiple textarea so it should have one.
2018-12-31 11:52:06 -05:00
techknowlogick
2631f7f64d Changelog for 1.6.2 (#5567) 2018-12-21 10:08:46 -05:00
techknowlogick
af4626a270 Immediate fix to htmlEncode user added text (#5575)
There are likely problems remaining with the way that initCommentForm
is creating its elements. I suspect that a malformed avatar url could
be used maliciously.
2018-12-21 09:05:47 -05:00
techknowlogick
21c70e1ed2 backport 5571 (#5573) 2018-12-21 16:22:56 +08:00
Lunny Xiao
b45d58805a fix indexer reindex bug when gitea restart (#5563) (#5564)
* fix issue indexer bug reindex when restart gitea

* also fix code indexer reindex when gitea restart
2018-12-19 09:51:53 -05:00
Greg Karékinian
200b974e19 Backport #5537 Remove a double slash in the HTTPS redirect with Let's Encrypt (#5539)
Before:

$ curl 0.0.0.0:3001
<a href="https://gitea.example.com:3000//">Found</a>.

After:

$ curl 0.0.0.0:3001
<a href="https://gitea.example.com:3000/">Found</a>.

Fixes #5536
2018-12-13 10:42:38 -05:00
Lunny Xiao
800271ee1f fix bug when a read perm user to edit his issue (#5516) (#5534) 2018-12-12 12:37:22 -05:00
Lunny Xiao
e6362f3d23 fix detect force push failure on deletion of protected branches (#5522) (#5531) 2018-12-12 09:49:47 -05:00
Greg Karékinian
716c2918be Backported #5525 Fix the Let's Encrypt handler (#5527)
* Fix the Let's Encrypt handler by listening on a valid address

Also handle errors in the HTTP server go routine, return a fatal error
when something goes wrong.

Thanks to @gbl08ma for finding the actual bug

Here is an example of the error handling:

    2018/12/11 14:23:07 [....io/gitea/cmd/web.go:87 func1()] [E] Failed to
    start the Let's Encrypt handler on port 30: listen tcp 0.0.0.0:30: bind:
    permission denied

Closes #5280

* Fix a typo
2018-12-11 13:34:35 -05:00
Lunny Xiao
60d7b614fe fix forgot deletion of notification when delete repository (#5506) (#5514) 2018-12-11 19:09:53 +08:00
Lunny Xiao
9cf9a54dca fix undeleted content when deleting user (#5509) 2018-12-11 10:33:20 +08:00
Lunny Xiao
2b4f87da46 Fix empty wiki (#5504) (#5508)
* fix wiki page when wiki path is exist but empty

* improve the error check
2018-12-10 22:37:56 +02:00
techknowlogick
ad9f9cdc30 Add 1.6.1 changelog (#5500) 2018-12-09 21:06:16 +08:00
Lunny Xiao
8237fd4a2d fix topic name length on database (#5493) (#5495) 2018-12-09 02:57:49 +02:00
romankl
8e4a0a978a ensure that the closed_at is set for closed (#5450)
right now the `closed_at` field for json responses is not filled during
the `APIIssue` creation for api responses.

For a closed issue you get a result like:
```json
"state":"open","comments":0,"created_at":"2018-11-29T16:39:24+01:00",
"updated_at":"2018-11-30T10:49:19+01:00","closed_at":null,
"due_date":null,"pull_request":null}
```
which has no information about the closing date. (which exists in the
db and ui)
with this PR the result changes to this:

```json
:null,"assignee":null,"assignees":null,
"state":"closed",
"comments":0,"created_at":"2018-11-29T16:43:05+01:00",
"updated_at":"2018-12-02T19:17:05+01:00",
"closed_at":"2018-12-02T19:17:05+01:00",
"due_date":null,"pull_request":null}
```

fixes: https://github.com/go-gitea/gitea/issues/5446
Signed-off-by: Roman <romaaan.git@gmail.com>
2018-12-02 17:50:47 -05:00
Lanre Adelowo
c1275e2ba6 Admin should be able to delete repos even if he is not a member of the organization (#5443) (#5447) 2018-12-02 20:07:20 +02:00
romankl
7bc1faabdb word-break the WebHook url to prevent a ui-break (#5445)
right now, the url is displayed with an anchor tag with no classes. If
the url is really really long, the url will break out of the containing
div and (depending on the url length) the browser shows the horizontal
scrollbar.
This pr makes use of the already existing css class `dont-break-out`
which gives all the anchor the necessary properties to prevent the
break.
Another solution could be to introduce some classes like
`text text-break-word`, but that would duplicate the `dont-break-out`
class just for text elements that use the `text` class.

fixes: https://github.com/go-gitea/gitea/issues/5416
Signed-off-by: Roman <romaaan.git@gmail.com>
2018-12-02 10:47:45 -05:00
Lunny Xiao
e406dc058d Fix repository deletion when there is large number of issues in it (#5426) (#5434) 2018-11-30 09:38:35 -05:00
Lauris BH
328e38ebc7 Fix dependent issue searching when gitea is run in subpath (#5392) (#5400) 2018-11-25 19:27:17 -05:00
Daniel Balko
773addf727 Backported #5383 on v1.6: API: '/orgs/:org/repos': return private repos with read access (#5310) (#3829) (#5393)
Signed-off-by: Daniel Balko <inxonic+github@gmail.com>
2018-11-24 13:14:27 +02:00
23 changed files with 268 additions and 112 deletions

View File

@@ -4,6 +4,36 @@ This changelog goes through all the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.io).
## [1.6.3](https://github.com/go-gitea/gitea/releases/tag/v1.6.3) - 2019-01-04
* SECURITY
* Prevent DeleteFilePost doing arbitrary deletion (#5631)
* BUGFIX
* Fix wrong text getting saved on editing second comment on an issue (#5608)
## [1.6.2](https://github.com/go-gitea/gitea/releases/tag/v1.6.2) - 2018-12-21
* SECURITY
* Sanitize uploaded file names (#5571) (#5573)
* HTMLEncode user added text (#5570) (#5575)
* BUGFIXES
* Fix indexer reindex bug when gitea restart (#5563) (#5564)
* Remove a double slash in the HTTPS redirect with Let's Encrypt (#5537) (#5539)
* Fix bug when a read perm user to edit his issue (#5516) (#5534)
* Detect force push failure on deletion of protected branches (#5522) (#5531)
* Let's Encrypt handler listens on correct port for certificate validation (#5525) (#5527)
* Fix forgot deletion of notification when delete repository (#5506) (#5514)
* Fix undeleted content when deleting user (#5429) (#5509)
* Fix empty wiki (#5504) (#5508)
## [1.6.1](https://github.com/go-gitea/gitea/releases/tag/v1.6.1) - 2018-12-08
* BUGFIXES
* Fix dependent issue searching when gitea is run in subpath (#5392) (#5400)
* API: '/orgs/:org/repos': return private repos with read access (#5393)
* Fix repository deletion when there is large number of issues in it (#5426) (#5434)
* Word-break the WebHook url to prevent a ui-break (#5445)
* Admin should be able to delete repos via the API even if they are not a member of the organization (#5443) (#5447)
* Ensure that the `closed_at` is set for closed (#5450)
* Fix topic name length on database (#5493) (#5495)
## [1.6.0](https://github.com/go-gitea/gitea/releases/tag/v1.6.0) - 2018-11-22
* BREAKING
* Respect email privacy option in user search via API (#4512)

View File

@@ -112,10 +112,15 @@ func runHookPreReceive(c *cli.Context) error {
branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
protectBranch, err := private.GetProtectedBranchBy(repoID, branchName)
if err != nil {
log.GitLogger.Fatal(2, "retrieve protected branches information failed")
fail("Internal error", fmt.Sprintf("retrieve protected branches information failed: %v", err))
}
if protectBranch != nil && protectBranch.IsProtected() {
// check and deletion
if newCommitID == git.EmptySHA {
fail(fmt.Sprintf("branch %s is protected from deletion", branchName), "")
}
// detect force push
if git.EmptySHA != oldCommitID {
output, err := git.NewCommand("rev-list", "--max-count=1", oldCommitID, "^"+newCommitID).RunInDir(repoPath)
@@ -126,17 +131,12 @@ func runHookPreReceive(c *cli.Context) error {
}
}
// check and deletion
if newCommitID == git.EmptySHA {
fail(fmt.Sprintf("branch %s is protected from deletion", branchName), "")
} else {
userID, _ := strconv.ParseInt(userIDStr, 10, 64)
canPush, err := private.CanUserPush(protectBranch.ID, userID)
if err != nil {
fail("Internal error", "Fail to detect user can push: %v", err)
} else if !canPush {
fail(fmt.Sprintf("protected branch %s can not be pushed to", branchName), "")
}
userID, _ := strconv.ParseInt(userIDStr, 10, 64)
canPush, err := private.CanUserPush(protectBranch.ID, userID)
if err != nil {
fail("Internal error", "Fail to detect user can push: %v", err)
} else if !canPush {
fail(fmt.Sprintf("protected branch %s can not be pushed to", branchName), "")
}
}
}

View File

@@ -80,7 +80,13 @@ func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler)
Cache: autocert.DirCache(directory),
Email: email,
}
go http.ListenAndServe(listenAddr+":"+setting.PortToRedirect, certManager.HTTPHandler(http.HandlerFunc(runLetsEncryptFallbackHandler))) // all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validatio happens here)
go func() {
log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
var err = http.ListenAndServe(setting.HTTPAddr+":"+setting.PortToRedirect, certManager.HTTPHandler(http.HandlerFunc(runLetsEncryptFallbackHandler))) // all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
if err != nil {
log.Fatal(4, "Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
}
}()
server := &http.Server{
Addr: listenAddr,
Handler: m,
@@ -96,7 +102,10 @@ func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Use HTTPS", http.StatusBadRequest)
return
}
target := setting.AppURL + r.URL.RequestURI()
// Remove the trailing slash at the end of setting.AppURL, the request
// URI always contains a leading slash, which would result in a double
// slash
target := strings.TrimRight(setting.AppURL, "/") + r.URL.RequestURI()
http.Redirect(w, r, target, http.StatusFound)
}

View File

@@ -212,21 +212,46 @@ func TestAPIViewRepo(t *testing.T) {
func TestAPIOrgRepos(t *testing.T) {
prepareTestEnv(t)
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 5}).(*models.User)
// User3 is an Org. Check their repos.
sourceOrg := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User)
// Login as User2.
session := loginUser(t, user.Name)
token := getTokenForLoggedInUser(t, session)
req := NewRequestf(t, "GET", "/api/v1/orgs/%s/repos?token="+token, sourceOrg.Name)
resp := session.MakeRequest(t, req, http.StatusOK)
var apiRepos []*api.Repository
DecodeJSON(t, resp, &apiRepos)
expectedLen := models.GetCount(t, models.Repository{OwnerID: sourceOrg.ID},
models.Cond("is_private = ?", false))
assert.Len(t, apiRepos, expectedLen)
for _, repo := range apiRepos {
assert.False(t, repo.Private)
expectedResults := map[*models.User]struct {
count int
includesPrivate bool
}{
nil: {count: 1},
user: {count: 2, includesPrivate: true},
user2: {count: 3, includesPrivate: true},
user3: {count: 1},
}
for userToLogin, expected := range expectedResults {
var session *TestSession
var testName string
var token string
if userToLogin != nil && userToLogin.ID > 0 {
testName = fmt.Sprintf("LoggedUser%d", userToLogin.ID)
session = loginUser(t, userToLogin.Name)
token = getTokenForLoggedInUser(t, session)
} else {
testName = "AnonymousUser"
session = emptyTestSession(t)
}
t.Run(testName, func(t *testing.T) {
req := NewRequestf(t, "GET", "/api/v1/orgs/%s/repos?token="+token, sourceOrg.Name)
resp := session.MakeRequest(t, req, http.StatusOK)
var apiRepos []*api.Repository
DecodeJSON(t, resp, &apiRepos)
assert.Len(t, apiRepos, expected.count)
for _, repo := range apiRepos {
if !expected.includesPrivate {
assert.False(t, repo.Private)
}
}
})
}
}

View File

@@ -325,6 +325,10 @@ func (issue *Issue) APIFormat() *api.Issue {
Updated: issue.UpdatedUnix.AsTime(),
}
if issue.ClosedUnix != 0 {
apiIssue.Closed = issue.ClosedUnix.AsTimePtr()
}
if issue.Milestone != nil {
apiIssue.Milestone = issue.Milestone.APIFormat()
}

View File

@@ -25,7 +25,7 @@ func reformatAndRemoveIncorrectTopics(x *xorm.Engine) (err error) {
type Topic struct {
ID int64
Name string `xorm:"UNIQUE"`
Name string `xorm:"UNIQUE VARCHAR(25)"`
RepoCount int
CreatedUnix int64 `xorm:"INDEX created"`
UpdatedUnix int64 `xorm:"INDEX updated"`

View File

@@ -32,6 +32,7 @@ import (
"github.com/Unknwon/cae/zip"
"github.com/Unknwon/com"
"github.com/go-xorm/builder"
"github.com/go-xorm/xorm"
"github.com/mcuadros/go-version"
"gopkg.in/ini.v1"
@@ -1837,55 +1838,56 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
&RepoRedirect{RedirectRepoID: repoID},
&Webhook{RepoID: repoID},
&HookTask{RepoID: repoID},
&Notification{RepoID: repoID},
); err != nil {
return fmt.Errorf("deleteBeans: %v", err)
}
// Delete comments and attachments.
issueIDs := make([]int64, 0, 25)
attachmentPaths := make([]string, 0, len(issueIDs))
if err = sess.
Table("issue").
Cols("id").
Where("repo_id=?", repoID).
Find(&issueIDs); err != nil {
deleteCond := builder.Select("id").From("issue").Where(builder.Eq{"repo_id": repoID})
// Delete comments and attachments
if _, err = sess.In("issue_id", deleteCond).
Delete(&Comment{}); err != nil {
return err
}
if len(issueIDs) > 0 {
if _, err = sess.In("issue_id", issueIDs).Delete(&Comment{}); err != nil {
return err
}
if _, err = sess.In("issue_id", issueIDs).Delete(&IssueUser{}); err != nil {
return err
}
if _, err = sess.In("issue_id", issueIDs).Delete(&Reaction{}); err != nil {
return err
}
if _, err = sess.In("issue_id", issueIDs).Delete(&IssueWatch{}); err != nil {
return err
}
if _, err = sess.In("issue_id", issueIDs).Delete(&Stopwatch{}); err != nil {
return err
}
if _, err = sess.In("issue_id", deleteCond).
Delete(&IssueUser{}); err != nil {
return err
}
attachments := make([]*Attachment, 0, 5)
if err = sess.
In("issue_id", issueIDs).
Find(&attachments); err != nil {
return err
}
for j := range attachments {
attachmentPaths = append(attachmentPaths, attachments[j].LocalPath())
}
if _, err = sess.In("issue_id", deleteCond).
Delete(&Reaction{}); err != nil {
return err
}
if _, err = sess.In("issue_id", issueIDs).Delete(&Attachment{}); err != nil {
return err
}
if _, err = sess.In("issue_id", deleteCond).
Delete(&IssueWatch{}); err != nil {
return err
}
if _, err = sess.Delete(&Issue{RepoID: repoID}); err != nil {
return err
}
if _, err = sess.In("issue_id", deleteCond).
Delete(&Stopwatch{}); err != nil {
return err
}
attachmentPaths := make([]string, 0, 20)
attachments := make([]*Attachment, 0, len(attachmentPaths))
if err = sess.Join("INNER", "issue", "issue.id = attachment.issue_id").
Where("issue.repo_id = ?", repoID).
Find(&attachments); err != nil {
return err
}
for j := range attachments {
attachmentPaths = append(attachmentPaths, attachments[j].LocalPath())
}
if _, err = sess.In("issue_id", deleteCond).
Delete(&Attachment{}); err != nil {
return err
}
if _, err = sess.Delete(&Issue{RepoID: repoID}); err != nil {
return err
}
if _, err = sess.Where("repo_id = ?", repoID).Delete(new(RepoUnit)); err != nil {

View File

@@ -535,6 +535,7 @@ func DeletePublicKey(doer *User, id int64) (err error) {
if err = sess.Commit(); err != nil {
return err
}
sess.Close()
return RewriteAllPublicKeys()
}
@@ -543,6 +544,10 @@ func DeletePublicKey(doer *User, id int64) (err error) {
// Note: x.Iterate does not get latest data after insert/delete, so we have to call this function
// outside any session scope independently.
func RewriteAllPublicKeys() error {
return rewriteAllPublicKeys(x)
}
func rewriteAllPublicKeys(e Engine) error {
//Don't rewrite key if internal server
if setting.SSH.StartBuiltinServer {
return nil
@@ -569,7 +574,7 @@ func RewriteAllPublicKeys() error {
}
}
err = x.Iterate(new(PublicKey), func(idx int, bean interface{}) (err error) {
err = e.Iterate(new(PublicKey), func(idx int, bean interface{}) (err error) {
_, err = t.WriteString((bean.(*PublicKey)).AuthorizedString())
return err
})

View File

@@ -26,7 +26,7 @@ var topicPattern = regexp.MustCompile(`^[a-z0-9][a-z0-9-]*$`)
// Topic represents a topic of repositories
type Topic struct {
ID int64
Name string `xorm:"UNIQUE"`
Name string `xorm:"UNIQUE VARCHAR(25)"`
RepoCount int
CreatedUnix util.TimeStamp `xorm:"INDEX created"`
UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`

View File

@@ -1038,25 +1038,26 @@ func deleteUser(e *xorm.Session, u *User) error {
&EmailAddress{UID: u.ID},
&UserOpenID{UID: u.ID},
&Reaction{UserID: u.ID},
&TeamUser{UID: u.ID},
&Collaboration{UserID: u.ID},
&Stopwatch{UserID: u.ID},
); err != nil {
return fmt.Errorf("deleteBeans: %v", err)
}
// ***** START: PublicKey *****
keys := make([]*PublicKey, 0, 10)
if err = e.Find(&keys, &PublicKey{OwnerID: u.ID}); err != nil {
return fmt.Errorf("get all public keys: %v", err)
}
keyIDs := make([]int64, len(keys))
for i := range keys {
keyIDs[i] = keys[i].ID
}
if err = deletePublicKeys(e, keyIDs...); err != nil {
if _, err = e.Delete(&PublicKey{OwnerID: u.ID}); err != nil {
return fmt.Errorf("deletePublicKeys: %v", err)
}
rewriteAllPublicKeys(e)
// ***** END: PublicKey *****
// ***** START: GPGPublicKey *****
if _, err = e.Delete(&GPGKey{OwnerID: u.ID}); err != nil {
return fmt.Errorf("deleteGPGKeys: %v", err)
}
// ***** END: GPGPublicKey *****
// Clear assignee.
if err = clearAssigneeByUserID(e, u.ID); err != nil {
return fmt.Errorf("clear assignee: %v", err)
@@ -1110,6 +1111,7 @@ func DeleteUser(u *User) (err error) {
if err = sess.Commit(); err != nil {
return err
}
sess.Close()
return RewriteAllPublicKeys()
}

View File

@@ -60,7 +60,7 @@ func InitIssueIndexer(populateIndexer func() error) {
return
}
if err = createIssueIndexer(); err != nil {
if err = createIssueIndexer(setting.Indexer.IssuePath, issueIndexerLatestVersion); err != nil {
log.Fatal(4, "InitIssuesIndexer: create index, %v", err)
}
if err = populateIndexer(); err != nil {
@@ -69,7 +69,7 @@ func InitIssueIndexer(populateIndexer func() error) {
}
// createIssueIndexer create an issue indexer if one does not already exist
func createIssueIndexer() error {
func createIssueIndexer(path string, latestVersion int) error {
mapping := bleve.NewIndexMapping()
docMapping := bleve.NewDocumentMapping()
@@ -100,8 +100,14 @@ func createIssueIndexer() error {
mapping.AddDocumentMapping("_all", bleve.NewDocumentDisabledMapping())
var err error
issueIndexer, err = bleve.New(setting.Indexer.IssuePath, mapping)
return err
issueIndexer, err = bleve.New(path, mapping)
if err != nil {
return err
}
return rupture.WriteIndexMetadata(path, &rupture.IndexMetadata{
Version: latestVersion,
})
}
// IssueIndexerBatch batch to add updates to

View File

@@ -84,7 +84,7 @@ func InitRepoIndexer(populateIndexer func() error) {
return
}
if err = createRepoIndexer(); err != nil {
if err = createRepoIndexer(setting.Indexer.RepoPath, repoIndexerLatestVersion); err != nil {
log.Fatal(4, "CreateRepoIndexer: %v", err)
}
if err = populateIndexer(); err != nil {
@@ -93,7 +93,7 @@ func InitRepoIndexer(populateIndexer func() error) {
}
// createRepoIndexer create a repo indexer if one does not already exist
func createRepoIndexer() error {
func createRepoIndexer(path string, latestVersion int) error {
var err error
docMapping := bleve.NewDocumentMapping()
numericFieldMapping := bleve.NewNumericFieldMapping()
@@ -119,8 +119,13 @@ func createRepoIndexer() error {
mapping.AddDocumentMapping(repoIndexerDocType, docMapping)
mapping.AddDocumentMapping("_all", bleve.NewDocumentDisabledMapping())
repoIndexer, err = bleve.New(setting.Indexer.RepoPath, mapping)
return err
repoIndexer, err = bleve.New(path, mapping)
if err != nil {
return err
}
return rupture.WriteIndexMetadata(path, &rupture.IndexMetadata{
Version: latestVersion,
})
}
func filenameIndexerID(repoID int64, filename string) string {

View File

@@ -1,5 +1,9 @@
'use strict';
function htmlEncode(text) {
return jQuery('<div />').text(text).html()
}
var csrf;
var suburl;
@@ -312,12 +316,12 @@ function initCommentForm() {
switch (input_id) {
case '#milestone_id':
$list.find('.selected').html('<a class="item" href=' + $(this).data('href') + '>' +
$(this).text() + '</a>');
htmlEncode($(this).text()) + '</a>');
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>');
htmlEncode($(this).text()) + '</a>');
}
$('.ui' + select_id + '.list .no-select').addClass('hide');
$(input_id).val($(this).data('id'));
@@ -604,7 +608,7 @@ function initRepository() {
// Setup new form
if ($editContentZone.html().length == 0) {
$editContentZone.html($('#edit-content-form').html());
$textarea = $('#content');
$textarea = $editContentZone.find('textarea');
issuesTribute.attach($textarea.get());
emojiTribute.attach($textarea.get());
@@ -1456,7 +1460,7 @@ function searchUsers() {
$.each(response.data, function (i, item) {
var title = item.login;
if (item.full_name && item.full_name.length > 0) {
title += ' (' + item.full_name + ')';
title += ' (' + htmlEncode(item.full_name) + ')';
}
items.push({
title: title,
@@ -2510,7 +2514,7 @@ function initTopicbar() {
if (res.topics) {
formattedResponse.success = true;
for (var i=0;i < res.topics.length;i++) {
formattedResponse.results.push({"description": res.topics[i].Name, "data-value":res.topics[i].Name})
formattedResponse.results.push({"description": res.topics[i].Name, "data-value": res.topics[i].Name})
}
}
@@ -2625,13 +2629,13 @@ function initIssueList() {
$('.new-dependency-drop-list')
.dropdown({
apiSettings: {
url: suburl + '/api/v1/repos' + repolink + '/issues?q={query}',
url: suburl + '/api/v1/repos/' + repolink + '/issues?q={query}',
onResponse: function(response) {
var filteredResponse = {'success': true, 'results': []};
// Parse the response from the api to work with our dropdown
$.each(response, function(index, issue) {
filteredResponse.results.push({
'name' : '#' + issue.number + '&nbsp;' + issue.title,
'name' : '#' + issue.number + '&nbsp;' + htmlEncode(issue.title),
'value' : issue.id
});
});

View File

@@ -508,7 +508,7 @@ func Delete(ctx *context.APIContext) {
owner := ctx.Repo.Owner
repo := ctx.Repo.Repository
if owner.IsOrganization() {
if owner.IsOrganization() && !ctx.User.IsAdmin {
isOwner, err := owner.IsOwnedBy(ctx.User.ID)
if err != nil {
ctx.Error(500, "IsOwnedBy", err)

View File

@@ -11,14 +11,13 @@ import (
)
// listUserRepos - List the repositories owned by the given user.
func listUserRepos(ctx *context.APIContext, u *models.User) {
showPrivateRepos := ctx.IsSigned && (ctx.User.ID == u.ID || ctx.User.IsAdmin)
repos, err := models.GetUserRepositories(u.ID, showPrivateRepos, 1, u.NumRepos, "")
func listUserRepos(ctx *context.APIContext, u *models.User, private bool) {
repos, err := models.GetUserRepositories(u.ID, private, 1, u.NumRepos, "")
if err != nil {
ctx.Error(500, "GetUserRepositories", err)
return
}
apiRepos := make([]*api.Repository, len(repos))
apiRepos := make([]*api.Repository, 0, len(repos))
var ctxUserID int64
if ctx.User != nil {
ctxUserID = ctx.User.ID
@@ -29,7 +28,9 @@ func listUserRepos(ctx *context.APIContext, u *models.User) {
ctx.Error(500, "AccessLevel", err)
return
}
apiRepos[i] = repos[i].APIFormat(access)
if ctx.IsSigned && ctx.User.IsAdmin || access >= models.AccessModeRead {
apiRepos = append(apiRepos, repos[i].APIFormat(access))
}
}
ctx.JSON(200, &apiRepos)
}
@@ -54,7 +55,8 @@ func ListUserRepos(ctx *context.APIContext) {
if ctx.Written() {
return
}
listUserRepos(ctx, user)
private := ctx.IsSigned && (ctx.User.ID == user.ID || ctx.User.IsAdmin)
listUserRepos(ctx, user, private)
}
// ListMyRepos - list the repositories you own or have access to.
@@ -106,5 +108,5 @@ func ListOrgRepos(ctx *context.APIContext) {
// responses:
// "200":
// "$ref": "#/responses/RepositoryList"
listUserRepos(ctx, ctx.Org.Organization)
listUserRepos(ctx, ctx.Org.Organization, ctx.IsSigned)
}

View File

@@ -163,7 +163,11 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo
branchName = form.NewBranchName
}
form.TreePath = strings.Trim(path.Clean("/"+form.TreePath), " /")
form.TreePath = cleanUploadFileName(form.TreePath)
if len(form.TreePath) == 0 {
ctx.Error(500, "Upload file name is invalid")
return
}
treeNames, treePaths := getParentTreeFields(form.TreePath)
ctx.Data["TreePath"] = form.TreePath
@@ -373,6 +377,13 @@ func DeleteFile(ctx *context.Context) {
func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) {
ctx.Data["PageIsDelete"] = true
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
ctx.Repo.TreePath = cleanUploadFileName(ctx.Repo.TreePath)
if len(ctx.Repo.TreePath) == 0 {
ctx.Error(500, "Delete file name is invalid")
return
}
ctx.Data["TreePath"] = ctx.Repo.TreePath
canCommit := renderCommitRights(ctx)
@@ -477,7 +488,12 @@ func UploadFilePost(ctx *context.Context, form auth.UploadRepoFileForm) {
branchName = form.NewBranchName
}
form.TreePath = strings.Trim(path.Clean("/"+form.TreePath), " /")
form.TreePath = cleanUploadFileName(form.TreePath)
if len(form.TreePath) == 0 {
ctx.Error(500, "Upload file name is invalid")
return
}
treeNames, treePaths := getParentTreeFields(form.TreePath)
if len(treeNames) == 0 {
// We must at least have one element for user to input.
@@ -559,6 +575,17 @@ func UploadFilePost(ctx *context.Context, form auth.UploadRepoFileForm) {
ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + branchName + "/" + form.TreePath)
}
func cleanUploadFileName(name string) string {
name = strings.TrimLeft(name, "./\\")
name = strings.Replace(name, "../", "", -1)
name = strings.Replace(name, "..\\", "", -1)
name = strings.TrimPrefix(path.Clean(name), ".git/")
if name == ".git" {
return ""
}
return name
}
// UploadFileToServer upload file to server file dir not git
func UploadFileToServer(ctx *context.Context) {
file, header, err := ctx.Req.FormFile("file")
@@ -591,7 +618,13 @@ func UploadFileToServer(ctx *context.Context) {
}
}
upload, err := models.NewUpload(header.Filename, buf, file)
name := cleanUploadFileName(header.Filename)
if len(name) == 0 {
ctx.Error(500, "Upload file name is invalid")
return
}
upload, err := models.NewUpload(name, buf, file)
if err != nil {
ctx.Error(500, fmt.Sprintf("NewUpload: %v", err))
return

View File

@@ -0,0 +1,30 @@
// Copyright 2018 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 repo
import (
"testing"
"code.gitea.io/gitea/models"
"github.com/stretchr/testify/assert"
)
func TestCleanUploadName(t *testing.T) {
models.PrepareTestEnv(t)
var kases = map[string]string{
".git/refs/master": "git/refs/master",
"/root/abc": "root/abc",
"./../../abc": "abc",
"a/../.git": "a/.git",
"a/../../../abc": "a/abc",
"../../../acd": "acd",
"../../.git/abc": "git/abc",
"..\\..\\.git/abc": "git/abc",
}
for k, v := range kases {
assert.EqualValues(t, v, cleanUploadFileName(k))
}
}

View File

@@ -73,7 +73,6 @@ func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, err
commit, err := wikiRepo.GetBranchCommit("master")
if err != nil {
ctx.ServerError("GetBranchCommit", err)
return wikiRepo, nil, err
}
return wikiRepo, commit, nil
@@ -111,6 +110,9 @@ func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName strin
func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *git.TreeEntry) {
wikiRepo, commit, err := findWikiRepoCommit(ctx)
if err != nil {
if !git.IsErrNotExist(err) {
ctx.ServerError("GetBranchCommit", err)
}
return nil, nil
}

View File

@@ -51,8 +51,6 @@
{{end}}
{{if .RequireTribute}}
<script src="{{AppSubUrl}}/vendor/plugins/tribute/tribute.min.js"></script>
{{if .Assignees}}
<script>
var issuesTribute = new Tribute({
values: [
@@ -73,7 +71,6 @@
})
issuesTribute.attach(document.getElementById('content'))
</script>
{{end}}
<script>
var emojiTribute = new Tribute({
collection: [{

View File

@@ -204,7 +204,7 @@
<a class="preview item" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
</div>
<div class="ui bottom attached active write tab segment">
<textarea tabindex="1" id="content" name="content"></textarea>
<textarea tabindex="1" name="content"></textarea>
</div>
<div class="ui bottom attached tab preview segment markdown">
{{$.i18n.Tr "loading"}}

View File

@@ -115,7 +115,7 @@
<a class="preview item" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
</div>
<div class="ui bottom attached active write tab segment">
<textarea tabindex="1" id="content" name="content"></textarea>
<textarea tabindex="1" name="content"></textarea>
</div>
<div class="ui bottom attached tab preview segment markdown">
{{$.i18n.Tr "loading"}}

View File

@@ -338,7 +338,7 @@
</div>
</div>
{{if .CanCreateIssueDependencies}}
<input type="hidden" id="repolink" value="{{$.RepoLink}}">
<input type="hidden" id="repolink" value="{{$.RepoRelPath}}">
<!-- I know, there is probably a better way to do this -->
<input type="hidden" id="issueIndex" value="{{.Issue.Index}}"/>

View File

@@ -38,7 +38,7 @@
{{else}}
<span class="text grey"><i class="octicon octicon-primitive-dot"></i></span>
{{end}}
<a href="{{$.BaseLink}}/settings/hooks/{{.ID}}">{{.URL}}</a>
<a class="dont-break-out" href="{{$.BaseLink}}/settings/hooks/{{.ID}}">{{.URL}}</a>
<div class="ui right">
<span class="text blue"><a href="{{$.BaseLink}}/settings/hooks/{{.ID}}"><i class="fa fa-pencil"></i></a></span>
<span class="text red"><a class="delete-button" data-url="{{$.Link}}/delete" data-id="{{.ID}}"><i class="fa fa-times"></i></a></span>