diff --git a/lib/PostText/Controller/Thread.pm b/lib/PostText/Controller/Thread.pm index d0cd240..8bb41b0 100644 --- a/lib/PostText/Controller/Thread.pm +++ b/lib/PostText/Controller/Thread.pm @@ -15,8 +15,15 @@ sub create($self) { $v->required('title' )->size(1, 127); $v->required('body' )->size(2, $body_limit); $v->optional('preview'); + $v->csrf_protect; - if ($v->has_error) { + if ($v->has_error('csrf_token')) { + $self->stash( + status => 403, + error => 'Something went wrong, please try again. 🥺' + ); + } + elsif ($v->has_error) { $self->stash(status => 400) } else { diff --git a/t/post.t b/t/post.t index 2b22093..fac43b4 100644 --- a/t/post.t +++ b/t/post.t @@ -51,12 +51,15 @@ $t->post_ok($bump_thread_url, form => \%good_captcha) $t->ua->max_redirects(1); subtest 'Post new thread', sub { + my $csrf_token; + # GET $t->get_ok('/human/thread/post')->status_is(200) - ->element_exists('form input[name="author"]' ) - ->element_exists('form input[name="title"]' ) - ->element_exists('form textarea[name="body"]') - ->element_exists('form button[type="submit"]' ) + ->element_exists('form input[name="author"]' ) + ->element_exists('form input[name="title"]' ) + ->element_exists('form textarea[name="body"]' ) + ->element_exists('form button[type="submit"]' ) + ->element_exists('form input[name="csrf_token"]') ->text_like(h2 => qr/New Thread/); # POST @@ -64,17 +67,31 @@ subtest 'Post new thread', sub { ->element_exists('form input[name="author"]' ) ->element_exists('form input[name="title"]' ) ->element_exists('form textarea[name="body"]') - ->element_exists('form button[type="submit"]' ) + ->element_exists('form button[type="submit"]') ->text_like(h2 => qr/New Thread/); + # No CSRF token + $t->post_ok('/human/thread/post', form => \%valid_thread) + ->status_is(403) + ->text_like(p => qr/Something went wrong/); + + $invalid_title{'csrf_token'} = + $t->tx->res->dom->at('input[name="csrf_token"]')->val; + $t->post_ok('/human/thread/post', form => \%invalid_title) ->status_is(400) ->text_like(p => qr/Must be between/); + $invalid_thread{'csrf_token'} = + $t->tx->res->dom->at('input[name="csrf_token"]')->val; + $t->post_ok('/human/thread/post', form => \%invalid_thread) ->status_is(400) ->text_like(p => qr/Must be between/); + $valid_thread{'csrf_token'} = + $t->tx->res->dom->at('input[name="csrf_token"]')->val; + $t->post_ok('/human/thread/post', form => \%valid_thread) ->status_is(200) ->text_like(h2 => qr/Thread #\d+/); @@ -85,22 +102,22 @@ subtest 'Post new remark', sub { $t->get_ok('/human/remark/post/1')->status_is(200) ->element_exists('form input[name="author"]' ) ->element_exists('form textarea[name="body"]') - ->element_exists('form button[type="submit"]' ) + ->element_exists('form button[type="submit"]') ->text_like(h2 => qr/Remark on Thread #/); $t->get_ok('/human/remark/post/65536')->status_is(404) ->text_like(p => qr/Thread not found/); # Test the remark-to-remark thing $t->get_ok('/human/remark/post/1/1')->status_is(200) - ->element_exists('form input[name="author"]' ) - ->element_exists('form textarea[name="body"]') + ->element_exists('form input[name="author"]' ) + ->element_exists('form textarea[name="body"]' ) ->element_exists('form button[type="submit"]' ) ->element_exists('a[href$="/remark/single/1"]') ->text_like(h3 => qr/Last Remark/); # POST $t->post_ok('/human/remark/post/1')->status_is(200) - ->element_exists('form input[name="author"]' ) - ->element_exists('form textarea[name="body"]') + ->element_exists('form input[name="author"]' ) + ->element_exists('form textarea[name="body"]' ) ->element_exists('form button[type="submit"]' ) ->text_like(h2 => qr/Remark on Thread #/); diff --git a/templates/thread/create.html.ep b/templates/thread/create.html.ep index 11a19d7..c5c2547 100644 --- a/templates/thread/create.html.ep +++ b/templates/thread/create.html.ep @@ -61,5 +61,6 @@ <%= check_box preview => 1, id => 'preview' %> <%= label_for preview => 'Preview' %> + <%= csrf_field %>