fix: 修复解压炸弹漏洞和goroutine泄漏
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f43b06e13d
commit
76a5eaaad9
@ -62,6 +62,7 @@ type minimaxService struct {
|
||||
jobs map[string]*ImageGenerationJob
|
||||
jobsLock sync.RWMutex
|
||||
client *http.Client
|
||||
stopCh chan struct{}
|
||||
}
|
||||
|
||||
// NewMinimaxService 创建 MiniMax 服务
|
||||
@ -70,6 +71,7 @@ func NewMinimaxService(cfg *config.AssetConfig) MinimaxService {
|
||||
config: cfg,
|
||||
jobs: make(map[string]*ImageGenerationJob),
|
||||
client: &http.Client{Timeout: 120 * time.Second},
|
||||
stopCh: make(chan struct{}),
|
||||
}
|
||||
go svc.cleanupExpiredJobs()
|
||||
return svc
|
||||
@ -246,10 +248,21 @@ func (s *minimaxService) compressImageIfNeeded(imageURL string) (string, error)
|
||||
return "", err
|
||||
}
|
||||
|
||||
img, format, err := image.Decode(bytes.NewReader(imgData))
|
||||
// First decode config to check dimensions (prevents decompression bomb)
|
||||
cfg, format, err := image.DecodeConfig(bytes.NewReader(imgData))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Limit decoded image to reasonable size (e.g., 4096x4096 = 64M pixels)
|
||||
if int64(cfg.Width)*int64(cfg.Height) > 4096*4096 {
|
||||
return "", fmt.Errorf("image too large: %dx%d", cfg.Width, cfg.Height)
|
||||
}
|
||||
// Full decode (already limited by size)
|
||||
img, _, err := image.Decode(bytes.NewReader(imgData))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_ = format // suppress unused warning
|
||||
|
||||
bounds := img.Bounds()
|
||||
maxDim := uint(1024)
|
||||
@ -332,17 +345,28 @@ func validateURL(rawURL string) error {
|
||||
// cleanupExpiredJobs 清理过期任务
|
||||
func (s *minimaxService) cleanupExpiredJobs() {
|
||||
ticker := time.NewTicker(1 * time.Hour)
|
||||
for range ticker.C {
|
||||
s.jobsLock.Lock()
|
||||
now := time.Now().UnixMilli()
|
||||
expiredThreshold := int64(24 * 60 * 60 * 1000) // 24h
|
||||
for jobID, job := range s.jobs {
|
||||
if job.Status == StatusCompleted || job.Status == StatusFailed {
|
||||
if now-job.UpdatedAt > expiredThreshold {
|
||||
delete(s.jobs, jobID)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-s.stopCh:
|
||||
return
|
||||
case <-ticker.C:
|
||||
s.jobsLock.Lock()
|
||||
now := time.Now().UnixMilli()
|
||||
expiredThreshold := int64(24 * 60 * 60 * 1000) // 24h
|
||||
for jobID, job := range s.jobs {
|
||||
if job.Status == StatusCompleted || job.Status == StatusFailed {
|
||||
if now-job.UpdatedAt > expiredThreshold {
|
||||
delete(s.jobs, jobID)
|
||||
}
|
||||
}
|
||||
}
|
||||
s.jobsLock.Unlock()
|
||||
}
|
||||
s.jobsLock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Close 关闭服务
|
||||
func (s *minimaxService) Close() {
|
||||
close(s.stopCh)
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user