mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2026-02-11 10:43:09 +02:00
Replace the anti-CSRF token with a [cross origin protection by Go](https://go.dev/doc/go1.25#nethttppkgnethttp) that uses a stateless way of verifying if a request was cross origin or not. This allows is to remove al lot of code and replace it with a few lines of code and we no longer have to hand roll this protection. The new protection uses indicators by the browser itself that indicate if the request is cross-origin, thus we no longer have to take care of ensuring the generated CSRF token is passed back to the server any request by the the browser will have send this indicator. Resolves forgejo/forgejo#3538 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9830 Reviewed-by: oliverpool <oliverpool@noreply.codeberg.org> Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org> Co-authored-by: Gusted <postmaster@gusted.xyz> Co-committed-by: Gusted <postmaster@gusted.xyz>
218 lines
7.5 KiB
Go
218 lines
7.5 KiB
Go
// Copyright 2024 The Forgejo Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package integration
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"forgejo.org/models/db"
|
|
issues_model "forgejo.org/models/issues"
|
|
repo_model "forgejo.org/models/repo"
|
|
unit_model "forgejo.org/models/unit"
|
|
"forgejo.org/models/unittest"
|
|
user_model "forgejo.org/models/user"
|
|
"forgejo.org/modules/git"
|
|
files_service "forgejo.org/services/repository/files"
|
|
"forgejo.org/tests"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func CodeOwnerTestCommon(t *testing.T, u *url.URL, codeownerTest CodeownerTest) {
|
|
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
|
|
|
// Create the repo.
|
|
repo, _, f := tests.CreateDeclarativeRepo(t, user2, codeownerTest.Name,
|
|
[]unit_model.Type{unit_model.TypePullRequests}, nil,
|
|
[]*files_service.ChangeRepoFile{
|
|
{
|
|
Operation: "create",
|
|
TreePath: codeownerTest.Path,
|
|
ContentReader: strings.NewReader("README.md @user5\ntest-file @user4"),
|
|
},
|
|
},
|
|
)
|
|
defer f()
|
|
|
|
dstPath := t.TempDir()
|
|
r := fmt.Sprintf("%suser2/%s.git", u.String(), repo.Name)
|
|
cloneURL, _ := url.Parse(r)
|
|
cloneURL.User = url.UserPassword("user2", userPassword)
|
|
require.NoError(t, git.CloneWithArgs(t.Context(), nil, cloneURL.String(), dstPath, git.CloneRepoOptions{}))
|
|
doGitSetRemoteURL(dstPath, "origin", cloneURL)(t)
|
|
|
|
t.Run("Normal", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
err := os.WriteFile(path.Join(dstPath, "README.md"), []byte("## test content"), 0o666)
|
|
require.NoError(t, err)
|
|
|
|
err = git.AddChanges(dstPath, true)
|
|
require.NoError(t, err)
|
|
|
|
err = git.CommitChanges(dstPath, git.CommitChangesOptions{
|
|
Committer: &git.Signature{
|
|
Email: "user2@example.com",
|
|
Name: "user2",
|
|
When: time.Now(),
|
|
},
|
|
Author: &git.Signature{
|
|
Email: "user2@example.com",
|
|
Name: "user2",
|
|
When: time.Now(),
|
|
},
|
|
Message: "Add README.",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/main", "-o", "topic=codeowner-normal").Run(&git.RunOpts{Dir: dstPath})
|
|
require.NoError(t, err)
|
|
|
|
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-normal"})
|
|
unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5})
|
|
})
|
|
|
|
t.Run("Forked repository", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
session := loginUser(t, "user1")
|
|
testRepoFork(t, session, user2.Name, repo.Name, "user1", codeownerTest.Name)
|
|
|
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: codeownerTest.Name})
|
|
|
|
r := fmt.Sprintf("%suser1/%s.git", u.String(), codeownerTest.Name)
|
|
remoteURL, _ := url.Parse(r)
|
|
remoteURL.User = url.UserPassword("user2", userPassword)
|
|
doGitAddRemote(dstPath, "forked", remoteURL)(t)
|
|
|
|
err := git.NewCommand(git.DefaultContext, "push", "forked", "HEAD:refs/for/main", "-o", "topic=codeowner-forked").Run(&git.RunOpts{Dir: dstPath})
|
|
require.NoError(t, err)
|
|
|
|
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-forked"})
|
|
unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5})
|
|
})
|
|
|
|
t.Run("Out of date", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
// Push the changes made from the previous subtest.
|
|
require.NoError(t, git.NewCommand(git.DefaultContext, "push", "origin").Run(&git.RunOpts{Dir: dstPath}))
|
|
|
|
// Reset the tree to the previous commit.
|
|
require.NoError(t, git.NewCommand(git.DefaultContext, "reset", "--hard", "HEAD~1").Run(&git.RunOpts{Dir: dstPath}))
|
|
|
|
err := os.WriteFile(path.Join(dstPath, "test-file"), []byte("## test content"), 0o666)
|
|
require.NoError(t, err)
|
|
|
|
err = git.AddChanges(dstPath, true)
|
|
require.NoError(t, err)
|
|
|
|
err = git.CommitChanges(dstPath, git.CommitChangesOptions{
|
|
Committer: &git.Signature{
|
|
Email: "user2@example.com",
|
|
Name: "user2",
|
|
When: time.Now(),
|
|
},
|
|
Author: &git.Signature{
|
|
Email: "user2@example.com",
|
|
Name: "user2",
|
|
When: time.Now(),
|
|
},
|
|
Message: "Add test-file.",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/main", "-o", "topic=codeowner-out-of-date").Run(&git.RunOpts{Dir: dstPath})
|
|
require.NoError(t, err)
|
|
|
|
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-out-of-date"})
|
|
unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 4})
|
|
unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5})
|
|
})
|
|
t.Run("From a forked repository", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
session := loginUser(t, "user1")
|
|
|
|
r := fmt.Sprintf("%suser1/%s.git", u.String(), codeownerTest.Name)
|
|
remoteURL, _ := url.Parse(r)
|
|
remoteURL.User = url.UserPassword("user1", userPassword)
|
|
doGitAddRemote(dstPath, "forked-2", remoteURL)(t)
|
|
|
|
err := git.NewCommand(git.DefaultContext, "push", "forked-2", "HEAD:branch").Run(&git.RunOpts{Dir: dstPath})
|
|
require.NoError(t, err)
|
|
|
|
req := NewRequestWithValues(t, "POST", repo.FullName()+"/compare/main...user1/"+codeownerTest.Name+":branch", map[string]string{
|
|
"title": "pull request",
|
|
})
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "branch"})
|
|
unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 4})
|
|
})
|
|
|
|
t.Run("Codeowner user with no permission", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
// Make repository private, only user2 (owner of repository) has now access to this repository.
|
|
repo.IsPrivate = true
|
|
_, err := db.GetEngine(db.DefaultContext).Cols("is_private").Update(repo)
|
|
require.NoError(t, err)
|
|
|
|
err = os.WriteFile(path.Join(dstPath, "README.md"), []byte("## very sensitive info"), 0o666)
|
|
require.NoError(t, err)
|
|
|
|
err = git.AddChanges(dstPath, true)
|
|
require.NoError(t, err)
|
|
|
|
err = git.CommitChanges(dstPath, git.CommitChangesOptions{
|
|
Committer: &git.Signature{
|
|
Email: "user2@example.com",
|
|
Name: "user2",
|
|
When: time.Now(),
|
|
},
|
|
Author: &git.Signature{
|
|
Email: "user2@example.com",
|
|
Name: "user2",
|
|
When: time.Now(),
|
|
},
|
|
Message: "Add secrets to the README.",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/main", "-o", "topic=codeowner-private").Run(&git.RunOpts{Dir: dstPath})
|
|
require.NoError(t, err)
|
|
|
|
// In CODEOWNERS file the codeowner for README.md is user5, but does not have access to this private repository.
|
|
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-private"})
|
|
unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5})
|
|
})
|
|
}
|
|
|
|
type CodeownerTest struct {
|
|
Name string
|
|
Path string
|
|
}
|
|
|
|
func TestCodeOwner(t *testing.T) {
|
|
onApplicationRun(t, func(t *testing.T, u *url.URL) {
|
|
tests := []CodeownerTest{
|
|
{Name: "root", Path: "CODEOWNERS"},
|
|
{Name: "docs", Path: "docs/CODEOWNERS"},
|
|
{Name: "gitea", Path: ".gitea/CODEOWNERS"},
|
|
{Name: "forgejo", Path: ".forgejo/CODEOWNERS"},
|
|
}
|
|
for _, test := range tests {
|
|
CodeOwnerTestCommon(t, u, test)
|
|
}
|
|
})
|
|
}
|