mirror of
https://github.com/labstack/echo.git
synced 2024-12-20 19:52:47 +02:00
Security: c.Attachment and c.Inline should escape name in Content-Disposition
header to avoid 'Reflect File Download' vulnerability. (#2541)
This is same as Go std does it 9d836d41d0/src/mime/multipart/writer.go (L132)
This commit is contained in:
parent
50ebcd8d7c
commit
14daeb9680
@ -584,8 +584,10 @@ func (c *context) Inline(file, name string) error {
|
||||
return c.contentDisposition(file, name, "inline")
|
||||
}
|
||||
|
||||
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
|
||||
|
||||
func (c *context) contentDisposition(file, name, dispositionType string) error {
|
||||
c.response.Header().Set(HeaderContentDisposition, fmt.Sprintf("%s; filename=%q", dispositionType, name))
|
||||
c.response.Header().Set(HeaderContentDisposition, fmt.Sprintf(`%s; filename="%s"`, dispositionType, quoteEscaper.Replace(name)))
|
||||
return c.File(file)
|
||||
}
|
||||
|
||||
|
@ -414,31 +414,73 @@ func TestContextStream(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestContextAttachment(t *testing.T) {
|
||||
var testCases = []struct {
|
||||
name string
|
||||
whenName string
|
||||
expectHeader string
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
whenName: "walle.png",
|
||||
expectHeader: `attachment; filename="walle.png"`,
|
||||
},
|
||||
{
|
||||
name: "ok, escape quotes in malicious filename",
|
||||
whenName: `malicious.sh"; \"; dummy=.txt`,
|
||||
expectHeader: `attachment; filename="malicious.sh\"; \\\"; dummy=.txt"`,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
e := New()
|
||||
rec := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodGet, "/?pretty", nil)
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
c := e.NewContext(req, rec).(*context)
|
||||
|
||||
err := c.Attachment("_fixture/images/walle.png", "walle.png")
|
||||
err := c.Attachment("_fixture/images/walle.png", tc.whenName)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, tc.expectHeader, rec.Header().Get(HeaderContentDisposition))
|
||||
|
||||
assert.Equal(t, http.StatusOK, rec.Code)
|
||||
assert.Equal(t, "attachment; filename=\"walle.png\"", rec.Header().Get(HeaderContentDisposition))
|
||||
assert.Equal(t, 219885, rec.Body.Len())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextInline(t *testing.T) {
|
||||
var testCases = []struct {
|
||||
name string
|
||||
whenName string
|
||||
expectHeader string
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
whenName: "walle.png",
|
||||
expectHeader: `inline; filename="walle.png"`,
|
||||
},
|
||||
{
|
||||
name: "ok, escape quotes in malicious filename",
|
||||
whenName: `malicious.sh"; \"; dummy=.txt`,
|
||||
expectHeader: `inline; filename="malicious.sh\"; \\\"; dummy=.txt"`,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
e := New()
|
||||
rec := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodGet, "/?pretty", nil)
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
c := e.NewContext(req, rec).(*context)
|
||||
|
||||
err := c.Inline("_fixture/images/walle.png", "walle.png")
|
||||
err := c.Inline("_fixture/images/walle.png", tc.whenName)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, tc.expectHeader, rec.Header().Get(HeaderContentDisposition))
|
||||
|
||||
assert.Equal(t, http.StatusOK, rec.Code)
|
||||
assert.Equal(t, "inline; filename=\"walle.png\"", rec.Header().Get(HeaderContentDisposition))
|
||||
assert.Equal(t, 219885, rec.Body.Len())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextNoContent(t *testing.T) {
|
||||
|
Loading…
Reference in New Issue
Block a user