diff --git a/pkg/common/adapters/router/bunrouter.go b/pkg/common/adapters/router/bunrouter.go
index ebb27d9..fc65cc2 100644
--- a/pkg/common/adapters/router/bunrouter.go
+++ b/pkg/common/adapters/router/bunrouter.go
@@ -6,6 +6,7 @@ import (
"github.com/uptrace/bunrouter"
"github.com/bitechdev/ResolveSpec/pkg/common"
+ "github.com/bitechdev/ResolveSpec/pkg/logger"
)
// BunRouterAdapter adapts uptrace/bunrouter to work with our Router interface
@@ -36,7 +37,10 @@ func (b *BunRouterAdapter) ServeHTTP(w common.ResponseWriter, r common.Request)
// This method would be used when we need to serve through our interface
// For now, we'll work directly with the underlying router
w.WriteHeader(http.StatusNotImplemented)
- w.Write([]byte(`{"error":"ServeHTTP not implemented - use GetBunRouter() for direct access"}`))
+ _, err := w.Write([]byte(`{"error":"ServeHTTP not implemented - use GetBunRouter() for direct access"}`))
+ if err != nil {
+ logger.Warn("Failed to write. %v", err)
+ }
}
// GetBunRouter returns the underlying bunrouter for direct access
diff --git a/pkg/common/adapters/router/mux.go b/pkg/common/adapters/router/mux.go
index 9287e40..4ace587 100644
--- a/pkg/common/adapters/router/mux.go
+++ b/pkg/common/adapters/router/mux.go
@@ -8,6 +8,7 @@ import (
"github.com/gorilla/mux"
"github.com/bitechdev/ResolveSpec/pkg/common"
+ "github.com/bitechdev/ResolveSpec/pkg/logger"
)
// MuxAdapter adapts Gorilla Mux to work with our Router interface
@@ -33,7 +34,10 @@ func (m *MuxAdapter) ServeHTTP(w common.ResponseWriter, r common.Request) {
// This method would be used when we need to serve through our interface
// For now, we'll work directly with the underlying router
w.WriteHeader(http.StatusNotImplemented)
- w.Write([]byte(`{"error":"ServeHTTP not implemented - use GetMuxRouter() for direct access"}`))
+ _, err := w.Write([]byte(`{"error":"ServeHTTP not implemented - use GetMuxRouter() for direct access"}`))
+ if err != nil {
+ logger.Warn("Failed to write. %v", err)
+ }
}
// MuxRouteRegistration implements RouteRegistration for Mux
diff --git a/pkg/common/handler_utils.go b/pkg/common/handler_utils.go
index 0440e6e..61716fb 100644
--- a/pkg/common/handler_utils.go
+++ b/pkg/common/handler_utils.go
@@ -45,4 +45,3 @@ func ValidateAndUnwrapModel(model interface{}) (*ValidateAndUnwrapModelResult, e
OriginalType: originalType,
}, nil
}
-
diff --git a/pkg/metrics/interfaces.go b/pkg/metrics/interfaces.go
index 56efa33..4c8b62c 100644
--- a/pkg/metrics/interfaces.go
+++ b/pkg/metrics/interfaces.go
@@ -3,6 +3,8 @@ package metrics
import (
"net/http"
"time"
+
+ "github.com/bitechdev/ResolveSpec/pkg/logger"
)
// Provider defines the interface for metric collection
@@ -57,12 +59,15 @@ func (n *NoOpProvider) IncRequestsInFlight()
func (n *NoOpProvider) DecRequestsInFlight() {}
func (n *NoOpProvider) RecordDBQuery(operation, table string, duration time.Duration, err error) {
}
-func (n *NoOpProvider) RecordCacheHit(provider string) {}
-func (n *NoOpProvider) RecordCacheMiss(provider string) {}
+func (n *NoOpProvider) RecordCacheHit(provider string) {}
+func (n *NoOpProvider) RecordCacheMiss(provider string) {}
func (n *NoOpProvider) UpdateCacheSize(provider string, size int64) {}
func (n *NoOpProvider) Handler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
- w.Write([]byte("Metrics provider not configured"))
+ _, err := w.Write([]byte("Metrics provider not configured"))
+ if err != nil {
+ logger.Warn("Failed to write. %v", err)
+ }
})
}
diff --git a/pkg/metrics/prometheus.go b/pkg/metrics/prometheus.go
index 9d7e498..49c6ebb 100644
--- a/pkg/metrics/prometheus.go
+++ b/pkg/metrics/prometheus.go
@@ -12,14 +12,14 @@ import (
// PrometheusProvider implements the Provider interface using Prometheus
type PrometheusProvider struct {
- requestDuration *prometheus.HistogramVec
- requestTotal *prometheus.CounterVec
- requestsInFlight prometheus.Gauge
- dbQueryDuration *prometheus.HistogramVec
- dbQueryTotal *prometheus.CounterVec
- cacheHits *prometheus.CounterVec
- cacheMisses *prometheus.CounterVec
- cacheSize *prometheus.GaugeVec
+ requestDuration *prometheus.HistogramVec
+ requestTotal *prometheus.CounterVec
+ requestsInFlight prometheus.Gauge
+ dbQueryDuration *prometheus.HistogramVec
+ dbQueryTotal *prometheus.CounterVec
+ cacheHits *prometheus.CounterVec
+ cacheMisses *prometheus.CounterVec
+ cacheSize *prometheus.GaugeVec
}
// NewPrometheusProvider creates a new Prometheus metrics provider
diff --git a/pkg/middleware/README.md b/pkg/middleware/README.md
index 92de2b5..51536fd 100644
--- a/pkg/middleware/README.md
+++ b/pkg/middleware/README.md
@@ -1,6 +1,14 @@
# Middleware Package
-HTTP middleware utilities including rate limiting.
+HTTP middleware utilities for security and performance.
+
+## Table of Contents
+
+1. [Rate Limiting](#rate-limiting)
+2. [Request Size Limits](#request-size-limits)
+3. [Input Sanitization](#input-sanitization)
+
+---
## Rate Limiting
@@ -370,3 +378,429 @@ func healthHandler(w http.ResponseWriter, r *http.Request) {
}
}
```
+
+---
+
+## Request Size Limits
+
+Protect against oversized request bodies with configurable size limits.
+
+### Quick Start
+
+```go
+import "github.com/bitechdev/ResolveSpec/pkg/middleware"
+
+// Default: 10MB limit
+sizeLimiter := middleware.NewRequestSizeLimiter(0)
+router.Use(sizeLimiter.Middleware)
+```
+
+### Custom Size Limit
+
+```go
+// 5MB limit
+sizeLimiter := middleware.NewRequestSizeLimiter(5 * 1024 * 1024)
+router.Use(sizeLimiter.Middleware)
+
+// Or use constants
+sizeLimiter := middleware.NewRequestSizeLimiter(middleware.Size5MB)
+```
+
+### Available Size Constants
+
+```go
+middleware.Size1MB // 1 MB
+middleware.Size5MB // 5 MB
+middleware.Size10MB // 10 MB (default)
+middleware.Size50MB // 50 MB
+middleware.Size100MB // 100 MB
+```
+
+### Different Limits Per Route
+
+```go
+func main() {
+ router := mux.NewRouter()
+
+ // File upload endpoint: 50MB
+ uploadLimiter := middleware.NewRequestSizeLimiter(middleware.Size50MB)
+ uploadRouter := router.PathPrefix("/upload").Subrouter()
+ uploadRouter.Use(uploadLimiter.Middleware)
+
+ // API endpoints: 1MB
+ apiLimiter := middleware.NewRequestSizeLimiter(middleware.Size1MB)
+ apiRouter := router.PathPrefix("/api").Subrouter()
+ apiRouter.Use(apiLimiter.Middleware)
+}
+```
+
+### Dynamic Size Limits
+
+```go
+// Custom size based on request
+sizeFunc := func(r *http.Request) int64 {
+ // Premium users get 50MB
+ if isPremiumUser(r) {
+ return middleware.Size50MB
+ }
+ // Free users get 5MB
+ return middleware.Size5MB
+}
+
+router.Use(sizeLimiter.MiddlewareWithCustomSize(sizeFunc))
+```
+
+**By Content-Type:**
+
+```go
+sizeFunc := func(r *http.Request) int64 {
+ contentType := r.Header.Get("Content-Type")
+
+ switch {
+ case strings.Contains(contentType, "multipart/form-data"):
+ return middleware.Size50MB // File uploads
+ case strings.Contains(contentType, "application/json"):
+ return middleware.Size1MB // JSON APIs
+ default:
+ return middleware.Size10MB // Default
+ }
+}
+```
+
+### Error Response
+
+When size limit exceeded:
+
+```http
+HTTP/1.1 413 Request Entity Too Large
+X-Max-Request-Size: 10485760
+
+http: request body too large
+```
+
+### Complete Example
+
+```go
+package main
+
+import (
+ "log"
+ "net/http"
+
+ "github.com/bitechdev/ResolveSpec/pkg/middleware"
+ "github.com/gorilla/mux"
+)
+
+func main() {
+ router := mux.NewRouter()
+
+ // API routes: 1MB limit
+ api := router.PathPrefix("/api").Subrouter()
+ apiLimiter := middleware.NewRequestSizeLimiter(middleware.Size1MB)
+ api.Use(apiLimiter.Middleware)
+ api.HandleFunc("/users", createUserHandler).Methods("POST")
+
+ // Upload routes: 50MB limit
+ upload := router.PathPrefix("/upload").Subrouter()
+ uploadLimiter := middleware.NewRequestSizeLimiter(middleware.Size50MB)
+ upload.Use(uploadLimiter.Middleware)
+ upload.HandleFunc("/file", uploadFileHandler).Methods("POST")
+
+ log.Fatal(http.ListenAndServe(":8080", router))
+}
+```
+
+---
+
+## Input Sanitization
+
+Protect against XSS, injection attacks, and malicious input.
+
+### Quick Start
+
+```go
+import "github.com/bitechdev/ResolveSpec/pkg/middleware"
+
+// Default sanitizer (safe defaults)
+sanitizer := middleware.DefaultSanitizer()
+router.Use(sanitizer.Middleware)
+```
+
+### Sanitizer Types
+
+**Default Sanitizer (Recommended):**
+
+```go
+sanitizer := middleware.DefaultSanitizer()
+// ✓ Escapes HTML entities
+// ✓ Removes null bytes
+// ✓ Removes control characters
+// ✓ Blocks XSS patterns (script tags, event handlers)
+// ✗ Does not strip HTML (allows legitimate content)
+```
+
+**Strict Sanitizer:**
+
+```go
+sanitizer := middleware.StrictSanitizer()
+// ✓ All default features
+// ✓ Strips ALL HTML tags
+// ✓ Max string length: 10,000 chars
+```
+
+### Custom Configuration
+
+```go
+sanitizer := &middleware.Sanitizer{
+ StripHTML: true, // Remove HTML tags
+ EscapeHTML: false, // Don't escape (already stripped)
+ RemoveNullBytes: true, // Remove \x00
+ RemoveControlChars: true, // Remove dangerous control chars
+ MaxStringLength: 5000, // Limit to 5000 chars
+
+ // Block patterns (regex)
+ BlockPatterns: []*regexp.Regexp{
+ regexp.MustCompile(`(?i)`
+2. **JavaScript protocol**: `javascript:alert(1)`
+3. **Event handlers**: `onclick="..."`, `onerror="..."`
+4. **Iframes**: `