1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
|
package cache
import (
"fmt"
"net/http"
"net/url"
"path"
"strconv"
"strings"
"github.com/sirupsen/logrus"
"gitlab.com/gitlab-org/gitlab-runner/common"
)
var createAdapter = CreateAdapter
// generateObjectName returns a fully-qualified name for the cache object,
// ensuring there's no path traversal outside.
func generateObjectName(build *common.Build, config *common.CacheConfig, key string) (string, error) {
if key == "" {
return "", nil
}
// runners get their own namespace, unless they're shared, in which case the
// namespace is empty.
namespace := ""
if !config.GetShared() {
namespace = path.Join("runner", build.Runner.ShortDescription())
}
basePath := path.Join(config.GetPath(), namespace, "project", strconv.FormatInt(build.JobInfo.ProjectID, 10))
fullPath := path.Join(basePath, key)
// The typical concerns regarding the use of strings.HasPrefix to detect
// path traversal do not apply here. The detection here is made easier
// as we're dealing with URL paths, not filepaths and we're ensuring that
// the basepath has a final separator (the key can not be empty).
// TestGenerateObjectName contains path traversal tests.
if !strings.HasPrefix(fullPath, basePath+"/") {
return "", fmt.Errorf("computed cache path outside of project bucket. Please remove `../` from cache key")
}
return fullPath, nil
}
func getAdaptorForBuild(build *common.Build, key string) Adapter {
if build == nil || build.Runner == nil || build.Runner.Cache == nil {
logrus.Warning("Cache config not defined. Skipping cache operation.")
return nil
}
objectName, err := generateObjectName(build, build.Runner.Cache, key)
if err != nil {
logrus.WithError(err).Error("Error while generating cache bucket.")
return nil
}
if objectName == "" {
logrus.Warning("Empty cache key. Skipping adapter selection.")
return nil
}
adapter, err := createAdapter(build.Runner.Cache, build.GetBuildTimeout(), objectName)
if err != nil {
logrus.WithError(err).Error("Could not create cache adapter")
}
return adapter
}
func GetCacheDownloadURL(build *common.Build, key string) *url.URL {
adaptor := getAdaptorForBuild(build, key)
if adaptor == nil {
return nil
}
return adaptor.GetDownloadURL()
}
func GetCacheUploadURL(build *common.Build, key string) *url.URL {
adaptor := getAdaptorForBuild(build, key)
if adaptor == nil {
return nil
}
return adaptor.GetUploadURL()
}
func GetCacheUploadHeaders(build *common.Build, key string) http.Header {
adaptor := getAdaptorForBuild(build, key)
if adaptor == nil {
return nil
}
return adaptor.GetUploadHeaders()
}
func GetCacheGoCloudURL(build *common.Build, key string) *url.URL {
adaptor := getAdaptorForBuild(build, key)
if adaptor == nil {
return nil
}
return adaptor.GetGoCloudURL()
}
func GetCacheUploadEnv(build *common.Build, key string) map[string]string {
adaptor := getAdaptorForBuild(build, key)
if adaptor == nil {
return nil
}
return adaptor.GetUploadEnv()
}
|