Skip to content

Commit 39e0b13

Browse files
authored
Merge commit from fork
Reject any filename that is not a local relative path via filepath.IsLocal before opening the destination file. GHSA-mcj4-mphf-j9ff
1 parent 28d44d3 commit 39e0b13

2 files changed

Lines changed: 36 additions & 1 deletion

File tree

pkg/oci/artifact.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,12 @@ func (a *Artifact) Download(ctx context.Context, dir string, opt DownloadOption)
144144
fileName = v
145145
}
146146

147+
// Reject filenames that are not local relative paths to prevent path traversal
148+
// (e.g., "../foo", "/foo", or "C:\foo" on Windows).
149+
if !filepath.IsLocal(fileName) {
150+
return xerrors.Errorf("invalid filename %q: must be a local relative path", fileName)
151+
}
152+
147153
layerMediaType, err := layer.MediaType()
148154
if err != nil {
149155
return xerrors.Errorf("media type error: %w", err)

pkg/oci/artifact_test.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package oci_test
22

33
import (
4+
"cmp"
45
"errors"
56
"os"
67
"path/filepath"
@@ -42,6 +43,7 @@ func TestArtifact_Download(t *testing.T) {
4243
name string
4344
input string
4445
mediaType string
46+
annotation string
4547
layersReturns layersReturns
4648
want string
4749
wantErr string
@@ -90,6 +92,33 @@ func TestArtifact_Download(t *testing.T) {
9092
},
9193
wantErr: "unacceptable media type",
9294
},
95+
{
96+
name: "sad: filename with parent traversal",
97+
mediaType: "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip",
98+
annotation: "../../../etc/passwd",
99+
layersReturns: layersReturns{
100+
layers: []v1.Layer{flayer},
101+
},
102+
wantErr: `invalid filename "../../../etc/passwd"`,
103+
},
104+
{
105+
name: "sad: filename with absolute path",
106+
mediaType: "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip",
107+
annotation: "/etc/passwd",
108+
layersReturns: layersReturns{
109+
layers: []v1.Layer{flayer},
110+
},
111+
wantErr: `invalid filename "/etc/passwd"`,
112+
},
113+
{
114+
name: "sad: filename with subdirectory traversal",
115+
mediaType: "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip",
116+
annotation: "foo/../../bar",
117+
layersReturns: layersReturns{
118+
layers: []v1.Layer{flayer},
119+
},
120+
wantErr: `invalid filename "foo/../../bar"`,
121+
},
93122
}
94123

95124
for _, tt := range tests {
@@ -109,7 +138,7 @@ func TestArtifact_Download(t *testing.T) {
109138
Hex: "cba33656188782852f58993f45b68bfb8577f64cdcf02a604e3fc2afbeb5f2d8",
110139
},
111140
Annotations: map[string]string{
112-
"org.opencontainers.image.title": "bundle.tar.gz",
141+
"org.opencontainers.image.title": cmp.Or(tt.annotation, "bundle.tar.gz"),
113142
},
114143
},
115144
},

0 commit comments

Comments
 (0)