Skip to content

Commit 42b9b46

Browse files
authored
Never add labels not from this repository or organisation and remove org labels on transfer (#14928)
* Never add labels not from this repository or organisation and remove org labels on transfer Prevent the addition of labels from outside of the repository or organisation and remove organisation labels on transfer. Related #14908 * switch to use sql * subquery alias * once more around the merry go round * fix api problem
1 parent ccfb205 commit 42b9b46

File tree

4 files changed

+65
-9
lines changed

4 files changed

+65
-9
lines changed

integrations/api_issue_label_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,22 +91,22 @@ func TestAPIAddIssueLabels(t *testing.T) {
9191

9292
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
9393
issue := models.AssertExistsAndLoadBean(t, &models.Issue{RepoID: repo.ID}).(*models.Issue)
94-
label := models.AssertExistsAndLoadBean(t, &models.Label{RepoID: repo.ID}).(*models.Label)
94+
_ = models.AssertExistsAndLoadBean(t, &models.Label{RepoID: repo.ID, ID: 2}).(*models.Label)
9595
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
9696

9797
session := loginUser(t, owner.Name)
9898
token := getTokenForLoggedInUser(t, session)
9999
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels?token=%s",
100-
owner.Name, repo.Name, issue.Index, token)
100+
repo.OwnerName, repo.Name, issue.Index, token)
101101
req := NewRequestWithJSON(t, "POST", urlStr, &api.IssueLabelsOption{
102-
Labels: []int64{label.ID},
102+
Labels: []int64{1, 2},
103103
})
104104
resp := session.MakeRequest(t, req, http.StatusOK)
105105
var apiLabels []*api.Label
106106
DecodeJSON(t, resp, &apiLabels)
107107
assert.Len(t, apiLabels, models.GetCount(t, &models.IssueLabel{IssueID: issue.ID}))
108108

109-
models.AssertExistsAndLoadBean(t, &models.IssueLabel{IssueID: issue.ID, LabelID: label.ID})
109+
models.AssertExistsAndLoadBean(t, &models.IssueLabel{IssueID: issue.ID, LabelID: 2})
110110
}
111111

112112
func TestAPIReplaceIssueLabels(t *testing.T) {

models/issue.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,10 @@ func (issue *Issue) ReplaceLabels(labels []*Label, doer *User) (err error) {
513513
return err
514514
}
515515

516+
if err = issue.loadRepo(sess); err != nil {
517+
return err
518+
}
519+
516520
if err = issue.loadLabels(sess); err != nil {
517521
return err
518522
}
@@ -527,10 +531,18 @@ func (issue *Issue) ReplaceLabels(labels []*Label, doer *User) (err error) {
527531
addLabel := labels[addIndex]
528532
removeLabel := issue.Labels[removeIndex]
529533
if addLabel.ID == removeLabel.ID {
534+
// Silently drop invalid labels
535+
if removeLabel.RepoID != issue.RepoID && removeLabel.OrgID != issue.Repo.OwnerID {
536+
toRemove = append(toRemove, removeLabel)
537+
}
538+
530539
addIndex++
531540
removeIndex++
532541
} else if addLabel.ID < removeLabel.ID {
533-
toAdd = append(toAdd, addLabel)
542+
// Only add if the label is valid
543+
if addLabel.RepoID == issue.RepoID || addLabel.OrgID == issue.Repo.OwnerID {
544+
toAdd = append(toAdd, addLabel)
545+
}
534546
addIndex++
535547
} else {
536548
toRemove = append(toRemove, removeLabel)

models/issue_label.go

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ func GetLabelsByIDs(labelIDs []int64) ([]*Label, error) {
321321
return labels, x.Table("label").
322322
In("id", labelIDs).
323323
Asc("name").
324-
Cols("id").
324+
Cols("id", "repo_id", "org_id").
325325
Find(&labels)
326326
}
327327

@@ -632,6 +632,8 @@ func HasIssueLabel(issueID, labelID int64) bool {
632632
return hasIssueLabel(x, issueID, labelID)
633633
}
634634

635+
// newIssueLabel this function creates a new label it does not check if the label is valid for the issue
636+
// YOU MUST CHECK THIS BEFORE THIS FUNCTION
635637
func newIssueLabel(e *xorm.Session, issue *Issue, label *Label, doer *User) (err error) {
636638
if _, err = e.Insert(&IssueLabel{
637639
IssueID: issue.ID,
@@ -671,6 +673,15 @@ func NewIssueLabel(issue *Issue, label *Label, doer *User) (err error) {
671673
return err
672674
}
673675

676+
if err = issue.loadRepo(sess); err != nil {
677+
return err
678+
}
679+
680+
// Do NOT add invalid labels
681+
if issue.RepoID != label.RepoID && issue.Repo.OwnerID != label.OrgID {
682+
return nil
683+
}
684+
674685
if err = newIssueLabel(sess, issue, label, doer); err != nil {
675686
return err
676687
}
@@ -683,13 +694,19 @@ func NewIssueLabel(issue *Issue, label *Label, doer *User) (err error) {
683694
return sess.Commit()
684695
}
685696

697+
// newIssueLabels add labels to an issue. It will check if the labels are valid for the issue
686698
func newIssueLabels(e *xorm.Session, issue *Issue, labels []*Label, doer *User) (err error) {
687-
for i := range labels {
688-
if hasIssueLabel(e, issue.ID, labels[i].ID) {
699+
if err = issue.loadRepo(e); err != nil {
700+
return err
701+
}
702+
for _, label := range labels {
703+
// Don't add already present labels and invalid labels
704+
if hasIssueLabel(e, issue.ID, label.ID) ||
705+
(label.RepoID != issue.RepoID && label.OrgID != issue.Repo.OwnerID) {
689706
continue
690707
}
691708

692-
if err = newIssueLabel(e, issue, labels[i], doer); err != nil {
709+
if err = newIssueLabel(e, issue, label, doer); err != nil {
693710
return fmt.Errorf("newIssueLabel: %v", err)
694711
}
695712
}

models/repo_transfer.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,33 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) (err e
325325
}
326326
}
327327

328+
// Delete labels that belong to the old organization and comments that added these labels
329+
if oldOwner.IsOrganization() {
330+
if _, err := sess.Exec(`DELETE FROM issue_label WHERE issue_label.id IN (
331+
SELECT il_too.id FROM (
332+
SELECT il_too_too.id
333+
FROM issue_label AS il_too_too
334+
INNER JOIN label ON il_too_too.id = label.id
335+
INNER JOIN issue on issue.id = il_too_too.issue_id
336+
WHERE
337+
issue.repo_id = ? AND (issue.repo_id != label.repo_id OR (label.repo_id = 0 AND label.org_id != ?))
338+
) AS il_too )`, repo.ID, newOwner.ID); err != nil {
339+
return fmt.Errorf("Unable to remove old org labels: %v", err)
340+
}
341+
342+
if _, err := sess.Exec(`DELETE FROM comment WHERE comment.id IN (
343+
SELECT il_too.id FROM (
344+
SELECT com.id
345+
FROM comment AS com
346+
INNER JOIN label ON com.label_id = label.id
347+
INNER JOIN issue on issue.id = com.issue_id
348+
WHERE
349+
com.type = ? AND issue.repo_id = ? AND (issue.repo_id != label.repo_id OR (label.repo_id = 0 AND label.org_id != ?))
350+
) AS il_too)`, CommentTypeLabel, repo.ID, newOwner.ID); err != nil {
351+
return fmt.Errorf("Unable to remove old org label comments: %v", err)
352+
}
353+
}
354+
328355
// Rename remote repository to new path and delete local copy.
329356
dir := UserPath(newOwner.Name)
330357

0 commit comments

Comments
 (0)