summaryrefslogtreecommitdiffstats
path: root/routers/api/actions/artifacts_utils.go
diff options
context:
space:
mode:
Diffstat (limited to 'routers/api/actions/artifacts_utils.go')
-rw-r--r--routers/api/actions/artifacts_utils.go94
1 files changed, 94 insertions, 0 deletions
diff --git a/routers/api/actions/artifacts_utils.go b/routers/api/actions/artifacts_utils.go
new file mode 100644
index 0000000..db602f1
--- /dev/null
+++ b/routers/api/actions/artifacts_utils.go
@@ -0,0 +1,94 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package actions
+
+import (
+ "crypto/md5"
+ "fmt"
+ "net/http"
+ "strconv"
+ "strings"
+
+ "code.gitea.io/gitea/models/actions"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/util"
+)
+
+const (
+ artifactXTfsFileLengthHeader = "x-tfs-filelength"
+ artifactXActionsResultsMD5Header = "x-actions-results-md5"
+)
+
+// The rules are from https://github.com/actions/toolkit/blob/main/packages/artifact/src/internal/path-and-artifact-name-validation.ts#L32
+var invalidArtifactNameChars = strings.Join([]string{"\\", "/", "\"", ":", "<", ">", "|", "*", "?", "\r", "\n"}, "")
+
+func validateArtifactName(ctx *ArtifactContext, artifactName string) bool {
+ if strings.ContainsAny(artifactName, invalidArtifactNameChars) {
+ log.Error("Error checking artifact name contains invalid character")
+ ctx.Error(http.StatusBadRequest, "Error checking artifact name contains invalid character")
+ return false
+ }
+ return true
+}
+
+func validateRunID(ctx *ArtifactContext) (*actions.ActionTask, int64, bool) {
+ task := ctx.ActionTask
+ runID := ctx.ParamsInt64("run_id")
+ if task.Job.RunID != runID {
+ log.Error("Error runID not match")
+ ctx.Error(http.StatusBadRequest, "run-id does not match")
+ return nil, 0, false
+ }
+ return task, runID, true
+}
+
+func validateRunIDV4(ctx *ArtifactContext, rawRunID string) (*actions.ActionTask, int64, bool) { //nolint:unparam
+ task := ctx.ActionTask
+ runID, err := strconv.ParseInt(rawRunID, 10, 64)
+ if err != nil || task.Job.RunID != runID {
+ log.Error("Error runID not match")
+ ctx.Error(http.StatusBadRequest, "run-id does not match")
+ return nil, 0, false
+ }
+ return task, runID, true
+}
+
+func validateArtifactHash(ctx *ArtifactContext, artifactName string) bool {
+ paramHash := ctx.Params("artifact_hash")
+ // use artifact name to create upload url
+ artifactHash := fmt.Sprintf("%x", md5.Sum([]byte(artifactName)))
+ if paramHash == artifactHash {
+ return true
+ }
+ log.Warn("Invalid artifact hash: %s", paramHash)
+ ctx.Error(http.StatusBadRequest, "Invalid artifact hash")
+ return false
+}
+
+func parseArtifactItemPath(ctx *ArtifactContext) (string, string, bool) {
+ // itemPath is generated from upload-artifact action
+ // it's formatted as {artifact_name}/{artfict_path_in_runner}
+ // act_runner in host mode on Windows, itemPath is joined by Windows slash '\'
+ itemPath := util.PathJoinRelX(ctx.Req.URL.Query().Get("itemPath"))
+ artifactName := strings.Split(itemPath, "/")[0]
+ artifactPath := strings.TrimPrefix(itemPath, artifactName+"/")
+ if !validateArtifactHash(ctx, artifactName) {
+ return "", "", false
+ }
+ if !validateArtifactName(ctx, artifactName) {
+ return "", "", false
+ }
+ return artifactName, artifactPath, true
+}
+
+// getUploadFileSize returns the size of the file to be uploaded.
+// The raw size is the size of the file as reported by the header X-TFS-FileLength.
+func getUploadFileSize(ctx *ArtifactContext) (int64, int64) {
+ contentLength := ctx.Req.ContentLength
+ xTfsLength, _ := strconv.ParseInt(ctx.Req.Header.Get(artifactXTfsFileLengthHeader), 10, 64)
+ if xTfsLength > 0 {
+ return xTfsLength, contentLength
+ }
+ return contentLength, contentLength
+}