1 Commits

Author SHA1 Message Date
Fabrizio Salmi
a7f965fb1c fix: resolve duplicate response headers and support CIDR notation in IP blacklist
Remove header duplication in copyResponse where recorder.Header() and
w.Header() share the same map, causing every header to be doubled.
The recorder delegates Header() and WriteHeader() to the underlying
writer, so only the body needs copying.

Support CIDR notation entries in IP blacklist files by detecting
existing prefix notation before appending a default mask. Also fix
IPv6 single-host default from /64 to /128.

Bump version to v0.3.0.

Fixes #91
Closes #92
2026-02-22 20:00:24 +01:00
3 changed files with 16 additions and 14 deletions

View File

@@ -49,7 +49,7 @@ var (
)
// Add or update the version constant as needed
const wafVersion = "v0.2.0" // update this value to the new release version when tagging
const wafVersion = "v0.3.0" // update this value to the new release version when tagging
// ==================== Initialization and Setup ====================
@@ -498,7 +498,15 @@ func (m *Middleware) loadIPBlacklist(path string, blacklistMap iptrie.Trie) erro
// Convert the map to CIDRTrie
for ip := range blacklist {
prefix, err := netip.ParsePrefix(appendCIDR(ip))
var prefix netip.Prefix
var err error
if strings.Contains(ip, "/") {
prefix, err = netip.ParsePrefix(ip)
} else {
prefix, err = netip.ParsePrefix(appendCIDR(ip))
}
if err != nil {
m.logger.Warn("Skipping invalid IP in blacklist", zap.String("ip", ip), zap.Error(err))
continue

View File

@@ -231,21 +231,15 @@ func (m *Middleware) logRequestCompletion(logID string, state *WAFState) {
)
}
// copyResponse copies the captured response from the recorder to the original writer
// copyResponse copies the captured response body from the recorder to the original writer.
// Headers and status code are already on w because the recorder delegates Header() and
// WriteHeader() directly to the underlying ResponseWriter, so only the body needs copying.
func (m *Middleware) copyResponse(w http.ResponseWriter, recorder *responseRecorder, r *http.Request) {
header := w.Header()
for key, values := range recorder.Header() {
for _, value := range values {
header.Add(key, value)
}
}
w.WriteHeader(recorder.StatusCode())
logID := getLogID(r.Context())
if logID == "unknown" {
m.logger.Error("Log ID not found in context during response copy") // added error log for clarity
m.logger.Error("Log ID not found in context during response copy")
}
_, err := w.Write(recorder.body.Bytes()) // Copy body from recorder to original writer
_, err := w.Write(recorder.body.Bytes())
if err != nil {
m.logger.Error("Failed to write recorded response body to client", zap.Error(err), zap.String("log_id", logID))
}

View File

@@ -33,7 +33,7 @@ func appendCIDR(ip string) string {
ip += "/32"
// IPv6
} else {
ip += "/64"
ip += "/128"
}
return ip
}