mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2026-01-11 20:56:29 +00:00
merge commit: [v11.0/forgejo] January 8th security patches (#10722)
Some checks failed
testing / test-unit (push) Has been cancelled
testing / test-e2e (push) Has been cancelled
testing / test-remote-cacher (redis) (push) Has been cancelled
testing / test-remote-cacher (valkey) (push) Has been cancelled
testing / test-remote-cacher (garnet) (push) Has been cancelled
testing / test-remote-cacher (redict) (push) Has been cancelled
testing / test-pgsql (push) Has been cancelled
testing / test-sqlite (push) Has been cancelled
testing / security-check (push) Has been cancelled
/ release (push) Has been cancelled
testing / backend-checks (push) Has been cancelled
testing / frontend-checks (push) Has been cancelled
testing / test-mysql (push) Has been cancelled
Some checks failed
testing / test-unit (push) Has been cancelled
testing / test-e2e (push) Has been cancelled
testing / test-remote-cacher (redis) (push) Has been cancelled
testing / test-remote-cacher (valkey) (push) Has been cancelled
testing / test-remote-cacher (garnet) (push) Has been cancelled
testing / test-remote-cacher (redict) (push) Has been cancelled
testing / test-pgsql (push) Has been cancelled
testing / test-sqlite (push) Has been cancelled
testing / security-check (push) Has been cancelled
/ release (push) Has been cancelled
testing / backend-checks (push) Has been cancelled
testing / frontend-checks (push) Has been cancelled
testing / test-mysql (push) Has been cancelled
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10722 Reviewed-by: 0ko <0ko@noreply.codeberg.org>
This commit is contained in:
commit
52406dc6ea
12 changed files with 438 additions and 10 deletions
|
|
@ -231,7 +231,7 @@ Forgejo or set your environment appropriately.`, "")
|
|||
continue
|
||||
}
|
||||
|
||||
fields := bytes.Fields(scanner.Bytes())
|
||||
fields := bytes.Split(scanner.Bytes(), []byte(" "))
|
||||
if len(fields) != 3 {
|
||||
continue
|
||||
}
|
||||
|
|
@ -397,7 +397,7 @@ Forgejo or set your environment appropriately.`, "")
|
|||
continue
|
||||
}
|
||||
|
||||
fields := bytes.Fields(scanner.Bytes())
|
||||
fields := bytes.Split(scanner.Bytes(), []byte(" "))
|
||||
if len(fields) != 3 {
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
133
cmd/hook_test.go
133
cmd/hook_test.go
|
|
@ -14,6 +14,9 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"forgejo.org/modules/git"
|
||||
"forgejo.org/modules/json"
|
||||
"forgejo.org/modules/private"
|
||||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/modules/test"
|
||||
|
||||
|
|
@ -162,6 +165,136 @@ func TestDelayWriter(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestRunHookPrePostReceive(t *testing.T) {
|
||||
// Setup the environment.
|
||||
defer test.MockVariableValue(&setting.InternalToken, "Random")()
|
||||
defer test.MockVariableValue(&setting.InstallLock, true)()
|
||||
defer test.MockVariableValue(&setting.Git.VerbosePush, true)()
|
||||
t.Setenv("SSH_ORIGINAL_COMMAND", "true")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
inputLine string
|
||||
oldCommitID string
|
||||
newCommitID string
|
||||
refFullName string
|
||||
}{
|
||||
{
|
||||
name: "base case",
|
||||
inputLine: "00000000000000000000 00000000000000000001 refs/head/main\n",
|
||||
oldCommitID: "00000000000000000000",
|
||||
newCommitID: "00000000000000000001",
|
||||
refFullName: "refs/head/main",
|
||||
},
|
||||
{
|
||||
name: "nbsp case",
|
||||
inputLine: "00000000000000000000 00000000000000000001 refs/head/ma\u00A0in\n",
|
||||
oldCommitID: "00000000000000000000",
|
||||
newCommitID: "00000000000000000001",
|
||||
refFullName: "refs/head/ma\u00A0in",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Setup the Stdin.
|
||||
f, err := os.OpenFile(t.TempDir()+"/stdin", os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o666)
|
||||
require.NoError(t, err)
|
||||
_, err = f.Write([]byte(tt.inputLine))
|
||||
require.NoError(t, err)
|
||||
_, err = f.Seek(0, 0)
|
||||
require.NoError(t, err)
|
||||
defer test.MockVariableValue(os.Stdin, *f)()
|
||||
|
||||
// Setup the server that processes the hooks.
|
||||
var serverError error
|
||||
var hookOpts *private.HookOptions
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
serverError = err
|
||||
w.WriteHeader(500)
|
||||
return
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &hookOpts)
|
||||
if err != nil {
|
||||
serverError = err
|
||||
w.WriteHeader(500)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(200)
|
||||
|
||||
resp := &private.HookPostReceiveResult{}
|
||||
bytes, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
serverError = err
|
||||
return
|
||||
}
|
||||
|
||||
_, err = w.Write(bytes)
|
||||
if err != nil {
|
||||
serverError = err
|
||||
return
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
defer test.MockVariableValue(&setting.LocalURL, ts.URL+"/")()
|
||||
|
||||
t.Run("pre-receive", func(t *testing.T) {
|
||||
app := cli.NewApp()
|
||||
app.Commands = []*cli.Command{subcmdHookPreReceive}
|
||||
|
||||
finish := captureOutput(t, os.Stdout)
|
||||
err = app.Run([]string{"./forgejo", "pre-receive"})
|
||||
require.NoError(t, err)
|
||||
out := finish()
|
||||
require.Empty(t, out)
|
||||
|
||||
require.NoError(t, serverError)
|
||||
require.NotNil(t, hookOpts)
|
||||
|
||||
require.Len(t, hookOpts.OldCommitIDs, 1)
|
||||
assert.Equal(t, tt.oldCommitID, hookOpts.OldCommitIDs[0])
|
||||
require.Len(t, hookOpts.NewCommitIDs, 1)
|
||||
assert.Equal(t, tt.newCommitID, hookOpts.NewCommitIDs[0])
|
||||
require.Len(t, hookOpts.RefFullNames, 1)
|
||||
assert.Equal(t, git.RefName(tt.refFullName), hookOpts.RefFullNames[0])
|
||||
})
|
||||
|
||||
// seek stdin back to beginning
|
||||
_, err = f.Seek(0, 0)
|
||||
require.NoError(t, err)
|
||||
// reset state from prev test
|
||||
serverError = nil
|
||||
hookOpts = nil
|
||||
|
||||
t.Run("post-receive", func(t *testing.T) {
|
||||
app := cli.NewApp()
|
||||
app.Commands = []*cli.Command{subcmdHookPostReceive}
|
||||
|
||||
finish := captureOutput(t, os.Stdout)
|
||||
err = app.Run([]string{"./forgejo", "post-receive"})
|
||||
require.NoError(t, err)
|
||||
out := finish()
|
||||
require.Empty(t, out)
|
||||
|
||||
require.NoError(t, serverError)
|
||||
require.NotNil(t, hookOpts)
|
||||
|
||||
require.Len(t, hookOpts.OldCommitIDs, 1)
|
||||
assert.Equal(t, tt.oldCommitID, hookOpts.OldCommitIDs[0])
|
||||
require.Len(t, hookOpts.NewCommitIDs, 1)
|
||||
assert.Equal(t, tt.newCommitID, hookOpts.NewCommitIDs[0])
|
||||
require.Len(t, hookOpts.RefFullNames, 1)
|
||||
assert.Equal(t, git.RefName(tt.refFullName), hookOpts.RefFullNames[0])
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunHookUpdate(t *testing.T) {
|
||||
app := cli.NewApp()
|
||||
app.Commands = []*cli.Command{subcmdHookUpdate}
|
||||
|
|
|
|||
|
|
@ -110,7 +110,13 @@ func GPGKeyToEntity(ctx context.Context, k *GPGKey) (*openpgp.Entity, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return keys[0], err
|
||||
|
||||
for _, key := range keys {
|
||||
if key.PrimaryKey.KeyIdString() == k.KeyID {
|
||||
return key, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("key with %s id not found", k.KeyID)
|
||||
}
|
||||
|
||||
// parseSubGPGKey parse a sub Key
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
email: user2@example.com
|
||||
keep_email_private: true
|
||||
keep_pronouns_private: true
|
||||
pronouns: he/him
|
||||
email_notifications_preference: enabled
|
||||
passwd: ZogKvWdyEx:password
|
||||
passwd_hash_algo: dummy
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"forgejo.org/modules/json"
|
||||
)
|
||||
|
|
@ -46,3 +47,9 @@ func MockProtect[T any](p *T) (reset func()) {
|
|||
old := *p
|
||||
return func() { *p = old }
|
||||
}
|
||||
|
||||
// When this is called, sleep until the truncated unix time to a minute was
|
||||
// increased by one.
|
||||
func SleepTillNextMinute() {
|
||||
time.Sleep(time.Minute - time.Since(time.Now().Truncate(time.Minute)))
|
||||
}
|
||||
|
|
|
|||
5
release-notes/10722.md
Normal file
5
release-notes/10722.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
fix: hide user profile anonymous options on public repo APIs
|
||||
fix: incorrect whitespace handling on pre&post receive hooks
|
||||
fix: reduce memory usage while processing large attachment uploads
|
||||
fix: load reviewer for pull review dismiss action notifier
|
||||
fix: use correct GPG key for export
|
||||
|
|
@ -290,7 +290,7 @@ func APIContexter() func(http.Handler) http.Handler {
|
|||
|
||||
// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
|
||||
if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
|
||||
if err := ctx.Req.ParseMultipartForm(setting.Attachment.MaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size
|
||||
if err := ctx.Req.ParseMultipartForm(32 << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ func Contexter() func(next http.Handler) http.Handler {
|
|||
|
||||
// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
|
||||
if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
|
||||
if err := ctx.Req.ParseMultipartForm(setting.Attachment.MaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size
|
||||
if err := ctx.Req.ParseMultipartForm(32 << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size
|
||||
ctx.ServerError("ParseMultipartForm", err)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
package convert
|
||||
|
||||
import (
|
||||
"context"
|
||||
stdCtx "context"
|
||||
"time"
|
||||
|
||||
"forgejo.org/models"
|
||||
|
|
@ -15,14 +15,15 @@ import (
|
|||
unit_model "forgejo.org/models/unit"
|
||||
"forgejo.org/modules/log"
|
||||
api "forgejo.org/modules/structs"
|
||||
"forgejo.org/services/context"
|
||||
)
|
||||
|
||||
// ToRepo converts a Repository to api.Repository
|
||||
func ToRepo(ctx context.Context, repo *repo_model.Repository, permissionInRepo access_model.Permission) *api.Repository {
|
||||
func ToRepo(ctx stdCtx.Context, repo *repo_model.Repository, permissionInRepo access_model.Permission) *api.Repository {
|
||||
return innerToRepo(ctx, repo, permissionInRepo, false)
|
||||
}
|
||||
|
||||
func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInRepo access_model.Permission, isParent bool) *api.Repository {
|
||||
func innerToRepo(ctx stdCtx.Context, repo *repo_model.Repository, permissionInRepo access_model.Permission, isParent bool) *api.Repository {
|
||||
var parent *api.Repository
|
||||
|
||||
if permissionInRepo.Units == nil && permissionInRepo.UnitsMode == nil {
|
||||
|
|
@ -179,9 +180,19 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
|
|||
|
||||
repoAPIURL := repo.APIURL()
|
||||
|
||||
// Calculate the effective permission for `ToUserWithAccessMode` for the repo owner. When accessing a public repo,
|
||||
// permissionInRepo.AccessMode will be AccessModeRead even for an anonymous user -- in that case, downgrade
|
||||
// `ownerViewPerms` to `AccessModeNone`. `innerToRepo` doesn't have great access to recognize an anonymous user, so
|
||||
// the best-effort made here is to check if `ctx` is an `APIContext`.
|
||||
ownerViewPerms := permissionInRepo.AccessMode
|
||||
apiCtx, ok := ctx.(*context.APIContext)
|
||||
if ok && apiCtx.Doer == nil {
|
||||
ownerViewPerms = perm.AccessModeNone
|
||||
}
|
||||
|
||||
return &api.Repository{
|
||||
ID: repo.ID,
|
||||
Owner: ToUserWithAccessMode(ctx, repo.Owner, permissionInRepo.AccessMode),
|
||||
Owner: ToUserWithAccessMode(ctx, repo.Owner, ownerViewPerms),
|
||||
Name: repo.Name,
|
||||
FullName: repo.FullName(),
|
||||
Description: repo.Description,
|
||||
|
|
@ -246,7 +257,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
|
|||
}
|
||||
|
||||
// ToRepoTransfer convert a models.RepoTransfer to a structs.RepeTransfer
|
||||
func ToRepoTransfer(ctx context.Context, t *models.RepoTransfer) *api.RepoTransfer {
|
||||
func ToRepoTransfer(ctx stdCtx.Context, t *models.RepoTransfer) *api.RepoTransfer {
|
||||
teams, _ := ToTeams(ctx, t.Teams, false)
|
||||
|
||||
return &api.RepoTransfer{
|
||||
|
|
|
|||
|
|
@ -300,6 +300,10 @@ func (*actionNotifier) AutoMergePullRequest(ctx context.Context, doer *user_mode
|
|||
}
|
||||
|
||||
func (*actionNotifier) NotifyPullRevieweDismiss(ctx context.Context, doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) {
|
||||
if err := review.LoadReviewer(ctx); err != nil {
|
||||
log.Error("LoadReviewer '%d/%d': %v", review.ID, review.ReviewerID, err)
|
||||
return
|
||||
}
|
||||
reviewerName := review.Reviewer.Name
|
||||
if len(review.OriginalAuthor) > 0 {
|
||||
reviewerName = review.OriginalAuthor
|
||||
|
|
|
|||
|
|
@ -1,19 +1,28 @@
|
|||
// Copyright 2017 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
auth_model "forgejo.org/models/auth"
|
||||
api "forgejo.org/modules/structs"
|
||||
"forgejo.org/modules/test"
|
||||
"forgejo.org/services/context"
|
||||
"forgejo.org/tests"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/armor"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type makeRequestFunc func(testing.TB, *RequestWrapper, int) *httptest.ResponseRecorder
|
||||
|
|
@ -277,3 +286,222 @@ mIMMn8taHIaQO7v9ln2EVQYTzbNCmwTw9ovTM0j/Pbkg2EftfP1TCoxQHvBnsCED
|
|||
=MgDv
|
||||
-----END PGP PUBLIC KEY BLOCK-----`)
|
||||
}
|
||||
|
||||
func TestAPIGPGMultipleKeys(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
// Token used for verification is valid for each minute, to prevent
|
||||
// this test being flaky wait till the next minute if we have less than
|
||||
// five seconds until the next minute.
|
||||
if _, _, sec := time.Now().Clock(); sec >= 55 {
|
||||
test.SleepTillNextMinute()
|
||||
}
|
||||
|
||||
session := loginUser(t, "user2")
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser)
|
||||
|
||||
publicKeyring := `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
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
|
||||
=3agD
|
||||
-----END PGP PUBLIC KEY BLOCK-----`
|
||||
|
||||
privateKeyring := `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
|
||||
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
|
||||
=9to/
|
||||
-----END PGP PRIVATE KEY BLOCK-----
|
||||
`
|
||||
block, err := armor.Decode(strings.NewReader(privateKeyring))
|
||||
require.NoError(t, err)
|
||||
keyring, err := openpgp.ReadKeyRing(block.Body)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, keyring, 1)
|
||||
|
||||
var verificationToken string
|
||||
t.Run("No signature provided", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
req := NewRequestWithJSON(t, "POST", "/api/v1/user/gpg_keys", api.CreateGPGKeyOption{
|
||||
ArmoredKey: publicKeyring,
|
||||
}).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
var apiError context.APIError
|
||||
DecodeJSON(t, resp, &apiError)
|
||||
|
||||
var ok bool
|
||||
_, verificationToken, ok = strings.Cut(apiError.Message, ": ")
|
||||
assert.True(t, ok)
|
||||
})
|
||||
|
||||
t.Run("Signature provided", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
signatureOutput := &bytes.Buffer{}
|
||||
require.NoError(t, openpgp.ArmoredDetachSign(signatureOutput, keyring[0], strings.NewReader(verificationToken), nil))
|
||||
|
||||
req := NewRequestWithJSON(t, "POST", "/api/v1/user/gpg_keys", api.CreateGPGKeyOption{
|
||||
ArmoredKey: publicKeyring,
|
||||
Signature: signatureOutput.String(),
|
||||
}).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusCreated)
|
||||
})
|
||||
|
||||
t.Run("{user}.gpg", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
req := NewRequest(t, "GET", "/user2.gpg")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
assert.Equal(t, `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xsFNBGlUhjYBEADHuyzOjbPk3U0qC85lGTecJBKb1CgUZ0xphv5wNg1TBzLjQLRB
|
||||
DzbnhGAROTjS08dESwrG5sf8lNKRRr9rRIKQ7p+OqhK2AkOqGC25vB6vJSBhUYNy
|
||||
UWDqV02XSmNy33FlwAgalIiff12hpZ+PctmzFcBY6TJ7Zh1dIKvS17CNZvkfjb/r
|
||||
m1r8VQk8I4qdgVUg8Ky898T8B6/sAHhKcDZNXXoOLpCCUsm6STCVlp+YE0fN3H6u
|
||||
UiyJ++zjrS85fzV9Ug+rDZr+V/ZiOc5e+mqIo51L9m9VfFsmZfJDZvPBcFJgcISv
|
||||
S7LySW/lJvZ+ntKoFANzxzlLOUNYWJv+X/tYzJIoiu4J9NAuUwieFU85CYNbz9aO
|
||||
7BUlVvNDVNn2sSTX/Aum9nqLvdi17pQRNmp7NFrmSzD9GbvD1G4C51tDesnCJI/j
|
||||
m5QQpor8PeiOAcaHey/GFIS0+e1nytT3RU72P8M22u44QVp+WSAeahnsEBHVLbA7
|
||||
BUoShykXul65d9Tko2H6lf4s5ggi66mXPvHH1O0Ry57JEcJVAFHpp+teckmXf8zD
|
||||
a+/ha4U+RtJkkJ4RI6mPrFnamx57mYPT2ji0igpU2paAtIGhbDxSi5UD3ZycdmDj
|
||||
Tw/uUTxl/lon+N7DVEFC+2F8HD2Aiw6TJ92AWs3CCZRsURMphH1W7cskvQARAQAB
|
||||
zRZIYWhhIHVzZXIyQGV4YW1wbGUuY29twsGOBBMBCgA4FiEEr+MV6CFVMbWYwDdi
|
||||
we3dkWyHwqYFAmlUhjYCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQwe3d
|
||||
kWyHwqapNhAAlPbcf96Ntyk0vttBOSDS/owq1fk2I/h66NgDK0G3601JT0JhFv1P
|
||||
nY9YV5w5rzuTvCLbQWALr8UD52B8Ua6fEn5XjiLf6dU6OuZnYqIgTqi2qm4xCuNh
|
||||
p5E1UqNLBb6Nv225R1CB6XGEda+wFQkTRif2e+bQOi4/+hl6tp6tQXkqAMdJAhD5
|
||||
3v0nvvpExHkPtRyfnoP2pFLC3Oic1fVlNUD9Gz2U/rL+BkKVFTRXHfsdf1eP5osO
|
||||
HbgXe8S8xWAW+K8+FeU/5uyzx6HVIMHvvu8ZxefEvkuFyBtzeikOulp7gY0OBSpr
|
||||
ysRbPGUHXz2W+l3t21L3QKx++gq+aPW3ILkzyK0ovtXoJdU0QeJmbM7UaqfkYHcU
|
||||
ut9HaohCheLIcWlpTaqJlqMeSxx+I0NHIbRGILM2Db0DWRms0zZxYIywtlJlolSP
|
||||
hJeq3jd/Yc334CXVtY/bU8pxInJewzUtAqMhJ8AUvUfsUpmaMUg0Vkqv7NrZBhL7
|
||||
TFZXsVpw1M3Vc5nF8cfX8GfDh16cmFx9Tpz9SrVPSVAwiK1V+Lp9p6CGPZhE3Y33
|
||||
b94YUmt+CvWxKx8OeiUGEqLG3W/KpXbnYUUetyCtCMikdmaGPpvq0ifDv5AEt3eZ
|
||||
DWcqZOZ1dSTG9wmKghkVrgnB6Zow4c62WFtrDtFoN0XAjC42KsufgNQ=
|
||||
=766/
|
||||
-----END PGP PUBLIC KEY BLOCK-----`, resp.Body.String())
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -289,6 +289,39 @@ func TestAPIViewRepo(t *testing.T) {
|
|||
assert.EqualValues(t, 1, repo.Stars)
|
||||
}
|
||||
|
||||
// Validate that private information on the user profile isn't exposed by way of being an owner of a public repository.
|
||||
func TestAPIViewRepoOwnerSettings(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
var repo api.Repository
|
||||
|
||||
req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1")
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &repo)
|
||||
assert.EqualValues(t, 1, repo.ID)
|
||||
assert.Equal(t, "user2@noreply.example.org", repo.Owner.Email) // unauthed, always private
|
||||
assert.Empty(t, repo.Owner.Pronouns) // user2.keep_pronouns_private = true
|
||||
|
||||
session := loginUser(t, "user2")
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
|
||||
req = NewRequest(t, "GET", "/api/v1/repos/user2/repo1").AddTokenAuth(token)
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &repo)
|
||||
assert.Equal(t, "user2@noreply.example.org", repo.Owner.Email) // user2.keep_email_private = true
|
||||
assert.Equal(t, "he/him", repo.Owner.Pronouns) // user2.keep_pronouns_private = true
|
||||
|
||||
req = NewRequest(t, "GET", "/api/v1/repos/user12/repo10")
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &repo)
|
||||
assert.EqualValues(t, 10, repo.ID)
|
||||
assert.Equal(t, "user12@noreply.example.org", repo.Owner.Email) // unauthed, always private
|
||||
|
||||
req = NewRequest(t, "GET", "/api/v1/repos/user12/repo10").AddTokenAuth(token)
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &repo)
|
||||
assert.Equal(t, "user12@example.com", repo.Owner.Email) // user2.keep_email_private = false
|
||||
}
|
||||
|
||||
func TestAPIOrgRepos(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue