@@ -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.
8888func (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.
111110func (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.
137135func (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.
147145type 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.
153150func SourceIgnoreFilter (ps []gitignore.Pattern , domain []string ) ArchiveFileFilter {
154151 matcher := sourceignore .NewDefaultMatcher (ps , domain )
155152 if len (ps ) > 0 {
@@ -163,10 +160,10 @@ 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. While archiving, any environment specific data (for example,
165+ // the user and group name) is stripped from file headers.
166+ // If successful, it sets the checksum and last update time on the artifact.
170167func (s * Storage ) Archive (artifact * sourcev1.Artifact , dir string , filter ArchiveFileFilter ) (err error ) {
171168 if f , err := os .Stat (dir ); os .IsNotExist (err ) || ! f .IsDir () {
172169 return fmt .Errorf ("invalid dir path: %s" , dir )
@@ -220,6 +217,16 @@ func (s *Storage) Archive(artifact *sourcev1.Artifact, dir string, filter Archiv
220217 }
221218 header .Name = relFilePath
222219
220+ // We want to remove any environment specific data as well, this
221+ // ensures the checksum is purely content based.
222+ header .Gid = 0
223+ header .Uid = 0
224+ header .Uname = ""
225+ header .Gname = ""
226+ header .ModTime = time.Time {}
227+ header .AccessTime = time.Time {}
228+ header .ChangeTime = time.Time {}
229+
223230 if err := tw .WriteHeader (header ); err != nil {
224231 return err
225232 }
@@ -341,9 +348,8 @@ func (s *Storage) Copy(artifact *sourcev1.Artifact, reader io.Reader) (err error
341348 return nil
342349}
343350
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.
351+ // CopyFromPath atomically copies the contents of the given path to the path of the v1beta1.Artifact.
352+ // If successful, the checksum and last update time on the artifact is set.
347353func (s * Storage ) CopyFromPath (artifact * sourcev1.Artifact , path string ) (err error ) {
348354 f , err := os .Open (path )
349355 if err != nil {
@@ -353,10 +359,10 @@ func (s *Storage) CopyFromPath(artifact *sourcev1.Artifact, path string) (err er
353359 return s .Copy (artifact , f )
354360}
355361
356- // CopyToPath copies the contents of the given artifact to the path.
362+ // CopyToPath copies the contents in the (sub)path of the given artifact to the given path.
357363func (s * Storage ) CopyToPath (artifact * sourcev1.Artifact , subPath , toPath string ) error {
358364 // create a tmp directory to store artifact
359- tmp , err := os .MkdirTemp ("" , "flux-include" )
365+ tmp , err := os .MkdirTemp ("" , "flux-include- " )
360366 if err != nil {
361367 return err
362368 }
@@ -371,7 +377,7 @@ func (s *Storage) CopyToPath(artifact *sourcev1.Artifact, subPath, toPath string
371377 defer f .Close ()
372378
373379 // untar the artifact
374- untarPath := filepath .Join (tmp , "tar " )
380+ untarPath := filepath .Join (tmp , "unpack " )
375381 if _ , err = untar .Untar (f , untarPath ); err != nil {
376382 return err
377383 }
@@ -382,15 +388,17 @@ func (s *Storage) CopyToPath(artifact *sourcev1.Artifact, subPath, toPath string
382388 }
383389
384390 // copy the artifact content to the destination dir
385- fromPath := filepath .Join (untarPath , subPath )
391+ fromPath , err := securejoin .SecureJoin (untarPath , subPath )
392+ if err != nil {
393+ return err
394+ }
386395 if err := fs .RenameWithFallback (fromPath , toPath ); err != nil {
387396 return err
388397 }
389398 return nil
390399}
391400
392- // Symlink creates or updates a symbolic link for the given v1beta1.Artifact
393- // and returns the URL for the symlink.
401+ // Symlink creates or updates a symbolic link for the given v1beta1.Artifact and returns the URL for the symlink.
394402func (s * Storage ) Symlink (artifact sourcev1.Artifact , linkName string ) (string , error ) {
395403 localPath := s .LocalPath (artifact )
396404 dir := filepath .Dir (localPath )
@@ -427,13 +435,16 @@ func (s *Storage) Lock(artifact sourcev1.Artifact) (unlock func(), err error) {
427435 return mutex .Lock ()
428436}
429437
430- // LocalPath returns the local path of the given artifact (that is: relative to
431- // the Storage.BasePath).
438+ // LocalPath returns the secure local path of the given artifact (that is: relative to the Storage.BasePath).
432439func (s * Storage ) LocalPath (artifact sourcev1.Artifact ) string {
433440 if artifact .Path == "" {
434441 return ""
435442 }
436- return filepath .Join (s .BasePath , artifact .Path )
443+ path , err := securejoin .SecureJoin (s .BasePath , artifact .Path )
444+ if err != nil {
445+ return ""
446+ }
447+ return path
437448}
438449
439450// newHash returns a new SHA1 hash.
0 commit comments