diff --git a/modules/actions/artifacts.go b/modules/actions/artifacts.go new file mode 100644 index 00000000000..743e2f00fe2 --- /dev/null +++ b/modules/actions/artifacts.go @@ -0,0 +1,29 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/binary" + "io" + + "code.gitea.io/gitea/modules/setting" +) + +type tagType string + +// BuildSignature builds a hmac signature for the input values. +// "tag" is an internal pre-defined static string to distinguish the signatures for different purpose. +func BuildSignature(tag tagType, vals ...string) []byte { + m := hmac.New(sha256.New, setting.GetGeneralTokenSigningSecret()) + _, _ = io.WriteString(m, string(tag)) + var buf8 [8]byte + for _, v := range vals { + binary.LittleEndian.PutUint64(buf8[:], uint64(len(v))) + _, _ = m.Write(buf8[:]) + _, _ = io.WriteString(m, v) + } + return m.Sum(nil) +} diff --git a/modules/actions/artifacts_test.go b/modules/actions/artifacts_test.go new file mode 100644 index 00000000000..4c038436539 --- /dev/null +++ b/modules/actions/artifacts_test.go @@ -0,0 +1,36 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBuildSignature(t *testing.T) { + a := BuildSignature("v0", "x") + b := BuildSignature("v0", "x") + assert.Equal(t, a, b) + + a = BuildSignature("v0", "x", "yz") + b = BuildSignature("v0", "xy", "z") + assert.NotEqual(t, a, b) + + a = BuildSignature("v1", "x") + b = BuildSignature("v2", "x") + assert.NotEqual(t, a, b) + + a = BuildSignature("v0", "x") + b = BuildSignature("v0x") + assert.NotEqual(t, a, b) + + a = BuildSignature("v0", "", "x") + b = BuildSignature("v0", "x", "") + assert.NotEqual(t, a, b) + + a = BuildSignature("v0") + b = BuildSignature("v0") + assert.Equal(t, a, b) +} diff --git a/routers/api/actions/artifactsv4.go b/routers/api/actions/artifactsv4.go index f1f33424edd..a50d9c9ffd5 100644 --- a/routers/api/actions/artifactsv4.go +++ b/routers/api/actions/artifactsv4.go @@ -104,6 +104,7 @@ import ( actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" + actions_module "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" @@ -162,13 +163,7 @@ func ArtifactsV4Routes(prefix string) *web.Router { } func (r *artifactV4Routes) buildSignature(endpoint, expires, artifactName string, taskID, artifactID int64) []byte { - mac := hmac.New(sha256.New, setting.GetGeneralTokenSigningSecret()) - mac.Write([]byte(endpoint)) - mac.Write([]byte(expires)) - mac.Write([]byte(artifactName)) - _, _ = fmt.Fprint(mac, taskID) - _, _ = fmt.Fprint(mac, artifactID) - return mac.Sum(nil) + return actions_module.BuildSignature("v4", endpoint, expires, artifactName, strconv.FormatInt(taskID, 10), strconv.FormatInt(artifactID, 10)) } func (r *artifactV4Routes) buildArtifactURL(ctx *ArtifactContext, endpoint, artifactName string, taskID, artifactID int64) string { diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go index 666d4f98ef0..b70cdafe242 100644 --- a/routers/api/v1/repo/action.go +++ b/routers/api/v1/repo/action.go @@ -6,7 +6,6 @@ package repo import ( go_context "context" "crypto/hmac" - "crypto/sha256" "encoding/base64" "errors" "fmt" @@ -21,9 +20,9 @@ import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" secret_model "code.gitea.io/gitea/models/secret" + "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/optional" - "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" @@ -1959,11 +1958,7 @@ func DeleteArtifact(ctx *context.APIContext) { } func buildSignature(endp string, expires, artifactID int64) []byte { - mac := hmac.New(sha256.New, setting.GetGeneralTokenSigningSecret()) - mac.Write([]byte(endp)) - fmt.Fprint(mac, expires) - fmt.Fprint(mac, artifactID) - return mac.Sum(nil) + return actions.BuildSignature("api", endp, strconv.FormatInt(expires, 10), strconv.FormatInt(artifactID, 10)) } func buildDownloadRawEndpoint(repo *repo_model.Repository, artifactID int64) string {