Skip to content

Commit

Permalink
Better logging of page_error
Browse files Browse the repository at this point in the history
This adds more logging to page_error, including the HTTP status code,
the request IP address, an internal message to show the reason for the
error, and a random string to help identify the error in the logs. This
string is also shown on the error page to the user, to improve the
chance of it being included in any screenshots from users.
  • Loading branch information
davea authored and dracos committed Dec 2, 2024
1 parent a104bc4 commit c5eef33
Show file tree
Hide file tree
Showing 21 changed files with 105 additions and 18 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## Releases

* Unreleased
- Development improvements
- More logging when page_error is called, to aid troubleshooting. #5279

* v6.0 (14th November 2024)
- Front end improvements:
- Include requirements for redeeming the link in the email change confirmation mail. #4422
Expand Down
20 changes: 11 additions & 9 deletions perllib/FixMyStreet/App/Controller/Auth.pm
Original file line number Diff line number Diff line change
Expand Up @@ -465,20 +465,22 @@ sub check_csrf_token : Private {
$c->stash->{csrf_time} = $time;
my $gen_token = $c->forward('get_csrf_token');
delete $c->stash->{csrf_time};
$c->detach('no_csrf_token')
unless $time
&& $time > time() - 3600
&& $token eq $gen_token;

my $cutoff = time() - 3600;
my $valid_time = $time && $time > $cutoff;
my $valid_token = $token eq $gen_token;
unless ($valid_time && $valid_token) {
my $msg = "Invalid CSRF token: ";
$msg .= "$token != $gen_token " unless $valid_token;
$msg .= "$time < $cutoff" unless $valid_time;
$c->stash->{internal_message} = $msg;
$c->detach('/page_error_400_bad_request', []);
}

# Also check recaptcha if needed
$c->cobrand->call_hook('check_recaptcha');
}

sub no_csrf_token : Private {
my ($self, $c) = @_;
$c->detach('/page_error_400_bad_request', []);
}

=item common_password
Returns 1/0 depending on if password is common/breached or not.
Expand Down
15 changes: 12 additions & 3 deletions perllib/FixMyStreet/App/Controller/Auth/Social.pm
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,10 @@ sub oidc_sign_in : Private {
$c->detach( '/page_error_403_access_denied', [] ) if FixMyStreet->config('SIGNUPS_DISABLED');

my $cfg = $c->forward('oidc_config');
$c->detach( '/page_error_400_bad_request', [] ) unless $cfg;
unless ($cfg) {
$c->stash->{internal_message} = 'OIDC config not found';
$c->detach( '/page_error_400_bad_request', [] );
}

my $oidc = $c->forward('oidc');
my $nonce = $self->generate_nonce();
Expand Down Expand Up @@ -269,7 +272,10 @@ sub oidc_callback: Path('/auth/OIDC') : Args(0) {
$c->detach('oauth_failure');
}
}
$c->detach('/page_error_400_bad_request', []) unless $c->get_param('code') && $c->get_param('state');
unless ( $c->get_param('code') && $c->get_param('state') ) {
$c->stash->{internal_message} = "Social::oidc_callback missing code (" . $c->get_param('code') . ") or state (" . $c->get_param('state') . ")";
$c->detach('/page_error_400_bad_request', []) ;
}

# After a password reset on the OIDC endpoint the user isn't properly logged
# in, so redirect them to the usual OIDC login process.
Expand All @@ -290,7 +296,10 @@ sub oidc_callback: Path('/auth/OIDC') : Args(0) {
}

# The only other valid state param is 'login' at this point.
$c->detach('/page_error_400_bad_request', []) unless $c->get_param('state') eq 'login';
unless ($c->get_param('state') eq 'login') {
$c->stash->{internal_message} = "Social::oidc_callback invalid state: " . $c->get_param('state');
$c->detach('/page_error_400_bad_request', []) ;
}

my $token = eval {
$oidc->get_access_token(
Expand Down
2 changes: 2 additions & 0 deletions perllib/FixMyStreet/App/Controller/Form.pm
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ sub load_form {
);

if (!$form->has_current_page) {
$c->stash->{internal_error} = "Form doesn't have current page";
$c->detach('/page_error_400_bad_request', [ 'Bad request' ]);
}

Expand Down Expand Up @@ -114,6 +115,7 @@ sub get_page : Private {
my $process = $c->get_param('process') || '';
$goto = $self->first_page($c) unless $goto || $process;
if ($goto && $process) {
$c->stash->{internal_error} = "Both goto and process parameters set";
$c->detach('/page_error_400_bad_request', [ 'Bad request' ]);
}

Expand Down
20 changes: 16 additions & 4 deletions perllib/FixMyStreet/App/Controller/My.pm
Original file line number Diff line number Diff line change
Expand Up @@ -349,15 +349,27 @@ sub anonymize : Path('anonymize') {
$object = $c->stash->{problem};
} elsif ($id = $c->get_param('update')) {
$c->stash->{update} = $object = $c->model('DB::Comment')->find({ id => $id });
$c->detach('/page_error_400_bad_request') unless $object;
unless ($object) {
$c->stash->{internal_error} = "No update found for ID $id";
$c->detach('/page_error_400_bad_request');
}
} else {
$c->detach('/page_error_404_not_found');
}
$c->detach('/page_error_400_bad_request') unless $c->user->id == $object->user_id;
$c->detach('/page_error_400_bad_request') if $object->anonymous;
unless ($c->user->id == $object->user_id) {
$c->stash->{internal_error} = "User " . $c->user->id . " does not own this object with ID " . $object->id;
$c->detach('/page_error_400_bad_request');
}
if ($object->anonymous) {
$c->stash->{internal_error} = "Object " . $object->id . " is already anonymous";
$c->detach('/page_error_400_bad_request');
}

if ($c->get_param('hide') || $c->get_param('hide_everywhere')) {
$c->detach('/page_error_400_bad_request') unless $c->req->method eq 'POST';
unless ($c->req->method eq 'POST') {
$c->stash->{internal_error} = "Not a POST request: " . $c->req->method;
$c->detach('/page_error_400_bad_request');
}
$c->forward('/auth/check_csrf_token');
if ($c->get_param('hide')) {
$object->update({ anonymous => 1 });
Expand Down
1 change: 1 addition & 0 deletions perllib/FixMyStreet/App/Controller/Questionnaire.pm
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ sub check_questionnaire : Private {
my $problem_url = $c->cobrand->base_url_for_report( $problem ) . $problem->url;
my $contact_url = $c->uri_for( "/contact" );
my $message = sprintf(_("You have already answered this questionnaire. If you have a question, please <a href='%s'>get in touch</a>, or <a href='%s'>view your problem</a>.\n"), $contact_url, $problem_url);
$c->stash->{internal_message} = "Questionnaire ID " . $questionnaire->id;
$c->detach('/page_error_400_bad_request', [ $message ]);
}

Expand Down
15 changes: 13 additions & 2 deletions perllib/FixMyStreet/App/Controller/Root.pm
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
package FixMyStreet::App::Controller::Root;
use Digest::MD5 qw(md5_hex);
use mySociety::Random qw(random_bytes);
use Moose;
use namespace::autoclean;

Expand Down Expand Up @@ -145,8 +147,17 @@ sub page_error_500_internal_error : Private {

sub page_error : Private {
my ($self, $c, $error_msg, $code) = @_;
$c->stash->{template} = 'errors/generic.html';
$c->stash->{message} = $error_msg || _('Unknown error');
$c->stash->{template} = 'errors/generic.html';
$c->stash->{message} = $error_msg || _('Unknown error');
$c->stash->{error_id} = substr(md5_hex(random_bytes(12, 1)), 0, 6);

my $addr = $c->req->address;
$c->log->info(
"page_error (HTTP $code, IP $addr, ID " .
$c->stash->{error_id} . "): " .
"User message: " . $c->stash->{message} .
($c->stash->{internal_message} ? "; Internal message: " . $c->stash->{internal_message} : "")
);
$c->response->status($code);
}

Expand Down
3 changes: 3 additions & 0 deletions t/app/controller/admin.t
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ package main;

use FixMyStreet::TestMech;

FixMyStreet::App->log->disable('info');
END { FixMyStreet::App->log->enable('info'); }

my $mech = FixMyStreet::TestMech->new;

my $user = $mech->create_user_ok('[email protected]', name => 'Test User');
Expand Down
3 changes: 3 additions & 0 deletions t/app/controller/admin/triage.t
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use FixMyStreet::TestMech;
use FixMyStreet::Script::Alerts;

FixMyStreet::App->log->disable('info');
END { FixMyStreet::App->log->enable('info'); }

my $mech = FixMyStreet::TestMech->new;

my $user = $mech->create_user_ok('[email protected]', name => 'Test User');
Expand Down
4 changes: 4 additions & 0 deletions t/app/controller/auth_profile.t
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ package main;

use Test::MockModule;
use FixMyStreet::TestMech;

FixMyStreet::App->log->disable('info');
END { FixMyStreet::App->log->enable('info'); }

my $mech = FixMyStreet::TestMech->new;

my $resolver = Test::MockModule->new('Email::Valid');
Expand Down
3 changes: 3 additions & 0 deletions t/app/controller/index.t
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ use FixMyStreet::TestMech;
use DateTime;
use Web::Scraper;

FixMyStreet::App->log->disable('info');
END { FixMyStreet::App->log->enable('info'); }

my $mech = FixMyStreet::TestMech->new;

use t::Mock::Nominatim;
Expand Down
4 changes: 4 additions & 0 deletions t/app/controller/my.t
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use FixMyStreet::TestMech;

FixMyStreet::App->log->disable('info');
END { FixMyStreet::App->log->enable('info'); }

my $mech = FixMyStreet::TestMech->new;

$mech->get_ok('/my');
Expand Down
4 changes: 4 additions & 0 deletions t/app/controller/my_planned.t
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use FixMyStreet::TestMech;

FixMyStreet::App->log->disable('info');
END { FixMyStreet::App->log->enable('info'); }

my $mech = FixMyStreet::TestMech->new;

$mech->get_ok('/my/planned');
Expand Down
3 changes: 3 additions & 0 deletions t/app/controller/questionnaire.t
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ use DateTime;

use FixMyStreet::TestMech;

FixMyStreet::App->log->disable('info');
END { FixMyStreet::App->log->enable('info'); }

ok( my $mech = FixMyStreet::TestMech->new, 'Created mech object' );

my $user = $mech->create_user_ok('[email protected]', name => 'Test User');
Expand Down
3 changes: 3 additions & 0 deletions t/app/controller/report_updates.t
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ use Web::Scraper;
use Path::Class;
use DateTime;

FixMyStreet::App->log->disable('info');
END { FixMyStreet::App->log->enable('info'); }

my $mech = FixMyStreet::TestMech->new;

my $user = $mech->create_user_ok('[email protected]', name => 'Test User');
Expand Down
3 changes: 3 additions & 0 deletions t/app/controller/waste_echo.t
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ use Test::MockTime qw(set_fixed_time);
use FixMyStreet::TestMech;
use FixMyStreet::Cobrand::Merton;

FixMyStreet::App->log->disable('info');
END { FixMyStreet::App->log->enable('info'); }

my $mech = FixMyStreet::TestMech->new;

$mech->create_body_ok(2500, 'Merton Council', { cobrand => 'merton' });
Expand Down
3 changes: 3 additions & 0 deletions templates/web/base/errors/generic.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
<div class="confirmation-header confirmation-header--[% header_class %]">
<h1>[% title %]</h1>
<p>[% message | safe %]</p>
[% IF error_id %]
<small>[% tprintf(loc('Code: %s'), error_id) %]</small>
[% END %]
</div>

[% INCLUDE 'footer.html' %]
Expand Down
3 changes: 3 additions & 0 deletions templates/web/brent/errors/generic.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ <h1>My Account login</h1>
<div class="confirmation-header confirmation-header--[% header_class %]">
<h1>[% title %]</h1>
<p>[% message | safe %]</p>
[% IF error_id %]
<small>[% tprintf(loc('Code: %s'), error_id) %]</small>
[% END %]
</div>
[% END %]

Expand Down
3 changes: 3 additions & 0 deletions templates/web/highwaysengland/errors/generic.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ <h1>Single sign on login</h1>
<div class="confirmation-header confirmation-header--failure">
<h1>[% title %]</h1>
<p>[% message | safe %]</p>
[% IF error_id %]
<small>[% tprintf(loc('Code: %s'), error_id) %]</small>
[% END %]
</div>
[% END %]

Expand Down
3 changes: 3 additions & 0 deletions templates/web/tfl/errors/generic.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ <h1>My Account login</h1>
<div class="confirmation-header confirmation-header--[% header_class %]">
<h1>[% title %]</h1>
<p>[% message | safe %]</p>
[% IF error_id %]
<small>[% tprintf(loc('Code: %s'), error_id) %]</small>
[% END %]
</div>
[% END %]

Expand Down
4 changes: 4 additions & 0 deletions web/cobrands/sass/_base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3074,6 +3074,10 @@ a#geolocate_link {
word-break: break-word; // It prevents screen overflow when there is a long url, when a page has not been found.
}

&.confirmation-header--failure small {
color: #888888;
}

& > :last-child {
margin-bottom: 0;
}
Expand Down

0 comments on commit c5eef33

Please sign in to comment.