package feedback import ( "path/filepath" "strings" ) // maxAttachmentBytes caps a single attachment's raw size. Chosen to fit, with the // message text and the FlatBuffers framing, under the gateway's 1 MiB edge body // cap, so the whole submit request passes without weakening that cap. const maxAttachmentBytes = 1_000_000 // allowedExt is the attachment extension allow-list. It is mirrored on the UI as a // pre-upload gate; the server re-checks here as the trust boundary (metadata only, // the file content is never parsed). Images render inline in the console; the rest // are download-only. var allowedExt = map[string]bool{ "png": true, "jpg": true, "jpeg": true, "webp": true, "gif": true, // images "pdf": true, "txt": true, "log": true, "doc": true, "docx": true, "rtf": true, "zip": true, "gz": true, "7z": true, } // imageType maps an image extension to the content-type the console serves it with // (loaded only via , which never executes, so a renamed non-image is inert). var imageType = map[string]string{ "png": "image/png", "jpg": "image/jpeg", "jpeg": "image/jpeg", "webp": "image/webp", "gif": "image/gif", } // ext returns name's lower-cased extension without the leading dot. func ext(name string) string { return strings.ToLower(strings.TrimPrefix(filepath.Ext(name), ".")) } // AllowedAttachment reports whether name's extension is on the allow-list. func AllowedAttachment(name string) bool { return allowedExt[ext(name)] } // IsImage reports whether name is an inline-previewable image by its extension. func IsImage(name string) bool { _, ok := imageType[ext(name)] return ok } // ContentType returns the safe content-type the console serves the attachment // with: the matching image type for an image, else application/octet-stream so a // non-image is downloaded rather than rendered. func ContentType(name string) string { if t, ok := imageType[ext(name)]; ok { return t } return "application/octet-stream" }