Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data Integrity #3

Merged
merged 4 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 45 additions & 6 deletions api/r0/upload_async.go
Original file line number Diff line number Diff line change
@@ -1,42 +1,81 @@
package r0

import (
"bytes"
"errors"
"io"
"net/http"
"path/filepath"

"github.com/getsentry/sentry-go"
"github.com/h2non/filetype"
"github.com/sirupsen/logrus"
"github.com/t2bot/matrix-media-repo/api/_apimeta"
"github.com/t2bot/matrix-media-repo/api/_responses"
"github.com/t2bot/matrix-media-repo/api/_routers"
"github.com/t2bot/matrix-media-repo/common"
"github.com/t2bot/matrix-media-repo/common/rcontext"
"github.com/t2bot/matrix-media-repo/pipelines/pipeline_upload"
"github.com/t2bot/matrix-media-repo/util"
)

func UploadMediaAsync(r *http.Request, rctx rcontext.RequestContext, user _apimeta.UserInfo) interface{} {
server := _routers.GetParam("server", r)
mediaId := _routers.GetParam("mediaId", r)
filename := filepath.Base(r.URL.Query().Get("filename"))

rctx = rctx.LogWithFields(logrus.Fields{
"mediaId": mediaId,
"server": server,
"filename": filename,
})

if r.Host != server {
// GK CUSTOMIZATION: Sanitize the filename
if len(filename) > rctx.Config.Uploads.MaxFilenameLength {
rctx.Log.Info("Filename too long")
return &_responses.ErrorResponse{
Code: common.ErrCodeNotFound,
Message: "Upload request is for another domain.",
InternalCode: common.ErrCodeForbidden,
Code: common.ErrCodeBadRequest,
Message: "Filename too long.",
InternalCode: common.ErrCodeBadRequest,
}
}

contentType := r.Header.Get("Content-Type")
if contentType == "" {
contentType = "application/octet-stream" // binary
} else {
// GK CUSTOMIZATION: Check if the file type is supported
buf, err := io.ReadAll(r.Body)
r.Body = io.NopCloser(bytes.NewBuffer(buf))
if err != nil {
return &_responses.ErrorResponse{
Code: common.ErrCodeBadRequest,
Message: "Error reading file.",
InternalCode: common.ErrCodeBadRequest,
}
}
kind, err := filetype.Match(buf)
if err != nil {
return &_responses.ErrorResponse{
Code: common.ErrCodeBadRequest,
Message: "Error matching file type.",
InternalCode: common.ErrCodeBadRequest,
}
}
if !util.IsSupportedFileType(kind.Extension, rctx.Config.Uploads.SupportedFileTypes) {
rctx.Log.Info("Unsupported file type: ", kind.Extension)
return &_responses.ErrorResponse{
Code: common.ErrCodeBadRequest,
Message: "Unsupported file type.",
InternalCode: common.ErrCodeBadRequest,
}
}
}

if r.Host != server {
return &_responses.ErrorResponse{
Code: common.ErrCodeNotFound,
Message: "Upload request is for another domain.",
InternalCode: common.ErrCodeForbidden,
}
}

// Early sizing constraints (reject requests which claim to be too large/small)
Expand Down
41 changes: 40 additions & 1 deletion api/r0/upload_sync.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package r0

import (
"bytes"
"errors"
"io"
"net/http"
"path/filepath"
"strconv"

"github.com/getsentry/sentry-go"
"github.com/h2non/filetype"
"github.com/sirupsen/logrus"
"github.com/t2bot/matrix-media-repo/api/_apimeta"
"github.com/t2bot/matrix-media-repo/api/_responses"
Expand All @@ -23,14 +26,50 @@ type MediaUploadedResponse struct {

func UploadMediaSync(r *http.Request, rctx rcontext.RequestContext, user _apimeta.UserInfo) interface{} {
filename := filepath.Base(r.URL.Query().Get("filename"))

rctx = rctx.LogWithFields(logrus.Fields{
"filename": filename,
})
// GK CUSTOMIZATION: Sanitize the filename
if len(filename) > rctx.Config.Uploads.MaxFilenameLength {
rctx.Log.Info("Filename too long")
return &_responses.ErrorResponse{
Code: common.ErrCodeBadRequest,
Message: "Filename too long.",
InternalCode: common.ErrCodeBadRequest,
}
}

contentType := r.Header.Get("Content-Type")
if contentType == "" {
contentType = "application/octet-stream" // binary
} else {
// GK CUSTOMIZATION: Check if the file type is supported
buf, err := io.ReadAll(r.Body)
r.Body = io.NopCloser(bytes.NewBuffer(buf))
if err != nil {
return &_responses.ErrorResponse{
Code: common.ErrCodeBadRequest,
Message: "Error reading file.",
InternalCode: common.ErrCodeBadRequest,
}
}
kind, err := filetype.Match(buf)
if err != nil {
return &_responses.ErrorResponse{
Code: common.ErrCodeBadRequest,
Message: "Error matching file type.",
InternalCode: common.ErrCodeBadRequest,
}
}
if !util.IsSupportedFileType(kind.Extension, rctx.Config.Uploads.SupportedFileTypes) {
rctx.Log.Info("Unsupported file type: ", kind.Extension)
return &_responses.ErrorResponse{
Code: common.ErrCodeBadRequest,
Message: "Unsupported file type.",
InternalCode: common.ErrCodeBadRequest,
}
}
//
}

// Early sizing constraints (reject requests which claim to be too large/small)
Expand Down
2 changes: 1 addition & 1 deletion api/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func buildRoutes() http.Handler {
// Standard (spec) features
register([]string{"PUT"}, PrefixMedia, "upload/:server/:mediaId", mxV3, router, makeRoute(_routers.RequireAccessToken(r0.UploadMediaAsync), "upload_async", counter))
register([]string{"POST"}, PrefixMedia, "upload", mxSpecV3Transition, router, makeRoute(_routers.RequireAccessToken(r0.UploadMediaSync), "upload", counter))
downloadRoute := makeRoute(_routers.OptionalAccessToken(r0.DownloadMedia), "download", counter)
downloadRoute := makeRoute(_routers.RequireAccessToken(r0.DownloadMedia), "download", counter)
register([]string{"GET"}, PrefixMedia, "download/:server/:mediaId/:filename", mxSpecV3Transition, router, downloadRoute)
register([]string{"GET"}, PrefixMedia, "download/:server/:mediaId", mxSpecV3Transition, router, downloadRoute)
register([]string{"GET"}, PrefixMedia, "thumbnail/:server/:mediaId", mxSpecV3Transition, router, makeRoute(_routers.OptionalAccessToken(r0.ThumbnailMedia), "thumbnail", counter))
Expand Down
34 changes: 34 additions & 0 deletions common/config/conf_min_shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,40 @@ func NewDefaultMinimumRepoConfig() MinimumRepoConfig {
TargetBytesPerPart: 209715200, // 200mb
},
Uploads: UploadsConfig{
MaxFilenameLength: 24,
SupportedFileTypes: []string{
"docx",
"doc",
"xlsx",
"xls",
"odt",
"ods",
"odp",
"csv",
"txt",
"pdf",
"ppr",
"pptx",
"ppt",
"jpg",
"png",
"gif",
"heic",
"bmp",
"webp",
"svg",
"mpeg4",
"mpeg",
"h264",
"webm",
"mkv",
"avi",
"mov",
"mp3",
"wav",
"flac",
"zip",
},
MaxSizeBytes: 104857600, // 100mb
MinSizeBytes: 100,
ReportedMaxSizeBytes: 0,
Expand Down
2 changes: 2 additions & 0 deletions common/config/models_domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type QuotasConfig struct {
}

type UploadsConfig struct {
MaxFilenameLength int `yaml:"maxFilenameLength"`
SupportedFileTypes []string `yaml:"supportedFileTypes"`
MaxSizeBytes int64 `yaml:"maxBytes"`
MinSizeBytes int64 `yaml:"minBytes"`
ReportedMaxSizeBytes int64 `yaml:"reportedMaxBytes"`
Expand Down
36 changes: 36 additions & 0 deletions config.sample.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,42 @@ archiving:

# The file upload settings for the media repository
uploads:
# GK CUSTOMIZATION
maxFilenameLength: 24
supportedFileTypes:
- "docx"
- "doc"
- "xlsx"
- "xls"
- "odt"
- "ods"
- "odp"
- "csv"
- "txt"
- "pdf"
- "ppr"
- "pptx"
- "ppt"
- "jpg"
- "png"
- "gif"
- "heic"
- "bmp"
- "webp"
- "svg"
- "mpeg4"
- "mpeg"
- "h264"
- "webm"
- "mkv"
- "avi"
- "mov"
- "mp3"
- "wav"
- "flac"
- "zip"
##################

# The maximum individual file size a user can upload.
maxBytes: 104857600 # 100MB default, 0 to disable

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ require (
github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
github.com/go-sql-driver/mysql v1.5.0 // indirect
github.com/h2non/filetype v1.1.3
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect
github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 // indirect
Expand Down
Loading