Skip to content

Commit 188ca35

Browse files
authored
Merge pull request #1151 from frazew/pull-preserve-mode
oci: preserve dir mode when extracting tar layers
2 parents 503def1 + 84aa850 commit 188ca35

3 files changed

Lines changed: 84 additions & 2 deletions

File tree

oci/diff_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,40 @@ func TestClient_Diff(t *testing.T) {
6363
g.Expect(err).To(HaveOccurred())
6464
g.Expect(err).To(MatchError("the remote artifact contents differs from the local one"))
6565
}
66+
67+
func TestClient_PushPullDiff_RoundTrip(t *testing.T) {
68+
g := NewWithT(t)
69+
ctx := context.Background()
70+
c := NewClient(DefaultOptions())
71+
tag := "v0.0.1"
72+
repo := "test-push" + randStringRunes(5)
73+
74+
url := fmt.Sprintf("%s/%s:%s", dockerReg, repo, tag)
75+
metadata := Metadata{
76+
Source: "github.com/fluxcd/flux2",
77+
Revision: "rev",
78+
}
79+
80+
testDir := "testdata/artifact"
81+
_, err := c.Push(ctx, url, testDir, WithPushMetadata(metadata))
82+
g.Expect(err).ToNot(HaveOccurred())
83+
84+
err = c.Diff(ctx, url, testDir, nil)
85+
g.Expect(err).ToNot(HaveOccurred())
86+
87+
testDirStat, err := os.Stat(testDir)
88+
g.Expect(err).ToNot(HaveOccurred())
89+
90+
tmpPullDir, err := os.MkdirTemp("", "oci")
91+
g.Expect(err).ToNot(HaveOccurred())
92+
defer os.RemoveAll(tmpPullDir)
93+
94+
err = os.Chmod(tmpPullDir, testDirStat.Mode())
95+
g.Expect(err).ToNot(HaveOccurred())
96+
97+
_, err = c.Pull(ctx, url, tmpPullDir)
98+
g.Expect(err).ToNot(HaveOccurred())
99+
100+
err = c.Diff(ctx, url, tmpPullDir, nil)
101+
g.Expect(err).ToNot(HaveOccurred())
102+
}

tar/tar.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,12 @@ func Untar(r io.Reader, dir string, inOpts ...TarOption) (err error) {
177177
}
178178
}
179179
case mode.IsDir():
180-
if err := os.MkdirAll(abs, 0o750); err != nil {
180+
// Ensure the owner can always traverse, read, and write
181+
// into extracted directories, regardless of what the tar
182+
// header claims. This prevents crafted archives from
183+
// creating directories that block cleanup or future writes.
184+
dirPerm := mode.Perm() | 0o700
185+
if err := os.MkdirAll(abs, dirPerm); err != nil {
181186
return err
182187
}
183188
madeDir[abs] = true

tar/tar_test.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ type untarTestCase struct {
3535
content []byte
3636
wantErr string
3737
maxUntarSize int
38+
fileMode int64
3839
}
3940

4041
func TestUntar(t *testing.T) {
@@ -178,6 +179,40 @@ func TestUntar(t *testing.T) {
178179
}
179180
}
180181

182+
func TestUntarDirectoryPermissions(t *testing.T) {
183+
testDirName := "test-dir"
184+
185+
f, err := createTestTar(untarTestCase{
186+
fileName: testDirName + "/", // from tar.Header: a trailing slash makes the entry a TypeDir
187+
fileMode: 0o555,
188+
content: nil,
189+
})
190+
if err != nil {
191+
t.Fatalf("creating test tar: %v", err)
192+
}
193+
194+
targetDir := t.TempDir()
195+
196+
if err := Untar(f, targetDir); err != nil {
197+
t.Fatalf("untar: %v", err)
198+
}
199+
200+
fullPath := filepath.Join(targetDir, testDirName)
201+
fi, err := os.Lstat(fullPath)
202+
if err != nil {
203+
t.Errorf("stat %q: %v", fullPath, err)
204+
}
205+
206+
if !fi.Mode().IsDir() {
207+
t.Fatalf("%q: not a directory", fullPath)
208+
}
209+
210+
ownerPerm := fi.Mode().Perm() & 0o700
211+
if ownerPerm != 0o700 {
212+
t.Errorf("the owner must always be able to traverse, read, and write extracted directories")
213+
}
214+
}
215+
181216
func Fuzz_Untar(f *testing.F) {
182217
tf, err := createTestTar(untarTestCase{
183218
name: "file at root",
@@ -211,10 +246,15 @@ func createTestTar(tt untarTestCase) (*os.File, error) {
211246
gzw := gzip.NewWriter(f)
212247
writer := tar.NewWriter(gzw)
213248

249+
fileMode := tt.fileMode
250+
if fileMode == 0 {
251+
fileMode = 0o777
252+
}
253+
214254
writer.WriteHeader(&tar.Header{
215255
Name: tt.fileName,
216256
Size: int64(len(tt.content)),
217-
Mode: 0o777,
257+
Mode: fileMode,
218258
})
219259

220260
writer.Write(tt.content)

0 commit comments

Comments
 (0)