Fixes #6403 - HTTP log does not record requests with unparsable parameters.
Approved-by: Florian Liebe <fl@zammad.com>
This commit is contained in:
@@ -17,6 +17,17 @@ module ApplicationController::HandlesErrors
|
||||
rescue_from Pundit::NotAuthorizedError, with: :pundit_not_authorized_error
|
||||
rescue_from 'Store::Provider::S3::Error', with: :unprocessable_content
|
||||
rescue_from Exceptions::MissingAttribute, Exceptions::InvalidAttribute, ActionController::ParameterMissing, with: :unprocessable_content
|
||||
rescue_from ActionController::BadRequest, ActionDispatch::Http::Parameters::ParseError, with: :bad_request
|
||||
end
|
||||
|
||||
def bad_request(e)
|
||||
logger.error e
|
||||
# unreadable params also hide the format default of the route
|
||||
if (format = request.path_parameters[:format])
|
||||
request.set_header('action_dispatch.request.formats', [Mime::Type.lookup_by_extension(format)])
|
||||
end
|
||||
respond_to_exception(e, :bad_request)
|
||||
http_log
|
||||
end
|
||||
|
||||
def not_found(e)
|
||||
@@ -72,6 +83,13 @@ module ApplicationController::HandlesErrors
|
||||
|
||||
private
|
||||
|
||||
# Rails raises these while parsing the params, before rescue_from handlers are reachable.
|
||||
def process_action(*)
|
||||
super
|
||||
rescue ActionController::BadRequest, ActionDispatch::Http::Parameters::ParseError => e
|
||||
bad_request(e)
|
||||
end
|
||||
|
||||
def respond_to_exception(e, status)
|
||||
status_code = Rack::Utils.status_code(status)
|
||||
|
||||
@@ -82,7 +100,7 @@ module ApplicationController::HandlesErrors
|
||||
@exception = e
|
||||
@message = errors[:error_human] || errors[:error] || param[:message]
|
||||
@traceback = !Rails.env.production?
|
||||
file = Rails.public_path.join("#{status_code}#{'-mobile' if params[:controller] == 'mobile'}.html").open('r')
|
||||
file = Rails.public_path.join("#{status_code}#{'-mobile' if request.path_parameters[:controller] == 'mobile'}.html").open('r')
|
||||
render inline: file.read, status: status, content_type: 'text/html' # rubocop:disable Rails/RenderInline
|
||||
end
|
||||
end
|
||||
@@ -115,7 +133,7 @@ module ApplicationController::HandlesErrors
|
||||
elsif e.is_a?(Exceptions::InvalidCSRFToken)
|
||||
data[:error_human] = data[:error]
|
||||
data[:invalid_csrf_token] = true
|
||||
elsif [ActionController::RoutingError, ActiveRecord::RecordNotFound, Exceptions::NotAuthorized, Exceptions::Forbidden, Store::Provider::S3::Error, Authorization::Provider::AccountError, Exceptions::MissingAttribute, ActionController::ParameterMissing].include?(e.class)
|
||||
elsif [ActionController::RoutingError, ActionController::BadRequest, ActionDispatch::Http::Parameters::ParseError, ActiveRecord::RecordNotFound, Exceptions::NotAuthorized, Exceptions::Forbidden, Store::Provider::S3::Error, Authorization::Provider::AccountError, Exceptions::MissingAttribute, ActionController::ParameterMissing].include?(e.class)
|
||||
data[:error_human] = data[:error]
|
||||
end
|
||||
|
||||
|
||||
@@ -5,17 +5,21 @@ module ApplicationController::LogsHttpAccess
|
||||
|
||||
included do
|
||||
after_action :http_log
|
||||
|
||||
class_attribute :http_log_support, instance_writer: false
|
||||
end
|
||||
|
||||
class_methods do
|
||||
def http_log_config(config)
|
||||
self.http_log_support = config
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def http_log_config(config)
|
||||
@http_log_support = config
|
||||
end
|
||||
|
||||
# log http access
|
||||
def http_log
|
||||
return if !@http_log_support
|
||||
return if !http_log_support
|
||||
|
||||
# request
|
||||
request_data = {
|
||||
@@ -57,7 +61,7 @@ module ApplicationController::LogsHttpAccess
|
||||
response_data[:content] = response_data[:content].slice(0, 8000)
|
||||
record = {
|
||||
direction: 'in',
|
||||
facility: @http_log_support[:facility],
|
||||
facility: http_log_support[:facility],
|
||||
url: url_for(only_path: false, overwrite_params: {}),
|
||||
status: response.status,
|
||||
ip: request.remote_ip,
|
||||
|
||||
@@ -4,6 +4,8 @@ class Integration::CheckMkController < ApplicationController
|
||||
skip_before_action :verify_csrf_token
|
||||
before_action :check_configured
|
||||
|
||||
http_log_config facility: 'check_mk'
|
||||
|
||||
def update
|
||||
|
||||
# check params
|
||||
@@ -137,8 +139,6 @@ UserAgent: #{request.env['HTTP_USER_AGENT'] || '-'}
|
||||
private
|
||||
|
||||
def check_configured
|
||||
http_log_config facility: 'check_mk'
|
||||
|
||||
if !Setting.get('check_mk_integration')
|
||||
raise Exceptions::UnprocessableContent, __('Feature is disabled, please contact your administrator!')
|
||||
end
|
||||
|
||||
@@ -4,6 +4,8 @@ class Integration::CtiController < ApplicationController
|
||||
skip_before_action :verify_csrf_token
|
||||
before_action :check_configured, :check_token
|
||||
|
||||
http_log_config facility: 'cti'
|
||||
|
||||
# notify about inbound call / block inbound call
|
||||
def event
|
||||
local_params = ActiveSupport::HashWithIndifferentAccess.new(params.permit!.to_h)
|
||||
@@ -49,8 +51,6 @@ class Integration::CtiController < ApplicationController
|
||||
end
|
||||
|
||||
def check_configured
|
||||
http_log_config facility: 'cti'
|
||||
|
||||
if !Setting.get('cti_integration')
|
||||
response_error(__('Feature is disabled, please contact your administrator!'))
|
||||
return
|
||||
|
||||
@@ -6,6 +6,8 @@ class Integration::PlacetelController < ApplicationController
|
||||
skip_before_action :verify_csrf_token
|
||||
before_action :check_configured, :check_token
|
||||
|
||||
http_log_config facility: 'placetel'
|
||||
|
||||
# notify about inbound call / block inbound call
|
||||
def event
|
||||
|
||||
@@ -48,8 +50,6 @@ class Integration::PlacetelController < ApplicationController
|
||||
end
|
||||
|
||||
def check_configured
|
||||
http_log_config facility: 'placetel'
|
||||
|
||||
if !Setting.get('placetel_integration')
|
||||
response_error(__('Feature is disabled, please contact your administrator!'))
|
||||
return
|
||||
|
||||
@@ -6,6 +6,8 @@ class Integration::SipgateController < ApplicationController
|
||||
skip_before_action :verify_csrf_token
|
||||
before_action :check_configured, :check_token
|
||||
|
||||
http_log_config facility: 'sipgate.io'
|
||||
|
||||
# notify about inbound call / block inbound call
|
||||
def event
|
||||
|
||||
@@ -48,8 +50,6 @@ class Integration::SipgateController < ApplicationController
|
||||
end
|
||||
|
||||
def check_configured
|
||||
http_log_config facility: 'sipgate.io'
|
||||
|
||||
if !Setting.get('sipgate_integration')
|
||||
xml_error(__('Feature is disabled, please contact your administrator!'))
|
||||
return
|
||||
|
||||
@@ -32,7 +32,7 @@ class TestsController < ApplicationController
|
||||
message = params.fetch(:message, 'no message provided')
|
||||
|
||||
# Emulate the originating controller.
|
||||
params[:controller] = origin if origin
|
||||
request.path_parameters[:controller] = origin if origin
|
||||
|
||||
klass = exception.safe_constantize
|
||||
|
||||
|
||||
74
public/400-mobile.html
Normal file
74
public/400-mobile.html
Normal file
@@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html class="dark">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"
|
||||
>
|
||||
|
||||
<title>400: Bad Request</title>
|
||||
|
||||
<link rel="stylesheet" href="/assets/error/style-mobile.css">
|
||||
|
||||
<body <% if @traceback %> class="error-message" <% end %>>
|
||||
<div class="h-full min-w-full bg-black font-sans text-sm text-white antialiased">
|
||||
<div class="flex min-h-screen flex-col px-4">
|
||||
<header class="fixed">
|
||||
<div class="grid h-16 grid-cols-3">
|
||||
<div class="flex cursor-pointer items-center justify-self-start text-base">
|
||||
<button
|
||||
class="flex cursor-pointer items-center"
|
||||
aria-label="Go home"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon fill-current icon-mobile-home"
|
||||
width="32"
|
||||
height="32"
|
||||
aria-hidden="true"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M8.5 14a1.5 1.5 0 0 1 1.5-1.5h4a1.5 1.5 0 0 1 1.5 1.5v4.5h3V9.442L12 4.838 5.5 9.442V18.5h3V14Zm1.5 6H4V9.184a1 1 0 0 1 .422-.816l7-4.959a1 1 0 0 1 1.156 0l7 4.959a1 1 0 0 1 .422.816V20h-6v-6h-4v6Z"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="flex grow flex-col items-center justify-center">
|
||||
<h1 class="mb-9 text-8xl font-bold">400</h1>
|
||||
|
||||
<% if !@traceback %>
|
||||
<img alt="Error" src="/assets/error/error-403.svg">
|
||||
<h2 class="mt-9 max-w-prose text-center text-xl font-medium">
|
||||
The request could not be processed
|
||||
</h2>
|
||||
<p class="mt-4 min-h-[4rem] max-w-prose text-center text-gray">
|
||||
The request was malformed, e.g. it used an invalid parameter encoding.
|
||||
</p>
|
||||
<% if @message.present? %>
|
||||
<p class="max-w-prose text-center text-gray"><%= @message %></p>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<pre class="traceback">
|
||||
<%= @exception.message %>
|
||||
<% if @exception.backtrace %>
|
||||
<code>
|
||||
<% @exception.backtrace.each {|row| %>
|
||||
<%= row %>
|
||||
<% } %>
|
||||
</code>
|
||||
<% end %>
|
||||
</pre>
|
||||
<% end %>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
25
public/400.html
Normal file
25
public/400.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="dark">
|
||||
<meta charset="utf-8">
|
||||
<title>400: Bad Request</title>
|
||||
<link rel="stylesheet" href="/assets/error/style.css">
|
||||
<body <% if @traceback %>class="error-message"<% end %>>
|
||||
<h1>400: The request could not be processed.</h1>
|
||||
<% if @message.present? %>
|
||||
<div><%= @message %></div>
|
||||
<% end %>
|
||||
<% if !@traceback %>
|
||||
<div class="error-image" style="background-image: url(/assets/error/error-1.svg)"></div>
|
||||
<p>The request was malformed, e.g. it used an invalid parameter encoding.</p>
|
||||
<% else %>
|
||||
<% if @exception.message != @message %>
|
||||
<div><%= @exception.message %></div>
|
||||
<% end %>
|
||||
<% if @exception.backtrace %>
|
||||
<div>Traceback:</div>
|
||||
<% @exception.backtrace.each {|row| %>
|
||||
<%= row %><br>
|
||||
<% } %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</html>
|
||||
@@ -149,6 +149,7 @@ RSpec.describe 'Error handling', type: :request do
|
||||
|
||||
let(:user) { :agent }
|
||||
|
||||
include_examples 'handles exception', ActionController::BadRequest, :bad_request, '400: Bad Request', '400: The request could not be processed.'
|
||||
include_examples 'handles exception', Exceptions::NotAuthorized, :unauthorized, '401: Unauthorized', '401: Unauthorized'
|
||||
include_examples 'handles exception', Exceptions::Forbidden, :forbidden, '403: Forbidden', '403: Forbidden'
|
||||
include_examples 'handles exception', Pundit::NotAuthorizedError, :forbidden, '403: Forbidden', '403: Forbidden', 'Not authorized'
|
||||
@@ -163,6 +164,7 @@ RSpec.describe 'Error handling', type: :request do
|
||||
|
||||
let(:user) { :admin }
|
||||
|
||||
include_examples 'handles exception', ActionController::BadRequest, :bad_request, '400: Bad Request', '400: The request could not be processed.'
|
||||
include_examples 'handles exception', Exceptions::NotAuthorized, :unauthorized, '401: Unauthorized', '401: Unauthorized'
|
||||
include_examples 'handles exception', Exceptions::Forbidden, :forbidden, '403: Forbidden', '403: Forbidden'
|
||||
include_examples 'handles exception', Pundit::NotAuthorizedError, :forbidden, '403: Forbidden', '403: Forbidden', 'Not authorized'
|
||||
@@ -180,6 +182,7 @@ RSpec.describe 'Error handling', type: :request do
|
||||
|
||||
let(:user) { :agent }
|
||||
|
||||
include_examples 'handles exception', ActionController::BadRequest, :bad_request, '400: Bad Request', '400'
|
||||
include_examples 'handles exception', Exceptions::NotAuthorized, :unauthorized, '401: Unauthorized', '401'
|
||||
include_examples 'handles exception', Exceptions::Forbidden, :forbidden, '403: Forbidden', '403'
|
||||
include_examples 'handles exception', Pundit::NotAuthorizedError, :forbidden, '403: Forbidden', '403', 'Not authorized'
|
||||
@@ -193,6 +196,7 @@ RSpec.describe 'Error handling', type: :request do
|
||||
|
||||
let(:user) { :admin }
|
||||
|
||||
include_examples 'handles exception', ActionController::BadRequest, :bad_request, '400: Bad Request', '400'
|
||||
include_examples 'handles exception', Exceptions::NotAuthorized, :unauthorized, '401: Unauthorized', '401'
|
||||
include_examples 'handles exception', Exceptions::Forbidden, :forbidden, '403: Forbidden', '403'
|
||||
include_examples 'handles exception', Pundit::NotAuthorizedError, :forbidden, '403: Forbidden', '403', 'Not authorized'
|
||||
|
||||
@@ -31,6 +31,20 @@ RSpec.describe 'Integration Check MK', type: :request do
|
||||
expect(json_response['error']).to eq('The provided token is invalid.')
|
||||
end
|
||||
|
||||
it 'does fail with invalid parameter encoding and logs the request', :aggregate_failures do
|
||||
post "/api/v1/integration/check_mk/#{Setting.get('check_mk_token')}", params: 'event_id=1&host=a%DFb&state=down', headers: { 'CONTENT_TYPE' => 'application/x-www-form-urlencoded' }
|
||||
expect(response).to have_http_status(:bad_request)
|
||||
expect(json_response['error']).to include('Invalid encoding for parameter')
|
||||
expect(HttpLog.where(facility: 'check_mk', status: 400).count).to eq(1)
|
||||
end
|
||||
|
||||
it 'does fail with malformed JSON body and logs the request', :aggregate_failures do
|
||||
post "/api/v1/integration/check_mk/#{Setting.get('check_mk_token')}", params: '{"event_id":', headers: { 'CONTENT_TYPE' => 'application/json' }
|
||||
expect(response).to have_http_status(:bad_request)
|
||||
expect(json_response['error']).to eq('Error occurred while parsing request parameters')
|
||||
expect(HttpLog.where(facility: 'check_mk', status: 400).count).to eq(1)
|
||||
end
|
||||
|
||||
context 'service check' do
|
||||
it 'does create ticket with customer email' do
|
||||
params = {
|
||||
|
||||
Reference in New Issue
Block a user