Merge pull request '[gitea] week 2024-39 cherry pick (gitea/main -> forgejo)' (#5372) from earl-warren/wcp/2024-39 into forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/5372
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
This commit is contained in:
Earl Warren 2024-09-27 08:12:43 +00:00
commit 89d9307d56
27 changed files with 229 additions and 244 deletions

View file

@ -37,6 +37,7 @@ func TestPackageComposer(t *testing.T) {
packageType := "composer-plugin"
packageAuthor := "Gitea Authors"
packageLicense := "MIT"
packageBin := "./bin/script"
var buf bytes.Buffer
archive := zip.NewWriter(&buf)
@ -50,6 +51,9 @@ func TestPackageComposer(t *testing.T) {
{
"name": "` + packageAuthor + `"
}
],
"bin": [
"` + packageBin + `"
]
}`))
archive.Close()
@ -211,6 +215,8 @@ func TestPackageComposer(t *testing.T) {
assert.Len(t, pkgs[0].Authors, 1)
assert.Equal(t, packageAuthor, pkgs[0].Authors[0].Name)
assert.Equal(t, "zip", pkgs[0].Dist.Type)
assert.Equal(t, "7b40bfd6da811b2b78deec1e944f156dbb2c747b", pkgs[0].Dist.Checksum)
assert.Equal(t, "4f5fa464c3cb808a1df191dbf6cb75363f8b7072", pkgs[0].Dist.Checksum)
assert.Len(t, pkgs[0].Bin, 1)
assert.Equal(t, packageBin, pkgs[0].Bin[0])
})
}

View file

@ -23,10 +23,10 @@ func TestAPICreateAndDeleteToken(t *testing.T) {
defer tests.PrepareTestEnv(t)()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
newAccessToken := createAPIAccessTokenWithoutCleanUp(t, "test-key-1", user, nil)
newAccessToken := createAPIAccessTokenWithoutCleanUp(t, "test-key-1", user, []auth_model.AccessTokenScope{auth_model.AccessTokenScopeAll})
deleteAPIAccessToken(t, newAccessToken, user)
newAccessToken = createAPIAccessTokenWithoutCleanUp(t, "test-key-2", user, nil)
newAccessToken = createAPIAccessTokenWithoutCleanUp(t, "test-key-2", user, []auth_model.AccessTokenScope{auth_model.AccessTokenScopeAll})
deleteAPIAccessToken(t, newAccessToken, user)
}
@ -72,19 +72,19 @@ func TestAPIDeleteTokensPermission(t *testing.T) {
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
// admin can delete tokens for other users
createAPIAccessTokenWithoutCleanUp(t, "test-key-1", user2, nil)
createAPIAccessTokenWithoutCleanUp(t, "test-key-1", user2, []auth_model.AccessTokenScope{auth_model.AccessTokenScopeAll})
req := NewRequest(t, "DELETE", "/api/v1/users/"+user2.LoginName+"/tokens/test-key-1").
AddBasicAuth(admin.Name)
MakeRequest(t, req, http.StatusNoContent)
// non-admin can delete tokens for himself
createAPIAccessTokenWithoutCleanUp(t, "test-key-2", user2, nil)
createAPIAccessTokenWithoutCleanUp(t, "test-key-2", user2, []auth_model.AccessTokenScope{auth_model.AccessTokenScopeAll})
req = NewRequest(t, "DELETE", "/api/v1/users/"+user2.LoginName+"/tokens/test-key-2").
AddBasicAuth(user2.Name)
MakeRequest(t, req, http.StatusNoContent)
// non-admin can't delete tokens for other users
createAPIAccessTokenWithoutCleanUp(t, "test-key-3", user2, nil)
createAPIAccessTokenWithoutCleanUp(t, "test-key-3", user2, []auth_model.AccessTokenScope{auth_model.AccessTokenScopeAll})
req = NewRequest(t, "DELETE", "/api/v1/users/"+user2.LoginName+"/tokens/test-key-3").
AddBasicAuth(user4.Name)
MakeRequest(t, req, http.StatusForbidden)
@ -520,7 +520,7 @@ func runTestCase(t *testing.T, testCase *requiredScopeTestCase, user *user_model
unauthorizedScopes = append(unauthorizedScopes, cateogoryUnauthorizedScopes...)
}
accessToken := createAPIAccessTokenWithoutCleanUp(t, "test-token", user, &unauthorizedScopes)
accessToken := createAPIAccessTokenWithoutCleanUp(t, "test-token", user, unauthorizedScopes)
defer deleteAPIAccessToken(t, accessToken, user)
// Request the endpoint. Verify that permission is denied.
@ -532,20 +532,12 @@ func runTestCase(t *testing.T, testCase *requiredScopeTestCase, user *user_model
// createAPIAccessTokenWithoutCleanUp Create an API access token and assert that
// creation succeeded. The caller is responsible for deleting the token.
func createAPIAccessTokenWithoutCleanUp(t *testing.T, tokenName string, user *user_model.User, scopes *[]auth_model.AccessTokenScope) api.AccessToken {
func createAPIAccessTokenWithoutCleanUp(t *testing.T, tokenName string, user *user_model.User, scopes []auth_model.AccessTokenScope) api.AccessToken {
payload := map[string]any{
"name": tokenName,
}
if scopes != nil {
for _, scope := range *scopes {
scopes, scopesExists := payload["scopes"].([]string)
if !scopesExists {
scopes = make([]string, 0)
}
scopes = append(scopes, string(scope))
payload["scopes"] = scopes
}
"name": tokenName,
"scopes": scopes,
}
log.Debug("Requesting creation of token with scopes: %v", scopes)
req := NewRequestWithJSON(t, "POST", "/api/v1/users/"+user.LoginName+"/tokens", payload).
AddBasicAuth(user.Name)
@ -563,8 +555,7 @@ func createAPIAccessTokenWithoutCleanUp(t *testing.T, tokenName string, user *us
return newAccessToken
}
// createAPIAccessTokenWithoutCleanUp Delete an API access token and assert that
// deletion succeeded.
// deleteAPIAccessToken deletes an API access token and assert that deletion succeeded.
func deleteAPIAccessToken(t *testing.T, accessToken api.AccessToken, user *user_model.User) {
req := NewRequestf(t, "DELETE", "/api/v1/users/"+user.LoginName+"/tokens/%d", accessToken.ID).
AddBasicAuth(user.Name)

View file

@ -60,7 +60,8 @@ func createAttachment(t *testing.T, session *TestSession, repoURL, filename stri
func TestCreateAnonymousAttachment(t *testing.T) {
defer tests.PrepareTestEnv(t)()
session := emptyTestSession(t)
createAttachment(t, session, "user2/repo1", "image.png", generateImg(), http.StatusSeeOther)
// this test is not right because it just doesn't pass the CSRF validation
createAttachment(t, session, "user2/repo1", "image.png", generateImg(), http.StatusBadRequest)
}
func TestCreateIssueAttachment(t *testing.T) {

View file

@ -5,12 +5,10 @@ package integration
import (
"net/http"
"strings"
"testing"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
@ -25,28 +23,12 @@ func TestCsrfProtection(t *testing.T) {
req := NewRequestWithValues(t, "POST", "/user/settings", map[string]string{
"_csrf": "fake_csrf",
})
session.MakeRequest(t, req, http.StatusSeeOther)
resp := session.MakeRequest(t, req, http.StatusSeeOther)
loc := resp.Header().Get("Location")
assert.Equal(t, setting.AppSubURL+"/", loc)
resp = session.MakeRequest(t, NewRequest(t, "GET", loc), http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
assert.Equal(t, "Bad Request: invalid CSRF token",
strings.TrimSpace(htmlDoc.doc.Find(".ui.message").Text()),
)
resp := session.MakeRequest(t, req, http.StatusBadRequest)
assert.Contains(t, resp.Body.String(), "Invalid CSRF token")
// test web form csrf via header. TODO: should use an UI api to test
req = NewRequest(t, "POST", "/user/settings")
req.Header.Add("X-Csrf-Token", "fake_csrf")
session.MakeRequest(t, req, http.StatusSeeOther)
resp = session.MakeRequest(t, req, http.StatusSeeOther)
loc = resp.Header().Get("Location")
assert.Equal(t, setting.AppSubURL+"/", loc)
resp = session.MakeRequest(t, NewRequest(t, "GET", loc), http.StatusOK)
htmlDoc = NewHTMLParser(t, resp.Body)
assert.Equal(t, "Bad Request: invalid CSRF token",
strings.TrimSpace(htmlDoc.doc.Find(".ui.message").Text()),
)
resp = session.MakeRequest(t, req, http.StatusBadRequest)
assert.Contains(t, resp.Body.String(), "Invalid CSRF token")
}

View file

@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/test"
forgejo_context "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
@ -190,11 +191,6 @@ func TestRedirectsWebhooks(t *testing.T) {
{from: "/user/settings/hooks/" + kind + "/new", to: "/user/login", verb: "GET"},
{from: "/admin/system-hooks/" + kind + "/new", to: "/user/login", verb: "GET"},
{from: "/admin/default-hooks/" + kind + "/new", to: "/user/login", verb: "GET"},
{from: "/user2/repo1/settings/hooks/" + kind + "/new", to: "/", verb: "POST"},
{from: "/admin/system-hooks/" + kind + "/new", to: "/", verb: "POST"},
{from: "/admin/default-hooks/" + kind + "/new", to: "/", verb: "POST"},
{from: "/user2/repo1/settings/hooks/1", to: "/", verb: "POST"},
{from: "/admin/hooks/1", to: "/", verb: "POST"},
}
for _, info := range redirects {
req := NewRequest(t, info.verb, info.from)
@ -202,6 +198,24 @@ func TestRedirectsWebhooks(t *testing.T) {
assert.EqualValues(t, path.Join(setting.AppSubURL, info.to), test.RedirectURL(resp), info.from)
}
}
for _, kind := range []string{"forgejo", "gitea"} {
csrf := []struct {
from string
verb string
}{
{from: "/user2/repo1/settings/hooks/" + kind + "/new", verb: "POST"},
{from: "/admin/hooks/1", verb: "POST"},
{from: "/admin/system-hooks/" + kind + "/new", verb: "POST"},
{from: "/admin/default-hooks/" + kind + "/new", verb: "POST"},
{from: "/user2/repo1/settings/hooks/1", verb: "POST"},
}
for _, info := range csrf {
req := NewRequest(t, info.verb, info.from)
resp := MakeRequest(t, req, http.StatusBadRequest)
assert.Contains(t, resp.Body.String(), forgejo_context.CsrfErrorString)
}
}
}
func TestRepoLinks(t *testing.T) {

View file

@ -11,6 +11,7 @@ import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
@ -24,6 +25,7 @@ import (
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/routers/web/auth"
forgejo_context "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/tests"
"github.com/markbates/goth"
@ -803,6 +805,16 @@ func TestOAuthIntrospection(t *testing.T) {
})
}
func requireCookieCSRF(t *testing.T, resp http.ResponseWriter) string {
for _, c := range resp.(*httptest.ResponseRecorder).Result().Cookies() {
if c.Name == "_csrf" {
return c.Value
}
}
require.True(t, false, "_csrf not found in cookies")
return ""
}
func TestOAuth_GrantScopesReadUser(t *testing.T) {
defer tests.PrepareTestEnv(t)()
@ -840,19 +852,18 @@ func TestOAuth_GrantScopesReadUser(t *testing.T) {
authorizeResp := ctx.MakeRequest(t, authorizeReq, http.StatusSeeOther)
authcode := strings.Split(strings.Split(authorizeResp.Body.String(), "?code=")[1], "&amp")[0]
htmlDoc := NewHTMLParser(t, authorizeResp.Body)
grantReq := NewRequestWithValues(t, "POST", "/login/oauth/grant", map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"_csrf": requireCookieCSRF(t, authorizeResp),
"client_id": app.ClientID,
"redirect_uri": "a",
"state": "thestate",
"granted": "true",
})
grantResp := ctx.MakeRequest(t, grantReq, http.StatusSeeOther)
htmlDocGrant := NewHTMLParser(t, grantResp.Body)
grantResp := ctx.MakeRequest(t, grantReq, http.StatusBadRequest)
assert.NotContains(t, grantResp.Body.String(), forgejo_context.CsrfErrorString)
accessTokenReq := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
"_csrf": htmlDocGrant.GetCSRF(),
"_csrf": requireCookieCSRF(t, authorizeResp),
"grant_type": "authorization_code",
"client_id": app.ClientID,
"client_secret": app.ClientSecret,
@ -921,19 +932,18 @@ func TestOAuth_GrantScopesFailReadRepository(t *testing.T) {
authorizeResp := ctx.MakeRequest(t, authorizeReq, http.StatusSeeOther)
authcode := strings.Split(strings.Split(authorizeResp.Body.String(), "?code=")[1], "&amp")[0]
htmlDoc := NewHTMLParser(t, authorizeResp.Body)
grantReq := NewRequestWithValues(t, "POST", "/login/oauth/grant", map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"_csrf": requireCookieCSRF(t, authorizeResp),
"client_id": app.ClientID,
"redirect_uri": "a",
"state": "thestate",
"granted": "true",
})
grantResp := ctx.MakeRequest(t, grantReq, http.StatusSeeOther)
htmlDocGrant := NewHTMLParser(t, grantResp.Body)
grantResp := ctx.MakeRequest(t, grantReq, http.StatusBadRequest)
assert.NotContains(t, grantResp.Body.String(), forgejo_context.CsrfErrorString)
accessTokenReq := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
"_csrf": htmlDocGrant.GetCSRF(),
"_csrf": requireCookieCSRF(t, authorizeResp),
"grant_type": "authorization_code",
"client_id": app.ClientID,
"client_secret": app.ClientSecret,
@ -1000,19 +1010,18 @@ func TestOAuth_GrantScopesReadRepository(t *testing.T) {
authorizeResp := ctx.MakeRequest(t, authorizeReq, http.StatusSeeOther)
authcode := strings.Split(strings.Split(authorizeResp.Body.String(), "?code=")[1], "&amp")[0]
htmlDoc := NewHTMLParser(t, authorizeResp.Body)
grantReq := NewRequestWithValues(t, "POST", "/login/oauth/grant", map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"_csrf": requireCookieCSRF(t, authorizeResp),
"client_id": app.ClientID,
"redirect_uri": "a",
"state": "thestate",
"granted": "true",
})
grantResp := ctx.MakeRequest(t, grantReq, http.StatusSeeOther)
htmlDocGrant := NewHTMLParser(t, grantResp.Body)
grantResp := ctx.MakeRequest(t, grantReq, http.StatusBadRequest)
assert.NotContains(t, grantResp.Body.String(), forgejo_context.CsrfErrorString)
accessTokenReq := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
"_csrf": htmlDocGrant.GetCSRF(),
"_csrf": requireCookieCSRF(t, authorizeResp),
"grant_type": "authorization_code",
"client_id": app.ClientID,
"client_secret": app.ClientSecret,
@ -1082,19 +1091,18 @@ func TestOAuth_GrantScopesReadPrivateGroups(t *testing.T) {
authorizeResp := ctx.MakeRequest(t, authorizeReq, http.StatusSeeOther)
authcode := strings.Split(strings.Split(authorizeResp.Body.String(), "?code=")[1], "&amp")[0]
htmlDoc := NewHTMLParser(t, authorizeResp.Body)
grantReq := NewRequestWithValues(t, "POST", "/login/oauth/grant", map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"_csrf": requireCookieCSRF(t, authorizeResp),
"client_id": app.ClientID,
"redirect_uri": "a",
"state": "thestate",
"granted": "true",
})
grantResp := ctx.MakeRequest(t, grantReq, http.StatusSeeOther)
htmlDocGrant := NewHTMLParser(t, grantResp.Body)
grantResp := ctx.MakeRequest(t, grantReq, http.StatusBadRequest)
assert.NotContains(t, grantResp.Body.String(), forgejo_context.CsrfErrorString)
accessTokenReq := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
"_csrf": htmlDocGrant.GetCSRF(),
"_csrf": requireCookieCSRF(t, authorizeResp),
"grant_type": "authorization_code",
"client_id": app.ClientID,
"client_secret": app.ClientSecret,
@ -1164,19 +1172,18 @@ func TestOAuth_GrantScopesReadOnlyPublicGroups(t *testing.T) {
authorizeResp := ctx.MakeRequest(t, authorizeReq, http.StatusSeeOther)
authcode := strings.Split(strings.Split(authorizeResp.Body.String(), "?code=")[1], "&amp")[0]
htmlDoc := NewHTMLParser(t, authorizeResp.Body)
grantReq := NewRequestWithValues(t, "POST", "/login/oauth/grant", map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"_csrf": requireCookieCSRF(t, authorizeResp),
"client_id": app.ClientID,
"redirect_uri": "a",
"state": "thestate",
"granted": "true",
})
grantResp := ctx.MakeRequest(t, grantReq, http.StatusSeeOther)
htmlDocGrant := NewHTMLParser(t, grantResp.Body)
grantResp := ctx.MakeRequest(t, grantReq, http.StatusBadRequest)
assert.NotContains(t, grantResp.Body.String(), forgejo_context.CsrfErrorString)
accessTokenReq := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
"_csrf": htmlDocGrant.GetCSRF(),
"_csrf": requireCookieCSRF(t, authorizeResp),
"grant_type": "authorization_code",
"client_id": app.ClientID,
"client_secret": app.ClientSecret,
@ -1260,19 +1267,18 @@ func TestOAuth_GrantScopesReadPublicGroupsWithTheReadScope(t *testing.T) {
authorizeResp := ctx.MakeRequest(t, authorizeReq, http.StatusSeeOther)
authcode := strings.Split(strings.Split(authorizeResp.Body.String(), "?code=")[1], "&amp")[0]
htmlDoc := NewHTMLParser(t, authorizeResp.Body)
grantReq := NewRequestWithValues(t, "POST", "/login/oauth/grant", map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"_csrf": requireCookieCSRF(t, authorizeResp),
"client_id": app.ClientID,
"redirect_uri": "a",
"state": "thestate",
"granted": "true",
})
grantResp := ctx.MakeRequest(t, grantReq, http.StatusSeeOther)
htmlDocGrant := NewHTMLParser(t, grantResp.Body)
grantResp := ctx.MakeRequest(t, grantReq, http.StatusBadRequest)
assert.NotContains(t, grantResp.Body.String(), forgejo_context.CsrfErrorString)
accessTokenReq := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
"_csrf": htmlDocGrant.GetCSRF(),
"_csrf": requireCookieCSRF(t, authorizeResp),
"grant_type": "authorization_code",
"client_id": app.ClientID,
"client_secret": app.ClientSecret,

View file

@ -18,7 +18,6 @@ import (
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/translation"
repo_service "code.gitea.io/gitea/services/repository"
@ -157,15 +156,8 @@ func TestCreateBranchInvalidCSRF(t *testing.T) {
"_csrf": "fake_csrf",
"new_branch_name": "test",
})
resp := session.MakeRequest(t, req, http.StatusSeeOther)
loc := resp.Header().Get("Location")
assert.Equal(t, setting.AppSubURL+"/", loc)
resp = session.MakeRequest(t, NewRequest(t, "GET", loc), http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
assert.Equal(t,
"Bad Request: invalid CSRF token",
strings.TrimSpace(htmlDoc.doc.Find(".ui.message").Text()),
)
resp := session.MakeRequest(t, req, http.StatusBadRequest)
assert.Contains(t, resp.Body.String(), "Invalid CSRF token")
}
func TestDatabaseMissingABranch(t *testing.T) {