loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix verifyCommits error when push a new branch (#26664)

> ### Description
> If a new branch is pushed, and the repository has a rule that would
require signed commits for the new branch, the commit is rejected with a
500 error regardless of whether it's signed.
>
> When pushing a new branch, the "old" commit is the empty ID
(0000000000000000000000000000000000000000). verifyCommits has no
provision for this and passes an invalid commit range to git rev-list.
Prior to 1.19 this wasn't an issue because only pre-existing individual
branches could be protected.
>
> I was able to reproduce with
[try.gitea.io/CraigTest/test](https://try.gitea.io/CraigTest/test),
which is set up with a blanket rule to require commits on all branches.


Fix #25565
Very thanks to @Craig-Holmquist-NTI for reporting the bug and suggesting
an valid solution!

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>

authored by

CaiCandong
wxiaoguang
Lunny Xiao
and committed by
GitHub
815d267c 508de3a5

+270 -20
+9 -1
models/fixtures/email_address.yml
··· 276 276 email: user2-2@example.com 277 277 lower_email: user2-2@example.com 278 278 is_activated: false 279 - is_primary: false 279 + is_primary: false 280 + 281 + - 282 + id: 36 283 + uid: 36 284 + email: abcde@gitea.com 285 + lower_email: abcde@gitea.com 286 + is_activated: true 287 + is_primary: false
+23 -1
models/fixtures/gpg_key.yml
··· 1 - [] # empty 1 + - 2 + id: 5 3 + owner_id: 36 4 + key_id: B15431642629B826 5 + primary_key_id: 6 + content: xsDNBGTrY3UBDAC2HLBqmMplAV15qSnC7g1c4dV406f5EHNhFr95Nup2My6b2eafTlvedv77s8PT/I7F3fy4apOZs5A7w2SsPlLMcQ3ev4uGOsxRtkq5RLy1Yb6SNueX0Da2UVKR5KTC5Q6BWaqxwS0IjKOLZ/xz0Pbe/ClV3bZSKBEY2omkVo3Z0HZ771vB2clPRvGJ/IdeKOsZ3ZytSFXfyiJBdARmeSPmydXLil8+Ibq5iLAeow5PK8hK1TCOnKHzLWNqcNq70tyjoHvcGi70iGjoVEEUgPCLLuU8WmzTJwlvA3BuDzjtaO7TLo/jdE6iqkHtMSS8x+43sAH6hcFRCWAVh/0Uq7n36uGDfNxGnX3YrmX3LR9x5IsBES1rGGWbpxio4o5GIf/Xd+JgDd9rzJCqRuZ3/sW/TxK38htWaVNZV0kMkHUCTc1ctzWpCm635hbFCHBhPYIp+/z206khkAKDbz/CNuU91Wazsh7KO07wrwDtxfDDbInJ8TfHE2TGjzjQzgChfmcAEQEAAQ== 7 + verified: true 8 + can_sign: true 9 + can_encrypt_comms: true 10 + can_encrypt_storage: true 11 + can_certify: true 12 + 13 + - 14 + id: 6 15 + owner_id: 36 16 + key_id: EE3AF48454AFD619 17 + primary_key_id: B15431642629B826 18 + content: zsDNBGTrY3UBDADsHrzuOicQaPdUQm0+0UNrs92cESm/j/4yBBUk+sfLZAo6J99c4eh4nAQzzZ7al080rYKB0G+7xoRz1eHcQH6zrVcqB8KYtf/sdY47WaMiMyxM+kTSvzp7tsv7QuSQZ0neUEXRyYMz5ttBfIjWUd+3NDItuHyB+MtNWlS3zXgaUbe5VifqKaNmzN0Ye4yXTKcpypE3AOqPVz+iIFv3c6TmsqLHJaR4VoicCleAqLyF/28WsJO7M9dDW+EM3MZVnsVpycTURyHAJGfSk10waQZAaRwmarCN/q0KEJ+aEAK/SRliUneBZoMO5hY5iBeG432tofwaQqAahPv9uXIb1n2JEMKwnMlMA9UGD1AcDbywfj1m/ZGBBw95i4Ekkfn43RvV3THr7uJU/dRqqP+iic4MwpUrOxqELW/kmeHXlBcNbZZhEEvwRoW7U2/9eeuog4nRleRJ0pi/xOP9wmxkKjaIPIK3phdBtEpVk4w/UTAWNdyIIrFggukeAnZFyGJwlm8AEQEAAQ== 19 + verified: true 20 + can_sign: true 21 + can_encrypt_comms: true 22 + can_encrypt_storage: true 23 + can_certify: true
+2 -2
models/fixtures/user.yml
··· 1301 1301 lower_name: limited_org36 1302 1302 name: limited_org36 1303 1303 full_name: Limited Org 36 1304 - email: limited_org36@example.com 1304 + email: abcde@gitea.com 1305 1305 keep_email_private: false 1306 1306 email_notifications_preference: enabled 1307 1307 passwd: ZogKvWdyEx:password ··· 1320 1320 allow_create_organization: true 1321 1321 prohibit_login: false 1322 1322 avatar: avatar22 1323 - avatar_email: limited_org36@example.com 1323 + avatar_email: abcde@gitea.com 1324 1324 use_custom_avatar: false 1325 1325 num_followers: 0 1326 1326 num_following: 0
+24 -16
routers/private/hook_verification.go
··· 28 28 _ = stdoutWriter.Close() 29 29 }() 30 30 31 + var command *git.Command 32 + if oldCommitID == git.EmptySHA { 33 + // When creating a new branch, the oldCommitID is empty, by using "newCommitID --not --all": 34 + // List commits that are reachable by following the newCommitID, exclude "all" existing heads/tags commits 35 + // So, it only lists the new commits received, doesn't list the commits already present in the receiving repository 36 + command = git.NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(newCommitID).AddArguments("--not", "--all") 37 + } else { 38 + command = git.NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(oldCommitID + "..." + newCommitID) 39 + } 31 40 // This is safe as force pushes are already forbidden 32 - err = git.NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(oldCommitID + "..." + newCommitID). 33 - Run(&git.RunOpts{ 34 - Env: env, 35 - Dir: repo.Path, 36 - Stdout: stdoutWriter, 37 - PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { 38 - _ = stdoutWriter.Close() 39 - err := readAndVerifyCommitsFromShaReader(stdoutReader, repo, env) 40 - if err != nil { 41 - log.Error("%v", err) 42 - cancel() 43 - } 44 - _ = stdoutReader.Close() 45 - return err 46 - }, 47 - }) 41 + err = command.Run(&git.RunOpts{ 42 + Env: env, 43 + Dir: repo.Path, 44 + Stdout: stdoutWriter, 45 + PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { 46 + _ = stdoutWriter.Close() 47 + err := readAndVerifyCommitsFromShaReader(stdoutReader, repo, env) 48 + if err != nil { 49 + log.Error("%v", err) 50 + cancel() 51 + } 52 + _ = stdoutReader.Close() 53 + return err 54 + }, 55 + }) 48 56 if err != nil && !isErrUnverifiedCommit(err) { 49 57 log.Error("Unable to check commits from %s to %s in %s: %v", oldCommitID, newCommitID, repo.Path, err) 50 58 }
+43
routers/private/hook_verification_test.go
··· 1 + // Copyright 2023 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package private 5 + 6 + import ( 7 + "context" 8 + "testing" 9 + 10 + "code.gitea.io/gitea/models/unittest" 11 + "code.gitea.io/gitea/modules/git" 12 + 13 + "github.com/stretchr/testify/assert" 14 + ) 15 + 16 + var testReposDir = "tests/repos/" 17 + 18 + func TestVerifyCommits(t *testing.T) { 19 + unittest.PrepareTestEnv(t) 20 + 21 + gitRepo, err := git.OpenRepository(context.Background(), testReposDir+"repo1_hook_verification") 22 + defer gitRepo.Close() 23 + assert.NoError(t, err) 24 + 25 + testCases := []struct { 26 + base, head string 27 + verified bool 28 + }{ 29 + {"72920278f2f999e3005801e5d5b8ab8139d3641c", "d766f2917716d45be24bfa968b8409544941be32", true}, 30 + {git.EmptySHA, "93eac826f6188f34646cea81bf426aa5ba7d3bfe", true}, // New branch with verified commit 31 + {"9779d17a04f1e2640583d35703c62460b2d86e0a", "72920278f2f999e3005801e5d5b8ab8139d3641c", false}, 32 + {git.EmptySHA, "9ce3f779ae33f31fce17fac3c512047b75d7498b", false}, // New branch with unverified commit 33 + } 34 + 35 + for _, tc := range testCases { 36 + err = verifyCommits(tc.base, tc.head, gitRepo, nil) 37 + if tc.verified { 38 + assert.NoError(t, err) 39 + } else { 40 + assert.Error(t, err) 41 + } 42 + } 43 + }
+17
routers/private/main_test.go
··· 1 + // Copyright 2017 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package private 5 + 6 + import ( 7 + "path/filepath" 8 + "testing" 9 + 10 + "code.gitea.io/gitea/models/unittest" 11 + ) 12 + 13 + func TestMain(m *testing.M) { 14 + unittest.MainTest(m, &unittest.TestOptions{ 15 + GiteaRootPath: filepath.Join("..", ".."), 16 + }) 17 + }
+1
routers/private/tests/repos/repo1_hook_verification/HEAD
··· 1 + ref: refs/heads/main
+6
routers/private/tests/repos/repo1_hook_verification/config
··· 1 + [core] 2 + repositoryformatversion = 0 3 + filemode = false 4 + bare = true 5 + symlinks = false 6 + ignorecase = true
+1
routers/private/tests/repos/repo1_hook_verification/info/refs
··· 1 + d766f2917716d45be24bfa968b8409544941be32 refs/heads/main
+1
routers/private/tests/repos/repo1_hook_verification/logs/HEAD
··· 1 + 0000000000000000000000000000000000000000 d766f2917716d45be24bfa968b8409544941be32 Gitea <gitea@fake.local> 1693148474 +0800 push
+1
routers/private/tests/repos/repo1_hook_verification/logs/refs/heads/main
··· 1 + 0000000000000000000000000000000000000000 d766f2917716d45be24bfa968b8409544941be32 Gitea <gitea@fake.local> 1693148474 +0800 push
routers/private/tests/repos/repo1_hook_verification/objects/08/cbc8f0e903b0916025ae7577564b7ed39ecb2c

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/0b/5987362fe3fabdd4406babdc819642ee2f5a2a

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/13/b0f23f673b161f4b5cb66f051cb93c99729e1e

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/23/33a51fdb238b7023a62ae3dcc58994061a7c86

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/2b/df04adb23d2b40b6085efb230856e5e2a775b7

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/65/a457425a679cbe9adf0d2741785d3ceabb44a7

This is a binary file and will not be displayed.

+2
routers/private/tests/repos/repo1_hook_verification/objects/72/920278f2f999e3005801e5d5b8ab8139d3641c
··· 1 + x��K 2 + 1]�� ���A�S��$�"32 oo���W����{�!��`��JC%�.� $�r]sѱe$�m��M��)���(O`�btl�E[:;4��H�1_���ray���l����~��E�L@c��Xv�M��":�MۃG�_}�?�
routers/private/tests/repos/repo1_hook_verification/objects/8b/903ede7c494725624bf842ec890f6342dababd

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/93/eac826f6188f34646cea81bf426aa5ba7d3bfe

This is a binary file and will not be displayed.

+2
routers/private/tests/repos/repo1_hook_verification/objects/97/79d17a04f1e2640583d35703c62460b2d86e0a
··· 1 + x��1 2 + !ES{��AwGGa 9E�Qg W���#�A�����Z�/������p��(�(����Bh�ۼ&�����:pLY`���U��-�z�����\��ZM:���������xJ/�G}:�3
routers/private/tests/repos/repo1_hook_verification/objects/9c/e3f779ae33f31fce17fac3c512047b75d7498b

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/a9/f76e70a663e40091749a97eeac5f57a6fec141

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/ba/0caedd359ebe310ef431335576e20f2b84e9b9

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/bb/87653e0819460e79b5f075f2563f583cbbf18c

This is a binary file and will not be displayed.

+3
routers/private/tests/repos/repo1_hook_verification/objects/cb/a4c30c196a0e03e7bdf6eeb8393d14b9d073aa
··· 1 + x��A 2 + �0E]��$�L� �x�L2�]��� 3 + ��� \}����e[��:{�M���Z5b�8$���v ��f�R�37];�ˆ�bt�� ��3�$�,tX��G��>m ���p���1w͗�-7p���Ąp��Zs�DZ�L̾�������L��e@�
+3
routers/private/tests/repos/repo1_hook_verification/objects/d7/66f2917716d45be24bfa968b8409544941be32
··· 1 + x��ˮ�FE3�+zn%44�!%Qx��ۀs�A�` 8����{��2IM�j�����d��f���2���"�$�e�� 2 + -(� ��!��J"�a��@�Ba��Ho3��V���<$�/)�$J�JD�B��H����{� #� RR��O��nf��F��O�� 3 + �q[��2�̇~������zjj����L����}prm�Fqh�� `@ث������՘f�?3�[7������) ^��u�ֿ,���l7�z��r|&�Ou4��9�:��Qj�����1x���6�Q����%��t��s���V�| (�V��,aL,��G~�������r����@�`���$[! Xˊep���[8 o��(���k�Z�γy�eй����Y��k�d��6�3�;3�� R�i ދdY�Dk91V]/C�#��&��po�F�b���}�����uW&]+m xaqd�I�X��3 ��3���K���I#�i_�rg�������ĩ7�=�`@[��&A��̤�L�o��3~M����8MGt���>���x��vQ�(��a��W�o"��s�r�z��e�ŭ}��QD��֨fK)��m�r>>�������̚$�F8�x� ��^J� k{mcz�I*�^�Mb�� m�6��M~h�p�� {���0��� ]€?nUwg�ɠ���J �б��<�7�2
routers/private/tests/repos/repo1_hook_verification/objects/e3/7e5d19823e42fad252f6341b1f77a7bc6ee451

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/e8/4e452c3a20c02dee17ec2f63c0cb9ef6c759eb

This is a binary file and will not be displayed.

+1
routers/private/tests/repos/repo1_hook_verification/objects/info/packs
··· 1 +
routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/08/cbc8f0e903b0916025ae7577564b7ed39ecb2c

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/0b/5987362fe3fabdd4406babdc819642ee2f5a2a

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/13/b0f23f673b161f4b5cb66f051cb93c99729e1e

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/23/33a51fdb238b7023a62ae3dcc58994061a7c86

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/8b/903ede7c494725624bf842ec890f6342dababd

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/93/eac826f6188f34646cea81bf426aa5ba7d3bfe

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/9c/e3f779ae33f31fce17fac3c512047b75d7498b

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/a9/f76e70a663e40091749a97eeac5f57a6fec141

This is a binary file and will not be displayed.

routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/bb/87653e0819460e79b5f075f2563f583cbbf18c

This is a binary file and will not be displayed.

+3
routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/cb/a4c30c196a0e03e7bdf6eeb8393d14b9d073aa
··· 1 + x��A 2 + �0E]��$�L� �x�L2�]��� 3 + ��� \}����e[��:{�M���Z5b�8$���v ��f�R�37];�ˆ�bt�� ��3�$�,tX��G��>m ���p���1w͗�-7p���Ąp��Zs�DZ�L̾�������L��e@�
+1
routers/private/tests/repos/repo1_hook_verification/refs/heads/main
··· 1 + d766f2917716d45be24bfa968b8409544941be32
+127
routers/private/tests/repos/repo1_hook_verification_dummy_gpg_key.txt
··· 1 + # GPG key for abcde@gitea.com 2 + 3 + -----BEGIN PGP PUBLIC KEY BLOCK----- 4 + 5 + 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 41 + 4akc 42 + =OYIo 43 + -----END PGP PUBLIC KEY BLOCK----- 44 + 45 + -----BEGIN PGP PRIVATE KEY BLOCK----- 46 + 47 + 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 125 + ZZtqph2TItCeV04HoaKHHc254akc 126 + =PPG4 127 + -----END PGP PRIVATE KEY BLOCK-----