69 lines
2.3 KiB
Elixir
69 lines
2.3 KiB
Elixir
defmodule ToweropsWeb.PlugExceptions do
|
|
@moduledoc """
|
|
Protocol implementations for converting expected Phoenix exceptions
|
|
into clean HTTP status codes rather than raising errors.
|
|
|
|
This reduces noise in error tracking by treating invalid requests
|
|
(wrong MIME types, invalid CSRF tokens) as bad requests rather than
|
|
application failures.
|
|
|
|
## Rationale
|
|
|
|
Production applications receive noise from legitimate bad requests:
|
|
- API clients sending incorrect Accept headers
|
|
- Mobile apps with stale CSRF tokens after backgrounding
|
|
- Automation tools (Terraform, scripts) with malformed requests
|
|
|
|
These are client errors (400-level), not server failures (500-level).
|
|
By implementing `Plug.Exception` protocol for these exceptions, we:
|
|
|
|
1. Return clean HTTP 400 Bad Request responses
|
|
2. Reduce exception tracking alerts for expected client errors
|
|
3. Keep error logs focused on genuine application failures
|
|
|
|
## Implementation
|
|
|
|
Each exception type gets a protocol implementation with two callbacks:
|
|
|
|
- `status/1` - Returns the HTTP status code (400 for bad requests)
|
|
- `actions/1` - Returns suggested actions (empty list for now)
|
|
|
|
## Covered Exception Types
|
|
|
|
- `Phoenix.NotAcceptableError` - Invalid Accept headers from API clients
|
|
- `Plug.CSRFProtection.InvalidCSRFTokenError` - Stale or invalid CSRF tokens
|
|
|
|
## Monitoring
|
|
|
|
These exceptions are still logged at debug level, allowing monitoring
|
|
without triggering production alerts. If you see patterns indicating
|
|
attacks (e.g., excessive CSRF failures), investigate via logs.
|
|
|
|
## See Also
|
|
|
|
- Documentation: `docs/exception_handling.md`
|
|
- Tests: `test/towerops_web/plug_exceptions_test.exs`
|
|
"""
|
|
|
|
require Logger
|
|
|
|
# Invalid Accept headers from API consumers (automation, mobile apps, etc.)
|
|
defimpl Plug.Exception, for: Phoenix.NotAcceptableError do
|
|
def status(exception) do
|
|
Logger.debug("Converting Phoenix.NotAcceptableError to 400: #{inspect(exception)}")
|
|
400
|
|
end
|
|
|
|
def actions(_exception), do: []
|
|
end
|
|
|
|
# Stale CSRF tokens from mobile app or browser sessions
|
|
defimpl Plug.Exception, for: Plug.CSRFProtection.InvalidCSRFTokenError do
|
|
def status(exception) do
|
|
Logger.debug("Converting InvalidCSRFTokenError to 400: #{inspect(exception)}")
|
|
400
|
|
end
|
|
|
|
def actions(_exception), do: []
|
|
end
|
|
end
|