fix: add field whitelist to site update, fix HTTP status codes in site handlers

- SiteService.Update: whitelist updatable fields to prevent injection of
  id, created_at, deleted_at, login_status, upload_bytes, download_bytes
- createSiteHandler: return 201 Created (was 200 OK)
- siteSearchHandler: return 500 for infra errors (was 400)

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
soldosluka857
2026-05-30 02:22:27 +00:00
committed by Shuke
parent 2c45fa596a
commit 0572612833
2 changed files with 42 additions and 9 deletions
+2 -2
View File
@@ -102,7 +102,7 @@ func createSiteHandler(svc *service.Container) gin.HandlerFunc {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return return
} }
c.JSON(http.StatusOK, site) c.JSON(http.StatusCreated, site)
} }
} }
@@ -156,7 +156,7 @@ func siteSearchHandler(svc *service.Container) gin.HandlerFunc {
} }
results, err := svc.Site.Search(c.Request.Context(), keyword) results, err := svc.Site.Search(c.Request.Context(), keyword)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return return
} }
c.JSON(http.StatusOK, gin.H{"items": results, "total": len(results)}) c.JSON(http.StatusOK, gin.H{"items": results, "total": len(results)})
+40 -7
View File
@@ -213,20 +213,53 @@ func (s *SiteService) FindByID(ctx context.Context, id string) (*model.Site, err
return &site, err return &site, err
} }
// siteUpdatableFields is the whitelist of columns that may be patched via
// the update endpoint. Fields like id, created_at, deleted_at, login_status,
// upload_bytes, download_bytes are excluded to prevent injection.
var siteUpdatableFields = map[string]bool{
"name": true,
"url": true,
"type": true,
"auth_type": true,
"api_key": true,
"cookie": true,
"auth_header": true,
"user_agent": true,
"rss_url": true,
"timeout": true,
"priority": true,
"use_proxy": true,
"rate_limit": true,
"browser_emulation": true,
"downloader": true,
"enabled": true,
"is_default": true,
"extra": true,
}
// Update applies a partial patch to an existing site. // Update applies a partial patch to an existing site.
func (s *SiteService) Update(ctx context.Context, id string, updates map[string]any) error { func (s *SiteService) Update(ctx context.Context, id string, updates map[string]any) error {
if id == "" { if id == "" {
return errors.New("site id required") return errors.New("site id required")
} }
if raw, ok := updates["url"].(string); ok { filtered := make(map[string]any, len(updates))
updates["url"] = strings.TrimRight(strings.TrimSpace(raw), "/") for k, v := range updates {
} if siteUpdatableFields[k] {
for _, key := range []string{"api_key", "cookie", "auth_header"} { filtered[k] = v
if raw, ok := updates[key].(string); ok && strings.TrimSpace(raw) == "" {
delete(updates, key)
} }
} }
return s.repo.DB.WithContext(ctx).Model(&model.Site{}).Where("id = ?", id).Updates(updates).Error if len(filtered) == 0 {
return errors.New("no valid fields to update")
}
if raw, ok := filtered["url"].(string); ok {
filtered["url"] = strings.TrimRight(strings.TrimSpace(raw), "/")
}
for _, key := range []string{"api_key", "cookie", "auth_header"} {
if raw, ok := filtered[key].(string); ok && strings.TrimSpace(raw) == "" {
delete(filtered, key)
}
}
return s.repo.DB.WithContext(ctx).Model(&model.Site{}).Where("id = ?", id).Updates(filtered).Error
} }
// Delete removes a site. // Delete removes a site.