HH-438: close upload and remote URL attack surfaces (#89)

* HH-438: close upload and remote URL attack surfaces

* HH-438: use valid PNG in direct upload test

* HH-438: align PostgreSQL upload staging schema

* HH-438: run upload migrations before PostgreSQL E2E

---------

Co-authored-by: Rogee <rogee@ipao.vip>
This commit is contained in:
Rogee
2026-08-21 20:36:52 +08:00
committed by GitHub
co-authored by rogee
parent 7a9fec33c5
commit 77c91662d2
18 changed files with 1311 additions and 259 deletions
@@ -2,6 +2,7 @@ package widget
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
@@ -105,6 +106,7 @@ func (h *WidgetHandler) SubmitPreChatForm(c *gin.Context) {
// Returns upload_uuid that can be referenced when sending a message with attachment.
// Reference: Chatwoot widget SDK — file upload before sending message
func (h *WidgetHandler) StageFileUpload(c *gin.Context) {
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, (16<<20)+(1<<20))
websiteToken := c.Param("website_token")
if websiteToken == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "website_token is required"})
@@ -113,7 +115,11 @@ func (h *WidgetHandler) StageFileUpload(c *gin.Context) {
file, err := c.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "file field is required", "details": err.Error()})
status := http.StatusBadRequest
if strings.Contains(strings.ToLower(err.Error()), "request body too large") {
status = http.StatusRequestEntityTooLarge
}
c.JSON(status, gin.H{"error": "file field is required", "details": err.Error()})
return
}
@@ -125,6 +131,7 @@ func (h *WidgetHandler) StageFileUpload(c *gin.Context) {
defer fileReader.Close()
req := service.WidgetUploadRequest{
WidgetToken: widgetTokenFromRequest(c),
WebsiteToken: websiteToken,
FileName: file.Filename,
FileSize: file.Size,
@@ -156,7 +163,7 @@ func (h *WidgetHandler) GetFileUploadStatus(c *gin.Context) {
return
}
resp, err := h.widgetService.GetFileUploadStatus(c.Request.Context(), websiteToken, uploadUUID)
resp, err := h.widgetService.GetFileUploadStatus(c.Request.Context(), websiteToken, uploadUUID, widgetTokenFromRequest(c))
if err != nil {
applogger.L().Errorf("Failed to get upload status for uuid=%s: %v", uploadUUID, err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
@@ -169,4 +176,4 @@ func (h *WidgetHandler) GetFileUploadStatus(c *gin.Context) {
}
c.JSON(http.StatusOK, resp)
}
}