Skip to content

Commit 63ae215

Browse files
authored
fix: update artifact server to address GHSL-2023-004 (nektos#1565)
1 parent efb12b7 commit 63ae215

File tree

4 files changed

+202
-73
lines changed

4 files changed

+202
-73
lines changed

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"go.lintTool": "golangci-lint",
33
"go.lintFlags": ["--fix"],
4+
"go.testTimeout": "300s",
45
"[json]": {
56
"editor.defaultFormatter": "esbenp.prettier-vscode"
67
},

pkg/artifacts/server.go

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"io/fs"
1010
"net/http"
1111
"os"
12-
"path"
1312
"path/filepath"
1413
"strings"
1514
"time"
@@ -46,28 +45,34 @@ type ResponseMessage struct {
4645
Message string `json:"message"`
4746
}
4847

49-
type MkdirFS interface {
50-
fs.FS
51-
MkdirAll(path string, perm fs.FileMode) error
52-
Open(name string) (fs.File, error)
53-
OpenAtEnd(name string) (fs.File, error)
48+
type WritableFile interface {
49+
io.WriteCloser
5450
}
5551

56-
type MkdirFsImpl struct {
57-
dir string
58-
fs.FS
52+
type WriteFS interface {
53+
OpenWritable(name string) (WritableFile, error)
54+
OpenAppendable(name string) (WritableFile, error)
5955
}
6056

61-
func (fsys MkdirFsImpl) MkdirAll(path string, perm fs.FileMode) error {
62-
return os.MkdirAll(fsys.dir+"/"+path, perm)
57+
type readWriteFSImpl struct {
6358
}
6459

65-
func (fsys MkdirFsImpl) Open(name string) (fs.File, error) {
66-
return os.OpenFile(fsys.dir+"/"+name, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)
60+
func (fwfs readWriteFSImpl) Open(name string) (fs.File, error) {
61+
return os.Open(name)
6762
}
6863

69-
func (fsys MkdirFsImpl) OpenAtEnd(name string) (fs.File, error) {
70-
file, err := os.OpenFile(fsys.dir+"/"+name, os.O_CREATE|os.O_RDWR, 0644)
64+
func (fwfs readWriteFSImpl) OpenWritable(name string) (WritableFile, error) {
65+
if err := os.MkdirAll(filepath.Dir(name), os.ModePerm); err != nil {
66+
return nil, err
67+
}
68+
return os.OpenFile(name, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)
69+
}
70+
71+
func (fwfs readWriteFSImpl) OpenAppendable(name string) (WritableFile, error) {
72+
if err := os.MkdirAll(filepath.Dir(name), os.ModePerm); err != nil {
73+
return nil, err
74+
}
75+
file, err := os.OpenFile(name, os.O_CREATE|os.O_RDWR, 0644)
7176

7277
if err != nil {
7378
return nil, err
@@ -77,13 +82,16 @@ func (fsys MkdirFsImpl) OpenAtEnd(name string) (fs.File, error) {
7782
if err != nil {
7883
return nil, err
7984
}
80-
8185
return file, nil
8286
}
8387

8488
var gzipExtension = ".gz__"
8589

86-
func uploads(router *httprouter.Router, fsys MkdirFS) {
90+
func safeResolve(baseDir string, relPath string) string {
91+
return filepath.Join(baseDir, filepath.Clean(filepath.Join(string(os.PathSeparator), relPath)))
92+
}
93+
94+
func uploads(router *httprouter.Router, baseDir string, fsys WriteFS) {
8795
router.POST("/_apis/pipelines/workflows/:runId/artifacts", func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
8896
runID := params.ByName("runId")
8997

@@ -108,19 +116,15 @@ func uploads(router *httprouter.Router, fsys MkdirFS) {
108116
itemPath += gzipExtension
109117
}
110118

111-
filePath := fmt.Sprintf("%s/%s", runID, itemPath)
119+
safeRunPath := safeResolve(baseDir, runID)
120+
safePath := safeResolve(safeRunPath, itemPath)
112121

113-
err := fsys.MkdirAll(path.Dir(filePath), os.ModePerm)
114-
if err != nil {
115-
panic(err)
116-
}
117-
118-
file, err := func() (fs.File, error) {
122+
file, err := func() (WritableFile, error) {
119123
contentRange := req.Header.Get("Content-Range")
120124
if contentRange != "" && !strings.HasPrefix(contentRange, "bytes 0-") {
121-
return fsys.OpenAtEnd(filePath)
125+
return fsys.OpenAppendable(safePath)
122126
}
123-
return fsys.Open(filePath)
127+
return fsys.OpenWritable(safePath)
124128
}()
125129

126130
if err != nil {
@@ -170,11 +174,13 @@ func uploads(router *httprouter.Router, fsys MkdirFS) {
170174
})
171175
}
172176

173-
func downloads(router *httprouter.Router, fsys fs.FS) {
177+
func downloads(router *httprouter.Router, baseDir string, fsys fs.FS) {
174178
router.GET("/_apis/pipelines/workflows/:runId/artifacts", func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
175179
runID := params.ByName("runId")
176180

177-
entries, err := fs.ReadDir(fsys, runID)
181+
safePath := safeResolve(baseDir, runID)
182+
183+
entries, err := fs.ReadDir(fsys, safePath)
178184
if err != nil {
179185
panic(err)
180186
}
@@ -204,12 +210,12 @@ func downloads(router *httprouter.Router, fsys fs.FS) {
204210
router.GET("/download/:container", func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
205211
container := params.ByName("container")
206212
itemPath := req.URL.Query().Get("itemPath")
207-
dirPath := fmt.Sprintf("%s/%s", container, itemPath)
213+
safePath := safeResolve(baseDir, filepath.Join(container, itemPath))
208214

209215
var files []ContainerItem
210-
err := fs.WalkDir(fsys, dirPath, func(path string, entry fs.DirEntry, err error) error {
216+
err := fs.WalkDir(fsys, safePath, func(path string, entry fs.DirEntry, err error) error {
211217
if !entry.IsDir() {
212-
rel, err := filepath.Rel(dirPath, path)
218+
rel, err := filepath.Rel(safePath, path)
213219
if err != nil {
214220
panic(err)
215221
}
@@ -218,7 +224,7 @@ func downloads(router *httprouter.Router, fsys fs.FS) {
218224
rel = strings.TrimSuffix(rel, gzipExtension)
219225

220226
files = append(files, ContainerItem{
221-
Path: fmt.Sprintf("%s/%s", itemPath, rel),
227+
Path: filepath.Join(itemPath, rel),
222228
ItemType: "file",
223229
ContentLocation: fmt.Sprintf("http://%s/artifact/%s/%s/%s", req.Host, container, itemPath, rel),
224230
})
@@ -245,10 +251,12 @@ func downloads(router *httprouter.Router, fsys fs.FS) {
245251
router.GET("/artifact/*path", func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
246252
path := params.ByName("path")[1:]
247253

248-
file, err := fsys.Open(path)
254+
safePath := safeResolve(baseDir, path)
255+
256+
file, err := fsys.Open(safePath)
249257
if err != nil {
250258
// try gzip file
251-
file, err = fsys.Open(path + gzipExtension)
259+
file, err = fsys.Open(safePath + gzipExtension)
252260
if err != nil {
253261
panic(err)
254262
}
@@ -273,9 +281,9 @@ func Serve(ctx context.Context, artifactPath string, addr string, port string) c
273281
router := httprouter.New()
274282

275283
logger.Debugf("Artifacts base path '%s'", artifactPath)
276-
fs := os.DirFS(artifactPath)
277-
uploads(router, MkdirFsImpl{artifactPath, fs})
278-
downloads(router, fs)
284+
fsys := readWriteFSImpl{}
285+
uploads(router, artifactPath, fsys)
286+
downloads(router, artifactPath, fsys)
279287

280288
server := &http.Server{
281289
Addr: fmt.Sprintf("%s:%s", addr, port),

0 commit comments

Comments
 (0)