Skip to content

Commit 3436595

Browse files
committed
zap.Open: Invalidate relative path roots
Add validation to ensure file schema paths passed to zap.Open are absolute since this is already documented. Tests are also added to demonstrate that double dot segements within file schema URIs passed to zap.Open remaining within the specified file hierarchy. This change addresses https://cwe.mitre.org/data/definitions/23.html ref #1390
1 parent d27427d commit 3436595

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed

sink.go

+5
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ func (sr *sinkRegistry) newSink(rawURL string) (Sink, error) {
103103
if err != nil {
104104
return nil, fmt.Errorf("can't parse %q as a URL: %v", rawURL, err)
105105
}
106+
107+
if u.Scheme == schemeFile && !filepath.IsAbs(u.Path) {
108+
return nil, fmt.Errorf("file URI %q attempts a relative path", rawURL)
109+
}
110+
106111
if u.Scheme == "" {
107112
u.Scheme = schemeFile
108113
}

writer_test.go

+79
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,85 @@ func TestOpenOtherErrors(t *testing.T) {
224224
}
225225
}
226226

227+
func TestOpenRelativeValidated(t *testing.T) {
228+
tests := []struct {
229+
msg string
230+
paths []string
231+
wantErr string
232+
}{
233+
{
234+
msg: "invalid relative path root",
235+
paths: []string{
236+
"file:../some/path",
237+
},
238+
// url.Parse's Path for this path value is "" which would result
239+
// in a file not found error if not validated.
240+
wantErr: `open sink "file:../some/path": file URI "file:../some/path" attempts a relative path`,
241+
},
242+
{
243+
msg: "invalid double dot as the host element",
244+
paths: []string{
245+
"file://../some/path",
246+
},
247+
wantErr: `open sink "file://../some/path": file URLs must leave host empty or use localhost: got file://../some/path`,
248+
},
249+
}
250+
251+
for _, tt := range tests {
252+
t.Run(tt.msg, func(t *testing.T) {
253+
_, _, err := Open(tt.paths...)
254+
assert.EqualError(t, err, tt.wantErr)
255+
})
256+
}
257+
}
258+
259+
func TestOpenDotSegmentsSanitized(t *testing.T) {
260+
tempName := filepath.Join(t.TempDir(), "test.log")
261+
assert.False(t, fileExists(tempName))
262+
require.True(t, filepath.IsAbs(tempName), "Expected absolute temp file path.")
263+
264+
tests := []struct {
265+
paths []string
266+
toWrite []byte
267+
wantFileContents string
268+
}{
269+
{
270+
paths: []string{"file:/.." + tempName},
271+
toWrite: []byte("a"),
272+
wantFileContents: "a",
273+
},
274+
{
275+
paths: []string{"file:/../.." + tempName},
276+
toWrite: []byte("b"),
277+
wantFileContents: "ab",
278+
},
279+
{
280+
paths: []string{"file:///.." + tempName},
281+
toWrite: []byte("c"),
282+
wantFileContents: "abc",
283+
},
284+
{
285+
paths: []string{"file:///../.." + tempName},
286+
toWrite: []byte("d"),
287+
wantFileContents: "abcd",
288+
},
289+
}
290+
291+
for _, tt := range tests {
292+
ws, cleanup, err := Open(tt.paths...)
293+
require.NoError(t, err)
294+
defer cleanup()
295+
296+
_, err = ws.Write(tt.toWrite)
297+
require.NoError(t, err)
298+
299+
b, err := os.ReadFile(tempName)
300+
require.NoError(t, err)
301+
302+
assert.Equal(t, string(b), tt.wantFileContents)
303+
}
304+
}
305+
227306
type testWriter struct {
228307
expected string
229308
t testing.TB

0 commit comments

Comments
 (0)