Skip to content

Commit 96797fe

Browse files
Gustedlunnyyardenshoham
authored
Unify hashing for avatar (#22289)
- Unify the hashing code for repository and user avatars into a function. - Use a sane hash function instead of MD5. - Only require hashing once instead of twice(w.r.t. hashing for user avatar). - Improve the comment for the hashing code of why it works. Co-authored-by: Lunny Xiao <[email protected]> Co-authored-by: Yarden Shoham <[email protected]>
1 parent fcd6cee commit 96797fe

File tree

4 files changed

+32
-11
lines changed

4 files changed

+32
-11
lines changed

modules/avatar/hash.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package avatar
5+
6+
import (
7+
"crypto/sha256"
8+
"encoding/hex"
9+
"strconv"
10+
)
11+
12+
// HashAvatar will generate a unique string, which ensures that when there's a
13+
// different unique ID while the data is the same, it will generate a different
14+
// output. It will generate the output according to:
15+
// HEX(HASH(uniqueID || - || data))
16+
// The hash being used is SHA256.
17+
// The sole purpose of the unique ID is to generate a distinct hash Such that
18+
// two unique IDs with the same data will have a different hash output.
19+
// The "-" byte is important to ensure that data cannot be modified such that
20+
// the first byte is a number, which could lead to a "collision" with the hash
21+
// of another unique ID.
22+
func HashAvatar(uniqueID int64, data []byte) string {
23+
h := sha256.New()
24+
h.Write([]byte(strconv.FormatInt(uniqueID, 10)))
25+
h.Write([]byte{'-'})
26+
h.Write(data)
27+
return hex.EncodeToString(h.Sum(nil))
28+
}

services/repository/avatar.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package repository
55

66
import (
77
"context"
8-
"crypto/md5"
98
"fmt"
109
"image/png"
1110
"io"
@@ -27,7 +26,7 @@ func UploadAvatar(repo *repo_model.Repository, data []byte) error {
2726
return err
2827
}
2928

30-
newAvatar := fmt.Sprintf("%d-%x", repo.ID, md5.Sum(data))
29+
newAvatar := avatar.HashAvatar(repo.ID, data)
3130
if repo.Avatar == newAvatar { // upload the same picture
3231
return nil
3332
}

services/repository/avatar_test.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@ package repository
55

66
import (
77
"bytes"
8-
"crypto/md5"
9-
"fmt"
108
"image"
119
"image/png"
1210
"testing"
1311

1412
repo_model "code.gitea.io/gitea/models/repo"
1513
"code.gitea.io/gitea/models/unittest"
14+
"code.gitea.io/gitea/modules/avatar"
1615

1716
"github.com/stretchr/testify/assert"
1817
)
@@ -28,7 +27,7 @@ func TestUploadAvatar(t *testing.T) {
2827

2928
err := UploadAvatar(repo, buff.Bytes())
3029
assert.NoError(t, err)
31-
assert.Equal(t, fmt.Sprintf("%d-%x", 10, md5.Sum(buff.Bytes())), repo.Avatar)
30+
assert.Equal(t, avatar.HashAvatar(10, buff.Bytes()), repo.Avatar)
3231
}
3332

3433
func TestUploadBigAvatar(t *testing.T) {

services/user/user.go

+1-6
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package user
55

66
import (
77
"context"
8-
"crypto/md5"
98
"fmt"
109
"image/png"
1110
"io"
@@ -241,11 +240,7 @@ func UploadAvatar(u *user_model.User, data []byte) error {
241240
defer committer.Close()
242241

243242
u.UseCustomAvatar = true
244-
// Different users can upload same image as avatar
245-
// If we prefix it with u.ID, it will be separated
246-
// Otherwise, if any of the users delete his avatar
247-
// Other users will lose their avatars too.
248-
u.Avatar = fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d-%x", u.ID, md5.Sum(data)))))
243+
u.Avatar = avatar.HashAvatar(u.ID, data)
249244
if err = user_model.UpdateUserCols(ctx, u, "use_custom_avatar", "avatar"); err != nil {
250245
return fmt.Errorf("updateUser: %w", err)
251246
}

0 commit comments

Comments
 (0)