From e4aad2ccbd82011d6a99bde869eb5b7f84e11877 Mon Sep 17 00:00:00 2001 From: swag Date: Sat, 8 Jan 2022 21:21:22 -0500 Subject: [PATCH] Validate input --- README.md | 2 +- assets/css/swagg.css | 8 ++++++- guestbook-ng.pl | 53 +++++++++++++++++++++++------------------- templates/sign.html.ep | 33 ++++++++++++++++---------- 4 files changed, 58 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 5962080..919af7f 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,6 @@ Add the `-v` option for more verbose output ## TODOs -1. Input validation +1. More tests 1. /spam route would be interesting 1. Include the total number of visitors or messages diff --git a/assets/css/swagg.css b/assets/css/swagg.css index 4dfdc9a..92f33b2 100644 --- a/assets/css/swagg.css +++ b/assets/css/swagg.css @@ -132,7 +132,7 @@ h1#top { } .error { - border-style: solid; + border-style: dashed; border-color: red; color: red; padding: 1em; @@ -140,6 +140,12 @@ h1#top { margin-bottom: 1em; } +.field-with-error { + border-style: dotted; + border-color: red; + padding-left: 1em; +} + @media screen and (max-width: 1023px) { .inner { max-width: 95%; diff --git a/guestbook-ng.pl b/guestbook-ng.pl index 242a67e..40ab7ea 100755 --- a/guestbook-ng.pl +++ b/guestbook-ng.pl @@ -46,6 +46,9 @@ under sub ($c) { $c->session(expiration => 604800); + $c->stash(status => 403) + if $c->flash('error') eq 'This message was flagged as spam'; + 1; }; @@ -64,7 +67,9 @@ get '/' => sub ($c) { } => 'index'; any [qw{GET POST}], '/sign' => sub ($c) { - if ($c->req->method() eq 'POST') { + my $v = $c->validation(); + + if ($c->req->method eq 'POST' && $v->has_data) { my $name = $c->param('name') || 'Anonymous'; my $url = $c->param('url'); my $message = $c->param('message'); @@ -73,37 +78,37 @@ any [qw{GET POST}], '/sign' => sub ($c) { $message =~ /$RE{URI}{HTTP}{-scheme => qr}/ ? 1 : 0; - if ($message) { + $v->required('name' )->size(1, 63); + $v->required('message')->size(2, 2000); + $v->optional('url', 'not_empty')->size(1, 255) + ->like(qr/$RE{URI}{HTTP}{-scheme => qr}/); + + unless ($v->has_error) { $c->message->create_post($name, $message, $url, $spam); $c->flash(error => 'This message was flagged as spam') if $spam; $c->redirect_to('index'); } - else { - $c->flash(error => 'Message cannot be blank'); - $c->redirect_to('sign'); - } } - else { - # Try to randomize things for the CAPTCHA challenge. The - # string 'false' actually evaluates to true so this is an - # attempt to confuse a (hypothetical) bot that would try to - # select what it thinks is the right answer - my @answers = shuffle(0, 'false', undef); - my $right_answer_label = 'I\'m ready to sign (choose this one)'; - my @wrong_answer_labels = shuffle( - 'I don\'t want to sign (wrong answer)', - 'This is spam/I\'m a bot, do not sign' - ); - $c->stash( - answers => \@answers, - right_answer_label => $right_answer_label, - wrong_answer_labels => \@wrong_answer_labels - ); + # Try to randomize things for the CAPTCHA challenge. The + # string 'false' actually evaluates to true so this is an + # attempt to confuse a (hypothetical) bot that would try to + # select what it thinks is the right answer + my @answers = shuffle(0, 'false', undef); + my $right_answer_label = 'I\'m ready to sign (choose this one)'; + my @wrong_answer_labels = shuffle( + 'I don\'t want to sign (wrong answer)', + 'This is spam/I\'m a bot, do not sign' + ); - $c->render(); - } + $c->stash( + answers => \@answers, + right_answer_label => $right_answer_label, + wrong_answer_labels => \@wrong_answer_labels + ); + + $c->render(); }; # Send it diff --git a/templates/sign.html.ep b/templates/sign.html.ep index 09f7f22..86a2b58 100644 --- a/templates/sign.html.ep +++ b/templates/sign.html.ep @@ -4,27 +4,36 @@
<%= label_for name => 'Name' %> - <%= input_tag name =>'Anonymous', maxlength => 63, minlength => 1 %> + <%= text_field name =>'Anonymous', maxlength => 63, minlength => 1 %>
<%= label_for url => 'Homepage URL' %> - <%= input_tag 'url', maxlength => 255 %> + <%= text_field 'url', maxlength => 255 %> + <% if (my $error = validation->error('url')) { =%> +

URL does not appear to be + <%= link_to 'RFC 2616', + 'https://datatracker.ietf.org/doc/html/rfc2616/#section-3.2.2' %> + compliant.

+ <% } =%>
- <%= label_for message => 'Message' %> - + <%= label_for message => 'Message' %> + <%= text_area 'message', + maxlength => 2000, + minlength => 2, + required => 'true', + rows => 6 %> + <% if (my $error = validation->error('message')) { =%> +

Message must be less than 2,000 + characters and cannot be blank.

+ <% } =%>

SwaggCAPTCHAâ„¢

<% for my $answer (@$answers) { =%> - <%= radio_button answer => $answer %> - <%= label_for answer => - $answer ? $right_answer_label : pop @$wrong_answer_labels %> + <%= radio_button answer => $answer %> + <%= label_for answer => + $answer ? $right_answer_label : pop @$wrong_answer_labels %> <% } =%>
<%= submit_button 'Sign it', class => 'win95button' %>