Skip to content

Commit 77d1c9c

Browse files
committed
Ensure rel path never traverses outside Storage
Signed-off-by: Hidde Beydals <[email protected]>
1 parent edb4595 commit 77d1c9c

File tree

1 file changed

+28
-28
lines changed

1 file changed

+28
-28
lines changed

controllers/storage.go

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"strings"
3030
"time"
3131

32+
securejoin "github.com/cyphar/filepath-securejoin"
3233
"github.com/go-git/go-git/v5/plumbing/format/gitignore"
3334
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3435

@@ -83,8 +84,7 @@ func (s Storage) SetArtifactURL(artifact *sourcev1.Artifact) {
8384
artifact.URL = fmt.Sprintf("http://%s/%s", s.Hostname, artifact.Path)
8485
}
8586

86-
// SetHostname sets the hostname of the given URL string to the current Storage.Hostname
87-
// and returns the result.
87+
// SetHostname sets the hostname of the given URL string to the current Storage.Hostname and returns the result.
8888
func (s Storage) SetHostname(URL string) string {
8989
u, err := url.Parse(URL)
9090
if err != nil {
@@ -106,8 +106,7 @@ func (s *Storage) RemoveAll(artifact sourcev1.Artifact) error {
106106
return os.RemoveAll(dir)
107107
}
108108

109-
// RemoveAllButCurrent removes all files for the given v1beta1.Artifact base dir,
110-
// excluding the current one.
109+
// RemoveAllButCurrent removes all files for the given v1beta1.Artifact base dir, excluding the current one.
111110
func (s *Storage) RemoveAllButCurrent(artifact sourcev1.Artifact) error {
112111
localPath := s.LocalPath(artifact)
113112
dir := filepath.Dir(localPath)
@@ -132,8 +131,7 @@ func (s *Storage) RemoveAllButCurrent(artifact sourcev1.Artifact) error {
132131
return nil
133132
}
134133

135-
// ArtifactExist returns a boolean indicating whether the v1beta1.Artifact exists in storage
136-
// and is a regular file.
134+
// ArtifactExist returns a boolean indicating whether the v1beta1.Artifact exists in storage and is a regular file.
137135
func (s *Storage) ArtifactExist(artifact sourcev1.Artifact) bool {
138136
fi, err := os.Lstat(s.LocalPath(artifact))
139137
if err != nil {
@@ -142,14 +140,13 @@ func (s *Storage) ArtifactExist(artifact sourcev1.Artifact) bool {
142140
return fi.Mode().IsRegular()
143141
}
144142

145-
// ArchiveFileFilter must return true if a file should not be included
146-
// in the archive after inspecting the given path and/or os.FileInfo.
143+
// ArchiveFileFilter must return true if a file should not be included in the archive after inspecting the given path
144+
// and/or os.FileInfo.
147145
type ArchiveFileFilter func(p string, fi os.FileInfo) bool
148146

149-
// SourceIgnoreFilter returns an ArchiveFileFilter that filters out
150-
// files matching sourceignore.VCSPatterns and any of the provided
151-
// patterns. If an empty gitignore.Pattern slice is given, the matcher
152-
// is set to sourceignore.NewDefaultMatcher.
147+
// SourceIgnoreFilter returns an ArchiveFileFilter that filters out files matching sourceignore.VCSPatterns and any of
148+
// the provided patterns.
149+
// If an empty gitignore.Pattern slice is given, the matcher is set to sourceignore.NewDefaultMatcher.
153150
func SourceIgnoreFilter(ps []gitignore.Pattern, domain []string) ArchiveFileFilter {
154151
matcher := sourceignore.NewDefaultMatcher(ps, domain)
155152
if len(ps) > 0 {
@@ -163,10 +160,9 @@ func SourceIgnoreFilter(ps []gitignore.Pattern, domain []string) ArchiveFileFilt
163160
}
164161
}
165162

166-
// Archive atomically archives the given directory as a tarball to the
167-
// given v1beta1.Artifact path, excluding directories and any
168-
// ArchiveFileFilter matches. If successful, it sets the checksum and
169-
// last update time on the artifact.
163+
// Archive atomically archives the given directory as a tarball to the given v1beta1.Artifact path, excluding
164+
// directories and any ArchiveFileFilter matches.
165+
// If successful, it sets the checksum and last update time on the artifact.
170166
func (s *Storage) Archive(artifact *sourcev1.Artifact, dir string, filter ArchiveFileFilter) (err error) {
171167
if f, err := os.Stat(dir); os.IsNotExist(err) || !f.IsDir() {
172168
return fmt.Errorf("invalid dir path: %s", dir)
@@ -341,9 +337,8 @@ func (s *Storage) Copy(artifact *sourcev1.Artifact, reader io.Reader) (err error
341337
return nil
342338
}
343339

344-
// CopyFromPath atomically copies the contents of the given path to the path of
345-
// the v1beta1.Artifact. If successful, the checksum and last update time on the
346-
// artifact is set.
340+
// CopyFromPath atomically copies the contents of the given path to the path of the v1beta1.Artifact.
341+
// If successful, the checksum and last update time on the artifact is set.
347342
func (s *Storage) CopyFromPath(artifact *sourcev1.Artifact, path string) (err error) {
348343
f, err := os.Open(path)
349344
if err != nil {
@@ -353,10 +348,10 @@ func (s *Storage) CopyFromPath(artifact *sourcev1.Artifact, path string) (err er
353348
return s.Copy(artifact, f)
354349
}
355350

356-
// CopyToPath copies the contents of the given artifact to the path.
351+
// CopyToPath copies the contents in the (sub)path of the given artifact to the given path.
357352
func (s *Storage) CopyToPath(artifact *sourcev1.Artifact, subPath, toPath string) error {
358353
// create a tmp directory to store artifact
359-
tmp, err := os.MkdirTemp("", "flux-include")
354+
tmp, err := os.MkdirTemp("", "flux-include-")
360355
if err != nil {
361356
return err
362357
}
@@ -371,7 +366,7 @@ func (s *Storage) CopyToPath(artifact *sourcev1.Artifact, subPath, toPath string
371366
defer f.Close()
372367

373368
// untar the artifact
374-
untarPath := filepath.Join(tmp, "tar")
369+
untarPath := filepath.Join(tmp, "unpack")
375370
if _, err = untar.Untar(f, untarPath); err != nil {
376371
return err
377372
}
@@ -382,15 +377,17 @@ func (s *Storage) CopyToPath(artifact *sourcev1.Artifact, subPath, toPath string
382377
}
383378

384379
// copy the artifact content to the destination dir
385-
fromPath := filepath.Join(untarPath, subPath)
380+
fromPath, err := securejoin.SecureJoin(untarPath, subPath)
381+
if err != nil {
382+
return err
383+
}
386384
if err := fs.RenameWithFallback(fromPath, toPath); err != nil {
387385
return err
388386
}
389387
return nil
390388
}
391389

392-
// Symlink creates or updates a symbolic link for the given v1beta1.Artifact
393-
// and returns the URL for the symlink.
390+
// Symlink creates or updates a symbolic link for the given v1beta1.Artifact and returns the URL for the symlink.
394391
func (s *Storage) Symlink(artifact sourcev1.Artifact, linkName string) (string, error) {
395392
localPath := s.LocalPath(artifact)
396393
dir := filepath.Dir(localPath)
@@ -427,13 +424,16 @@ func (s *Storage) Lock(artifact sourcev1.Artifact) (unlock func(), err error) {
427424
return mutex.Lock()
428425
}
429426

430-
// LocalPath returns the local path of the given artifact (that is: relative to
431-
// the Storage.BasePath).
427+
// LocalPath returns the secure local path of the given artifact (that is: relative to the Storage.BasePath).
432428
func (s *Storage) LocalPath(artifact sourcev1.Artifact) string {
433429
if artifact.Path == "" {
434430
return ""
435431
}
436-
return filepath.Join(s.BasePath, artifact.Path)
432+
path, err := securejoin.SecureJoin(s.BasePath, artifact.Path)
433+
if err != nil {
434+
return ""
435+
}
436+
return path
437437
}
438438

439439
// newHash returns a new SHA1 hash.

0 commit comments

Comments
 (0)