code.gitea.io/gitea@v1.21.7/routers/api/actions/artifacts_utils.go (about) 1 // Copyright 2023 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package actions 5 6 import ( 7 "crypto/md5" 8 "fmt" 9 "net/http" 10 "strconv" 11 "strings" 12 13 "code.gitea.io/gitea/models/actions" 14 "code.gitea.io/gitea/modules/log" 15 "code.gitea.io/gitea/modules/util" 16 ) 17 18 const ( 19 artifactXTfsFileLengthHeader = "x-tfs-filelength" 20 artifactXActionsResultsMD5Header = "x-actions-results-md5" 21 ) 22 23 // The rules are from https://github.com/actions/toolkit/blob/main/packages/artifact/src/internal/path-and-artifact-name-validation.ts#L32 24 var invalidArtifactNameChars = strings.Join([]string{"\\", "/", "\"", ":", "<", ">", "|", "*", "?", "\r", "\n"}, "") 25 26 func validateArtifactName(ctx *ArtifactContext, artifactName string) bool { 27 if strings.ContainsAny(artifactName, invalidArtifactNameChars) { 28 log.Error("Error checking artifact name contains invalid character") 29 ctx.Error(http.StatusBadRequest, "Error checking artifact name contains invalid character") 30 return false 31 } 32 return true 33 } 34 35 func validateRunID(ctx *ArtifactContext) (*actions.ActionTask, int64, bool) { 36 task := ctx.ActionTask 37 runID := ctx.ParamsInt64("run_id") 38 if task.Job.RunID != runID { 39 log.Error("Error runID not match") 40 ctx.Error(http.StatusBadRequest, "run-id does not match") 41 return nil, 0, false 42 } 43 return task, runID, true 44 } 45 46 func validateArtifactHash(ctx *ArtifactContext, artifactName string) bool { 47 paramHash := ctx.Params("artifact_hash") 48 // use artifact name to create upload url 49 artifactHash := fmt.Sprintf("%x", md5.Sum([]byte(artifactName))) 50 if paramHash == artifactHash { 51 return true 52 } 53 log.Error("Invalid artifact hash: %s", paramHash) 54 ctx.Error(http.StatusBadRequest, "Invalid artifact hash") 55 return false 56 } 57 58 func parseArtifactItemPath(ctx *ArtifactContext) (string, string, bool) { 59 // itemPath is generated from upload-artifact action 60 // it's formatted as {artifact_name}/{artfict_path_in_runner} 61 // act_runner in host mode on Windows, itemPath is joined by Windows slash '\' 62 itemPath := util.PathJoinRelX(ctx.Req.URL.Query().Get("itemPath")) 63 artifactName := strings.Split(itemPath, "/")[0] 64 artifactPath := strings.TrimPrefix(itemPath, artifactName+"/") 65 if !validateArtifactHash(ctx, artifactName) { 66 return "", "", false 67 } 68 if !validateArtifactName(ctx, artifactName) { 69 return "", "", false 70 } 71 return artifactName, artifactPath, true 72 } 73 74 // getUploadFileSize returns the size of the file to be uploaded. 75 // The raw size is the size of the file as reported by the header X-TFS-FileLength. 76 func getUploadFileSize(ctx *ArtifactContext) (int64, int64, error) { 77 contentLength := ctx.Req.ContentLength 78 xTfsLength, _ := strconv.ParseInt(ctx.Req.Header.Get(artifactXTfsFileLengthHeader), 10, 64) 79 if xTfsLength > 0 { 80 return xTfsLength, contentLength, nil 81 } 82 return contentLength, contentLength, nil 83 }