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  }