Codebase list golang-github-hashicorp-go-slug / 39c00eb
New upstream version 0.9.1 Mathias Gibbens 1 year, 9 months ago
6 changed file(s) with 295 addition(s) and 135 deletion(s). Raw diff Collapse all Expand all
192192 header.Size = info.Size()
193193
194194 case fm&os.ModeSymlink != 0:
195 target, err := filepath.EvalSymlinks(path)
195 // First read the symlink file to find the destination.
196 target, err := os.Readlink(path)
196197 if err != nil {
197198 return fmt.Errorf("failed to get symbolic link destination for %q: %w", path, err)
198199 }
200
201 // Try to make absolute paths relative.
202 if filepath.IsAbs(target) {
203 absPath, err := filepath.Abs(path)
204 if err != nil {
205 return fmt.Errorf("failed to get absolute path for %q: %w", path, err)
206 }
207 absDir := filepath.Dir(absPath)
208
209 rel, err := filepath.Rel(absDir, target)
210 if err != nil {
211 return fmt.Errorf("failed to find relative path for %q: %w", target, err)
212 }
213 target = rel
214 }
215
216 // Get the path to the target relative to path.
217 target = filepath.Join(filepath.Dir(path), target)
199218
200219 // If the target is within the current source, we
201220 // create the symlink using a relative path.
214233 }
215234
216235 if !dereference {
217 // Return early as the symlink has a target outside of the
218 // src directory and we don't want to dereference symlinks.
219 return nil
236 // Symlinks with targets outside of src are prohibited.
237 return &IllegalSlugError{
238 Err: fmt.Errorf(
239 "invalid symlink (%q -> %q) has target outside of %q",
240 path, target, src,
241 ),
242 }
220243 }
221244
222245 // Get the file info for the target.
385408 }
386409
387410 // Only unpack regular files from this point on.
388 if header.Typeflag == tar.TypeDir {
411 if header.Typeflag == tar.TypeDir || header.Typeflag == tar.TypeXGlobalHeader || header.Typeflag == tar.TypeXHeader {
389412 continue
390413 } else if header.Typeflag != tar.TypeReg && header.Typeflag != tar.TypeRegA {
391414 return fmt.Errorf("failed creating %q: unsupported type %c", path,
44 "bytes"
55 "compress/gzip"
66 "errors"
7 "fmt"
78 "io"
89 "io/ioutil"
910 "os"
137138 }
138139 }
139140
140 func TestPackWithDereferencing(t *testing.T) {
141 func TestPackWithoutIgnoring(t *testing.T) {
141142 slug := bytes.NewBuffer(nil)
142143
143 meta, err := Pack("testdata/archive-dir", slug, true)
144 // By default NewPacker() creates a Packer that does not use
145 // .terraformignore or dereference symlinks.
146 p, err := NewPacker()
147 if err != nil {
148 t.Fatalf("err: %v", err)
149 }
150
151 meta, err := p.Pack("testdata/archive-dir", slug)
144152 if err != nil {
145153 t.Fatalf("err: %v", err)
146154 }
169177 if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
170178 slugSize += hdr.Size
171179 }
172
173 if hdr.Name == "foo.txt" {
174 if hdr.Typeflag != tar.TypeReg {
175 t.Fatalf("expect symlink 'foo.txt' to be dereferenced")
176 }
177 }
178 }
179
180 // Check the metadata
181 expect := &Meta{
182 Files: fileList,
183 Size: slugSize,
184 }
185 if !reflect.DeepEqual(meta, expect) {
186 t.Fatalf("\nexpect:\n%#v\n\nactual:\n%#v", expect, meta)
187 }
188 }
189
190 func TestPackWithoutDereferencing(t *testing.T) {
191 slug := bytes.NewBuffer(nil)
192
193 meta, err := Pack("testdata/archive-dir", slug, false)
194 if err != nil {
195 t.Fatalf("err: %v", err)
196 }
197
198 gzipR, err := gzip.NewReader(slug)
199 if err != nil {
200 t.Fatalf("err: %v", err)
201 }
202
203 tarR := tar.NewReader(gzipR)
204 var (
205 fileList []string
206 slugSize int64
207 )
208
209 for {
210 hdr, err := tarR.Next()
211 if err == io.EOF {
212 break
213 }
214 if err != nil {
215 t.Fatalf("err: %v", err)
216 }
217
218 fileList = append(fileList, hdr.Name)
219 if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
220 slugSize += hdr.Size
221 }
222 }
223
224 // Make sure the the foo.txt symlink is not dereferenced
225 // but is indeed ignored and not added to the archive.
226 for _, file := range fileList {
227 if file == "foo.txt" {
228 t.Fatalf("unexpected dereferenced symlink: %s", file)
229 }
230 }
231
232 // Check the metadata
233 expect := &Meta{
234 Files: fileList,
235 Size: slugSize,
236 }
237 if !reflect.DeepEqual(meta, expect) {
238 t.Fatalf("\nexpect:\n%#v\n\nactual:\n%#v", expect, meta)
239 }
240 }
241
242 func TestPackWithoutIgnoring(t *testing.T) {
243 slug := bytes.NewBuffer(nil)
244
245 // By default NewPacker() creates a Packer that does not use
246 // .terraformignore or dereference symlinks.
247 p, err := NewPacker()
248 if err != nil {
249 t.Fatalf("err: %v", err)
250 }
251
252 meta, err := p.Pack("testdata/archive-dir", slug)
253 if err != nil {
254 t.Fatalf("err: %v", err)
255 }
256
257 gzipR, err := gzip.NewReader(slug)
258 if err != nil {
259 t.Fatalf("err: %v", err)
260 }
261
262 tarR := tar.NewReader(gzipR)
263 var (
264 fileList []string
265 slugSize int64
266 )
267
268 for {
269 hdr, err := tarR.Next()
270 if err == io.EOF {
271 break
272 }
273 if err != nil {
274 t.Fatalf("err: %v", err)
275 }
276
277 fileList = append(fileList, hdr.Name)
278 if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
279 slugSize += hdr.Size
280 }
281180 }
282181
283182 // baz.txt would normally be ignored, but should not be
312211 }
313212 }
314213
214 func TestPack_symlinks(t *testing.T) {
215 type tcase struct {
216 absolute bool
217 external bool
218 targetExists bool
219 dereference bool
220 }
221
222 var tcases []tcase
223 for _, absolute := range []bool{true, false} {
224 for _, external := range []bool{true, false} {
225 for _, targetExists := range []bool{true, false} {
226 for _, dereference := range []bool{true, false} {
227 tcases = append(tcases, tcase{
228 absolute: absolute,
229 external: external,
230 targetExists: targetExists,
231 dereference: dereference,
232 })
233 }
234 }
235 }
236 }
237
238 for _, tc := range tcases {
239 desc := fmt.Sprintf(
240 "absolute:%v external:%v targetExists:%v dereference:%v",
241 tc.absolute, tc.external, tc.targetExists, tc.dereference)
242
243 t.Run(desc, func(t *testing.T) {
244 td, err := ioutil.TempDir("", "go-slug")
245 if err != nil {
246 t.Fatal(err)
247 }
248 defer os.RemoveAll(td)
249
250 internal := filepath.Join(td, "internal")
251 if err := os.MkdirAll(internal, 0700); err != nil {
252 t.Fatal(err)
253 }
254
255 external := filepath.Join(td, "external")
256 if err := os.MkdirAll(external, 0700); err != nil {
257 t.Fatal(err)
258 }
259
260 // Make the symlink in a subdirectory. This will help ensure that
261 // a proper relative link gets created, even when the relative
262 // path requires upward directory traversal.
263 symPath := filepath.Join(internal, "sub", "sym")
264 if err := os.MkdirAll(filepath.Join(internal, "sub"), 0700); err != nil {
265 t.Fatal(err)
266 }
267
268 // Get an absolute path within the temp dir and an absolute target.
269 // We place the target into a subdir to ensure the link is created
270 // properly within a nested structure.
271 var targetPath string
272 if tc.external {
273 targetPath = filepath.Join(external, "foo", "bar")
274 } else {
275 targetPath = filepath.Join(internal, "foo", "bar")
276 }
277
278 if tc.targetExists {
279 if err := os.MkdirAll(filepath.Dir(targetPath), 0700); err != nil {
280 t.Fatal(err)
281 }
282 if err := ioutil.WriteFile(targetPath, []byte("foo"), 0644); err != nil {
283 t.Fatal(err)
284 }
285 }
286
287 if !tc.absolute {
288 targetPath, err = filepath.Rel(filepath.Dir(symPath), targetPath)
289 if err != nil {
290 t.Fatal(err)
291 }
292 }
293
294 // Make the symlink.
295 if err := os.Symlink(targetPath, symPath); err != nil {
296 t.Fatal(err)
297 }
298
299 var expectErr string
300 if tc.external && !tc.dereference {
301 expectErr = "target outside"
302 }
303 if tc.external && tc.dereference && !tc.targetExists {
304 expectErr = "no such file or directory"
305 }
306
307 var expectTypeflag byte
308 if tc.external && tc.dereference {
309 expectTypeflag = tar.TypeReg
310 } else {
311 expectTypeflag = tar.TypeSymlink
312 }
313
314 // Pack up the temp dir.
315 slug := bytes.NewBuffer(nil)
316 _, err = Pack(internal, slug, tc.dereference)
317 if expectErr != "" {
318 if err != nil {
319 if strings.Contains(err.Error(), expectErr) {
320 return
321 }
322 t.Fatalf("expected error %q, got %v", expectErr, err)
323 }
324 t.Fatal("expected error, got nil")
325 } else if err != nil {
326 t.Fatal(err)
327 }
328
329 // Inspect the result.
330 gzipR, err := gzip.NewReader(slug)
331 if err != nil {
332 t.Fatalf("err: %v", err)
333 }
334 tarR := tar.NewReader(gzipR)
335
336 symFound := false
337 for {
338 hdr, err := tarR.Next()
339 if err == io.EOF {
340 break
341 }
342 if err != nil {
343 t.Fatalf("err: %v", err)
344 }
345 if hdr.Name == "sub/sym" {
346 symFound = true
347 if hdr.Typeflag != expectTypeflag {
348 t.Fatalf("unexpected file type in slug: %q", hdr.Typeflag)
349 }
350 if expectTypeflag == tar.TypeSymlink && hdr.Linkname != "../foo/bar" {
351 t.Fatalf("unexpected link target in slug: %q", hdr.Linkname)
352 }
353 }
354 }
355
356 if !symFound {
357 t.Fatal("did not find symlink in archive")
358 }
359 })
360 }
361 }
362
315363 func TestUnpack(t *testing.T) {
316364 // First create the slug file so we can try to unpack it.
317365 slug := bytes.NewBuffer(nil)
333381 }
334382
335383 // Verify all the files
336 verifyFile(t, filepath.Join(dst, "foo.txt"), 0, "foo\n")
337384 verifyFile(t, filepath.Join(dst, "bar.txt"), 0, "bar\n")
338385 verifyFile(t, filepath.Join(dst, "sub", "bar.txt"), os.ModeSymlink, "../bar.txt")
339386 verifyFile(t, filepath.Join(dst, "sub", "zip.txt"), 0, "zip\n")
340387
341388 // Check that we can set permissions properly
342 verifyPerms(t, filepath.Join(dst, "foo.txt"), 0644)
343389 verifyPerms(t, filepath.Join(dst, "bar.txt"), 0644)
344390 verifyPerms(t, filepath.Join(dst, "sub", "zip.txt"), 0644)
345391 verifyPerms(t, filepath.Join(dst, "sub", "bar.txt"), 0644)
408454
409455 // Check that we can set permissions properly
410456 verifyPerms(t, filepath.Join(dst, "a"), 0400)
457 }
458
459 func TestUnpackPaxHeaders(t *testing.T) {
460 tcases := []struct {
461 desc string
462 headers []*tar.Header
463 }{
464 {
465 desc: "extended pax header",
466 headers: []*tar.Header{
467 &tar.Header{
468 Name: "h",
469 Typeflag: tar.TypeXHeader,
470 },
471 },
472 },
473 {
474 desc: "global pax header",
475 headers: []*tar.Header{
476 &tar.Header{
477 Name: "h",
478 Typeflag: tar.TypeXGlobalHeader,
479 },
480 },
481 },
482 }
483
484 for _, tc := range tcases {
485 t.Run(tc.desc, func(t *testing.T) {
486 dir, err := ioutil.TempDir("", "slug")
487 if err != nil {
488 t.Fatalf("err:%v", err)
489 }
490 defer os.RemoveAll(dir)
491 in := filepath.Join(dir, "slug.tar.gz")
492
493 // Create the output file
494 wfh, err := os.Create(in)
495 if err != nil {
496 t.Fatalf("err: %v", err)
497 }
498
499 // Gzip compress all the output data
500 gzipW := gzip.NewWriter(wfh)
501
502 // Tar the file contents
503 tarW := tar.NewWriter(gzipW)
504
505 for _, hdr := range tc.headers {
506 tarW.WriteHeader(hdr)
507 }
508
509 tarW.Close()
510 gzipW.Close()
511 wfh.Close()
512
513 // Open the slug file for reading.
514 fh, err := os.Open(in)
515 if err != nil {
516 t.Fatalf("err: %v", err)
517 }
518
519 // Create a dir to unpack into.
520 dst, err := ioutil.TempDir(dir, "")
521 if err != nil {
522 t.Fatalf("err: %v", err)
523 }
524 defer os.RemoveAll(dst)
525
526 // Now try unpacking it.
527 if err := Unpack(fh, dst); err != nil {
528 t.Fatalf("err: %v", err)
529 }
530
531 // Verify no file was created.
532 path := filepath.Join(dst, "h")
533 fh, err = os.Open(path)
534 if err == nil {
535 t.Fatalf("expected file not to exist: %q", path)
536 }
537 defer fh.Close()
538 })
539 }
411540 }
412541
413542 // ensure Unpack returns an error when an unsupported file type is encountered
769898 }
770899
771900 func verifyFile(t *testing.T, path string, mode os.FileMode, expect string) {
772 fh, err := os.Open(path)
901 info, err := os.Lstat(path)
773902 if err != nil {
774903 t.Fatal(err)
775904 }
776 defer fh.Close()
777
778 info, err := fh.Stat()
779 if err != nil {
780 t.Fatal(err)
781 }
905
906 if info.Mode()&os.ModeSymlink != 0 {
907 if mode == os.ModeSymlink {
908 if target, _ := os.Readlink(path); target != expect {
909 t.Fatalf("expect link target %q, got %q", expect, target)
910 }
911 return
912 } else {
913 t.Fatalf("found symlink, expected %v", mode)
914 }
915 }
916
782917 if !((mode == 0 && info.Mode().IsRegular()) || info.Mode()&mode == 0) {
783918 t.Fatalf("wrong file mode for %q", path)
784919 }
785920
786 if mode == os.ModeSymlink {
787 if target, _ := os.Readlink(path); target != expect {
788 t.Fatalf("expect link target %q, got %q", expect, target)
789 }
790 return
791 }
921 fh, err := os.Open(path)
922 if err != nil {
923 t.Fatal(err)
924 }
925 defer fh.Close()
792926
793927 raw := make([]byte, info.Size())
794928 if _, err := fh.Read(raw); err != nil {
191191 .git/
192192 .terraform/
193193 !.terraform/modules/
194 terraform.tfstate
194195 */
195196
196197 var defaultExclusions = []rule{
205206 {
206207 val: filepath.Join("**", ".terraform", "modules", "**"),
207208 excluded: true,
209 },
210 {
211 val: filepath.Join("**", "terraform.tfstate"),
212 excluded: false,
208213 },
209214 }
210215
55
66 func TestTerraformIgnore(t *testing.T) {
77 // path to directory without .terraformignore
8 p := parseIgnoreFile("testdata/external-dir")
9 if len(p) != 3 {
8 p := parseIgnoreFile("testdata")
9 if len(p) != 4 {
1010 t.Fatal("A directory without .terraformignore should get the default patterns")
1111 }
1212
+0
-1
testdata/archive-dir/foo.txt less more
0 ../external-dir/foo.txt
+0
-1
testdata/external-dir/foo.txt less more
0 foo