fix: treat drop action same as deny for status code resolution#261
fix: treat drop action same as deny for status code resolution#261erfianugrah wants to merge 1 commit intocorazawaf:mainfrom
Conversation
obtainStatusCodeFromInterruptionOrDefault only checks for action == "deny", so "drop" falls through to defaultStatusCode (200 in ServeHTTP). Since coraza-caddy runs as HTTP middleware and can't do TCP-level FIN/RST, this means drop rules silently return 200 instead of blocking. This adds "drop" to the condition so it uses the rule's status field (or 403 if unset), same as deny. Caddy's handle_errors can then serve the right error page. Includes table-driven tests covering deny, drop, redirect, pass, and unknown actions.
|
@erfianugrah Can you solve conflicts? |
There was a problem hiding this comment.
Pull request overview
This PR fixes incorrect HTTP status codes returned when Coraza triggers a drop interruption in the Caddy middleware path, aligning drop with deny for status code resolution so downstream Caddy error handling serves the correct status.
Changes:
- Treat
dropthe same asdenyinobtainStatusCodeFromInterruptionOrDefault()so the interruption’s configured status (or 403) is used. - Expand the function’s doc comment to explain why
dropis mapped to HTTP semantics in this middleware. - Add table-driven unit tests covering
deny,drop, and fall-through actions.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| interceptor.go | Updates status code selection logic (and documentation) so drop resolves to the rule status/403 like deny. |
| interceptor_test.go | Adds unit tests validating status code behavior across interruption actions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| func TestObtainStatusCodeFromInterruption(t *testing.T) { |
There was a problem hiding this comment.
Test name is slightly ambiguous: it exercises obtainStatusCodeFromInterruptionOrDefault(), but the test function is named TestObtainStatusCodeFromInterruption. Consider renaming the test to match the function under test for easier grep/discovery (e.g., include "OrDefault" in the test name).
| func TestObtainStatusCodeFromInterruption(t *testing.T) { | |
| func TestObtainStatusCodeFromInterruptionOrDefault(t *testing.T) { |
Problem
obtainStatusCodeFromInterruptionOrDefault()only checks foraction == "deny". When a SecRule uses thedropaction, it falls through todefaultStatusCode, which ishttp.StatusOK(200) in theServeHTTPcall path:The request is blocked (the upstream handler is never called), but Caddy sees a 200 status on the error, so
handle_errorsserves error pages with the wrong code. From the client's perspective, a blocked request comes back as HTTP 200.coraza-caddy runs as HTTP middleware — it doesn't have access to the underlying TCP socket, so the intended "send FIN and close" behavior of
dropisn't possible here. The practical thing to do is treat it the same asdeny.Fix
One-line change:
it.Action == "deny"→it.Action == "deny" || it.Action == "drop".When
dropfires, it now uses the rule'sstatus:field (or 403 if unset), matchingdenybehavior. This makeshandle_errorsandfile_serverserve error pages with the correct HTTP status.Tests
Added
interceptor_test.gowith table-driven tests coveringdeny,drop,redirect,pass, and unknown actions.