diff --git a/internal/store/store.go b/internal/store/store.go index 84283da36..2e04b884a 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -46,9 +46,14 @@ func (s *store) Namespace() string { } func (s *store) check(filename string) error { - if filename[:len(s.namespace)+1] != s.namespace+"/" { - return errors.Errorf("Invalid namespace when trying to store file: %s (for %s)", filename, s.namespace) + if len(filename) == 0 { + return errors.Errorf("Invalid filename when trying to store file: '%s' (for %s)", filename, s.namespace) } + + if filename[:len(s.namespace)+1] != s.namespace+"/" { + return errors.Errorf("Invalid namespace when trying to store file: '%s' (for %s)", filename, s.namespace) + } + return nil } diff --git a/internal/store/store_test.go b/internal/store/store_test.go index 838cac17f..7147ab15c 100644 --- a/internal/store/store_test.go +++ b/internal/store/store_test.go @@ -68,3 +68,14 @@ func TestStore(t *testing.T) { assert(err != nil, "Expected error when deleting file outside of namespace") } } + +func TestStoreCheckFunc(t *testing.T) { + assert := func(ok bool, format string, params ...interface{}) { + if !ok { + t.Fatalf(format, params...) + } + } + + // Should not cause panic + assert((&store{}).check("") != nil, "Expecting an error to be returned on empty filename check") +} diff --git a/sam/rest/attachment.go b/sam/rest/attachment.go index ea833832e..f05a52730 100644 --- a/sam/rest/attachment.go +++ b/sam/rest/attachment.go @@ -77,3 +77,7 @@ func (f *file) ModTime() time.Time { func (f *file) Content() io.ReadSeeker { return f.content } + +func (f *file) Valid() bool { + return f.content != nil +} diff --git a/sam/rest/handlers/attachment_custom.go b/sam/rest/handlers/attachment_custom.go index 9fd84606e..395eb86b2 100644 --- a/sam/rest/handlers/attachment_custom.go +++ b/sam/rest/handlers/attachment_custom.go @@ -1,12 +1,12 @@ package handlers import ( - "net/http" - - "github.com/crusttech/crust/sam/rest/request" "io" + "net/http" "net/url" "time" + + "github.com/crusttech/crust/sam/rest/request" ) // HTTP API interface @@ -20,6 +20,7 @@ type Downloadable interface { Download() bool ModTime() time.Time Content() io.ReadSeeker + Valid() bool } func NewAttachmentDownloadable(ah AttachmentAPI) *Attachment { @@ -32,11 +33,15 @@ func NewAttachmentDownloadable(ah AttachmentAPI) *Attachment { http.Error(w, err.Error(), http.StatusInternalServerError) } } else if dl, ok := f.(Downloadable); ok { - if dl.Download() { - w.Header().Add("Content-Disposition", "attachment; filename="+url.QueryEscape(dl.Name())) - } + if !dl.Valid() { + w.WriteHeader(http.StatusNotFound) + } else { + if dl.Download() { + w.Header().Add("Content-Disposition", "attachment; filename="+url.QueryEscape(dl.Name())) + } - http.ServeContent(w, r, dl.Name(), dl.ModTime(), dl.Content()) + http.ServeContent(w, r, dl.Name(), dl.ModTime(), dl.Content()) + } } else { http.Error(w, "Got incompatible type from controller", http.StatusInternalServerError) } diff --git a/sam/service/attachment.go b/sam/service/attachment.go index ad4fdeb78..95e36bbe7 100644 --- a/sam/service/attachment.go +++ b/sam/service/attachment.go @@ -67,10 +67,18 @@ func (svc *attachment) FindByID(id uint64) (*types.Attachment, error) { } func (svc *attachment) OpenOriginal(att *types.Attachment) (io.ReadSeeker, error) { + if len(att.Url) == 0 { + return nil, nil + } + return svc.store.Open(att.Url) } func (svc *attachment) OpenPreview(att *types.Attachment) (io.ReadSeeker, error) { + if len(att.PreviewUrl) == 0 { + return nil, nil + } + return svc.store.Open(att.PreviewUrl) }