v0.1.17: security hardening — 18/20 vulnerabilities fixed
Phase 1 (Critical): - #1 JWT auth (done in v0.1.16) - #2 Batch message size validation in send_message_batch.go - #10 RLock in GetQueueUrlV1 (data race fix) Phase 2 (AWS-compatible limits): - #3 QueueName validation: max 80 chars, [a-zA-Z0-9_-](.fifo)? - #4 WaitTimeSeconds clamped to 0-20 - #5 ReceiveMessageWaitTimeSeconds clamped to 0-20 - #6 DelaySeconds clamped to 0-900 - #7 VisibilityTimeout clamped to 0-43200 - #8 MaxNumberOfMessages clamped to 1-10 - #9 Message attributes limited to 10 per message - #15 BatchEntryId length validated (max 80) - #16 DeduplicationID length validated (max 128) - #17 GroupID length validated (max 128) Phase 3 (Per-tenant resource limits): - #11 Max messages per queue (120K standard, 20K FIFO) - #12 Global tenant limit (1000) - #3.5 HTTP request body size limit (1MB via MaxBytesReader) Phase 4 (Stability): - #14 Duplicates map cleanup (already in PeriodicTasks) - #13 FIFO group lock timeout (already in visibility timeout reset) - #18 Redis size guard: skip save if >50MB Skipped (Low, no real risk): - #19 {account} URL param (informational only, not used for access) - #20 ReceiptHandle format (self-validating UUID#UUID) New file: app/gosqs/validation.go — centralized AWS SQS limits and validators
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
// app/gosqs/validation.go
|
||||
// AWS SQS-совместимые валидации параметров.
|
||||
// Все лимиты берутся из спецификации AWS SQS — не придумываем свои.
|
||||
// Created: 2026-04-10
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// ── AWS SQS лимиты ────────────────────────────────────────────────────────────
|
||||
const (
|
||||
// Очередь
|
||||
MaxQueueNameLength = 80
|
||||
MaxMessageSizeDefault = 262144 // 256 KB
|
||||
MaxMessageSizeLimit = 262144
|
||||
MinMessageSizeLimit = 1024 // 1 KB
|
||||
|
||||
// Атрибуты очереди
|
||||
MaxDelaySeconds = 900 // 15 min
|
||||
MaxVisibilityTimeout = 43200 // 12 hours
|
||||
MaxReceiveMessageWaitTimeSeconds = 20 // long polling cap
|
||||
MinMessageRetentionPeriod = 60 // 1 min
|
||||
MaxMessageRetentionPeriod = 1209600 // 14 days
|
||||
|
||||
// Receive
|
||||
MaxNumberOfMessagesLimit = 10
|
||||
MinNumberOfMessagesLimit = 1
|
||||
|
||||
// Message attributes
|
||||
MaxMessageAttributes = 10
|
||||
MaxMessageAttributeSize = 262144 // 256 KB суммарно (тело + атрибуты)
|
||||
|
||||
// Deduplication / GroupID
|
||||
MaxDeduplicationIDLength = 128
|
||||
MaxGroupIDLength = 128
|
||||
|
||||
// Batch
|
||||
MaxBatchEntryIDLength = 80
|
||||
|
||||
// Per-queue message limit (AWS = 120,000 для standard, 20,000 для FIFO)
|
||||
MaxMessagesPerQueue = 120000
|
||||
MaxMessagesPerFIFOQueue = 20000
|
||||
|
||||
// Per-request body size limit
|
||||
MaxRequestBodySize = 1 * 1024 * 1024 // 1 MB
|
||||
|
||||
// Global tenant limit
|
||||
MaxTenantsDefault = 1000
|
||||
)
|
||||
|
||||
// queueNameRegex — допустимые символы для имени очереди (AWS SQS)
|
||||
var queueNameRegex = regexp.MustCompile(`^[a-zA-Z0-9_-]+(\.fifo)?$`)
|
||||
|
||||
// ValidateQueueName — проверяет имя очереди по AWS правилам:
|
||||
// макс 80 символов, только [a-zA-Z0-9_-], опционально суффикс .fifo
|
||||
func ValidateQueueName(name string) error {
|
||||
if name == "" {
|
||||
return fmt.Errorf("QueueName is required")
|
||||
}
|
||||
if len(name) > MaxQueueNameLength {
|
||||
return fmt.Errorf("QueueName exceeds %d characters", MaxQueueNameLength)
|
||||
}
|
||||
if !queueNameRegex.MatchString(name) {
|
||||
return fmt.Errorf("QueueName contains invalid characters")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClampInt — ограничивает значение в диапазоне [min, max].
|
||||
// Если val < min — возвращает min, если > max — возвращает max.
|
||||
func ClampInt(val, min, max int) int {
|
||||
if val < min {
|
||||
return min
|
||||
}
|
||||
if val > max {
|
||||
return max
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// ValidateDeduplicationID — проверяет длину deduplication ID (макс 128 chars по AWS)
|
||||
func ValidateDeduplicationID(id string) error {
|
||||
if len(id) > MaxDeduplicationIDLength {
|
||||
return fmt.Errorf("MessageDeduplicationId exceeds %d characters", MaxDeduplicationIDLength)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateGroupID — проверяет длину group ID (макс 128 chars по AWS)
|
||||
func ValidateGroupID(id string) error {
|
||||
if len(id) > MaxGroupIDLength {
|
||||
return fmt.Errorf("MessageGroupId exceeds %d characters", MaxGroupIDLength)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateBatchEntryID — проверяет длину batch entry ID (макс 80 chars)
|
||||
func ValidateBatchEntryID(id string) error {
|
||||
if len(id) > MaxBatchEntryIDLength {
|
||||
return fmt.Errorf("BatchEntryId exceeds %d characters", MaxBatchEntryIDLength)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateMessageAttributes — проверяет количество атрибутов (макс 10 по AWS)
|
||||
func ValidateMessageAttributes(attrs map[string]interface{}) error {
|
||||
if len(attrs) > MaxMessageAttributes {
|
||||
return fmt.Errorf("message attributes exceed limit of %d", MaxMessageAttributes)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MaxMessagesForQueue — возвращает лимит сообщений для очереди (FIFO или standard)
|
||||
func MaxMessagesForQueue(isFIFO bool) int {
|
||||
if isFIFO {
|
||||
return MaxMessagesPerFIFOQueue
|
||||
}
|
||||
return MaxMessagesPerQueue
|
||||
}
|
||||
Reference in New Issue
Block a user