Sanitize HTML in body; enforce NOT NULL on body; template clean-up

This commit is contained in:
swagg boi 2023-05-03 21:05:57 -04:00
parent 5e857b9426
commit ab0f53ba26
12 changed files with 68 additions and 24 deletions

View file

@ -58,9 +58,10 @@ Run the tests locally (against development environment):
## TODOs ## TODOs
1. Use something like HTML::Restrict on thread/remark body
1. CSS 1. CSS
1. "All new posts flagged" mode (require approval for new posts) 1. "All new posts flagged" mode (require approval for new posts)
1. Tests for mod-only user?
1. Check input validation
## Crazy future ideas ## Crazy future ideas

View file

@ -9,3 +9,4 @@ requires 'Date::Format';
requires 'XML::RSS'; requires 'XML::RSS';
requires 'CSS::Minifier::XS'; requires 'CSS::Minifier::XS';
requires 'Text::Markdown'; requires 'Text::Markdown';
requires 'HTML::Restrict';

View file

@ -6,6 +6,7 @@ use Mojo::Base 'Mojolicious', -signatures;
use Mojo::Pg; use Mojo::Pg;
use Crypt::Passphrase; use Crypt::Passphrase;
use Text::Markdown qw{markdown}; use Text::Markdown qw{markdown};
use HTML::Restrict;
# The local libs # The local libs
use PostText::Model::Thread; use PostText::Model::Thread;
@ -29,12 +30,22 @@ sub startup($self) {
) )
}); });
$self->helper(hr => sub ($c) {
state $hr = HTML::Restrict->new(strip_enclosed_content => [])
});
$self->helper(thread => sub ($c) { $self->helper(thread => sub ($c) {
state $thread = PostText::Model::Thread->new(pg => $c->pg) state $thread = PostText::Model::Thread->new(
pg => $c->pg,
hr => $c->hr
)
}); });
$self->helper(remark => sub ($c) { $self->helper(remark => sub ($c) {
state $remark = PostText::Model::Remark->new(pg => $c->pg) state $remark = PostText::Model::Remark->new(
pg => $c->pg,
hr => $c->hr
)
}); });
$self->helper(moderator => sub ($c) { $self->helper(moderator => sub ($c) {
@ -70,7 +81,7 @@ sub startup($self) {
# Finish configuring some things # Finish configuring some things
$self->secrets($self->config->{'secrets'}) || die $@; $self->secrets($self->config->{'secrets'}) || die $@;
$self->pg->migrations->from_dir('migrations')->migrate(11); $self->pg->migrations->from_dir('migrations')->migrate(12);
if (my $threads_per_page = $self->config->{'threads_per_page'}) { if (my $threads_per_page = $self->config->{'threads_per_page'}) {
$self->thread->per_page($threads_per_page) $self->thread->per_page($threads_per_page)

View file

@ -2,7 +2,7 @@ package PostText::Model::Remark;
use Mojo::Base -base, -signatures; use Mojo::Base -base, -signatures;
has 'pg'; has [qw{pg hr}];
has per_page => 5; has per_page => 5;
@ -28,7 +28,8 @@ sub by_page_for($self, $thread_id, $this_page = 1) {
} }
sub create($self, $thread_id, $author, $body, $hidden = 0, $flagged = 0) { sub create($self, $thread_id, $author, $body, $hidden = 0, $flagged = 0) {
my @data = ($thread_id, $author, $body, $hidden, $flagged); my $clean_body = $self->hr->process($body);
my @data = ($thread_id, $author, $clean_body, $hidden, $flagged);
$self->pg->db->query(<<~'END_SQL', @data); $self->pg->db->query(<<~'END_SQL', @data);
INSERT INTO remarks ( INSERT INTO remarks (

View file

@ -2,14 +2,15 @@ package PostText::Model::Thread;
use Mojo::Base -base, -signatures; use Mojo::Base -base, -signatures;
has 'pg'; has [qw{pg hr}];
has per_page => 5; has per_page => 5;
has date_format => 'Dy, FMDD Mon YYYY HH24:MI:SS TZ'; has date_format => 'Dy, FMDD Mon YYYY HH24:MI:SS TZ';
sub create($self, $author, $title, $body, $hidden = 0, $flagged = 0) { sub create($self, $author, $title, $body, $hidden = 0, $flagged = 0) {
my @data = ($author, $title, $body, $hidden, $flagged); my $clean_body = $self->hr->process($body);
my @data = ($author, $title, $clean_body, $hidden, $flagged);
$self->pg->db->query(<<~'END_SQL', @data)->hash->{'thread_id'}; $self->pg->db->query(<<~'END_SQL', @data)->hash->{'thread_id'};
INSERT INTO threads ( INSERT INTO threads (

7
migrations/12/down.sql Normal file
View file

@ -0,0 +1,7 @@
ALTER TABLE threads
ALTER COLUMN thread_body
DROP NOT NULL;
ALTER TABLE remarks
ALTER COLUMN remark_body
DROP NOT NULL;

9
migrations/12/up.sql Normal file
View file

@ -0,0 +1,9 @@
-- Input validation may fail for body if user submits only an HTML tag
ALTER TABLE threads
ALTER COLUMN thread_body
SET NOT NULL;
ALTER TABLE remarks
ALTER COLUMN remark_body
SET NOT NULL;

View file

@ -3,13 +3,13 @@
<h2><%= title %></h2> <h2><%= title %></h2>
<div class="about body"> <div class="about body">
<p>Post::Text is a <a href="">textboard</a> a bit like 2channel. You <p>Post::Text is a <a href="">textboard</a> a bit like 2channel. You
can post whatever you want anonymously just please mind the can post whatever you want anonymously just please mind the <%=
<%= link_to rules => 'rules_page' %>. Markdown is supported for link_to rules => 'rules_page' %>. Markdown is supported for
formatting posts using the formatting posts using the <a
<a href="https://daringfireball.net/projects/markdown/syntax"> href="https://daringfireball.net/projects/markdown/syntax">
original implementation from The Daring Fireball</a>. For original implementation from The Daring Fireball</a>. For example,
example, back-ticks are for <em>inline code</em> while back-ticks are for <em>inline code</em> while indentation should
indentation should be used if you want an entire code bock:</p> be used if you want an entire code bock:</p>
<pre><code> <pre><code>
This is `inline_code()` and so is ```this()```. This is incorrect: This is `inline_code()` and so is ```this()```. This is incorrect:
@ -24,9 +24,10 @@ This is correct for a multi-line code block:
if (true) { if (true) {
do_stuff(); do_stuff();
} }
You can use an actual tab character or four spaces to indent.
</code></pre> </code></pre>
<p>You can use an actual tab character or four spaces to indent.
<strong>Only Markdown is supported, HTML will be filtered
out.</strong></p>
<p>There is a button for users to 'flag' a post for review by a <p>There is a button for users to 'flag' a post for review by a
moderator. If you need further assistance you can reach out to the moderator. If you need further assistance you can reach out to the
<a href="mailto:swaggboi@slackware.uk">webmaster</a>. There is <a href="mailto:swaggboi@slackware.uk">webmaster</a>. There is

View file

@ -6,7 +6,9 @@
<nav class="id">#<%= $remark->{'id'} %></nav> <nav class="id">#<%= $remark->{'id'} %></nav>
<h4 class="date"><%= $remark->{'date'} %></h4> <h4 class="date"><%= $remark->{'date'} %></h4>
<h5 class="author"><%= $remark->{'author'} %></h5> <h5 class="author"><%= $remark->{'author'} %></h5>
<div class="body"><%== markdown $remark->{'body'} %></div> <div class="body">
<%== markdown $remark->{'body'} =%>
</div>
</article> </article>
</div> </div>
<nav> <nav>

View file

@ -37,7 +37,9 @@
<h3 class="title"><%= $thread->{'title'} %></h3> <h3 class="title"><%= $thread->{'title'} %></h3>
<h4 class="date"><%= $thread->{'date'} %></h4> <h4 class="date"><%= $thread->{'date'} %></h4>
<h5 class="author"><%= $thread->{'author'} %></h5> <h5 class="author"><%= $thread->{'author'} %></h5>
<div class="body"><%== markdown $thread->{'body'} %></div> <div class="body">
<%== markdown $thread->{'body'} =%>
</div>
</article> </article>
</div> </div>
<% if (my $last_remark_id = $last_remark->{'id'}) { =%> <% if (my $last_remark_id = $last_remark->{'id'}) { =%>
@ -50,7 +52,9 @@
</nav> </nav>
<h4 class="date"><%= $last_remark->{'date'} %></h4> <h4 class="date"><%= $last_remark->{'date'} %></h4>
<h5 class="author"><%= $last_remark->{'author'} %></h5> <h5 class="author"><%= $last_remark->{'author'} %></h5>
<div class="body"><%== markdown $last_remark->{'body'} %></div> <div class="body">
<%== markdown $last_remark->{'body'} =%>
</div>
</article> </article>
</div> </div>
<% } =%> <% } =%>

View file

@ -6,7 +6,9 @@
<h3 class="title"><%= $thread->{'title'} %></h3> <h3 class="title"><%= $thread->{'title'} %></h3>
<h4 class="date"><%= $thread->{'date'} %></h4> <h4 class="date"><%= $thread->{'date'} %></h4>
<h5 class="author"><%= $thread->{'author'} %></h5> <h5 class="author"><%= $thread->{'author'} %></h5>
<div class="body"><%== markdown $thread->{'body'} %></div> <div class="body">
<%== markdown $thread->{'body'} =%>
</div>
</article> </article>
</div> </div>
<nav> <nav>
@ -32,7 +34,9 @@
</nav> </nav>
<h4 class="date"><%= $remark->{'date'} %></h4> <h4 class="date"><%= $remark->{'date'} %></h4>
<h5 class="author"><%= $remark->{'author'} %></h5> <h5 class="author"><%= $remark->{'author'} %></h5>
<div class="body"><%== markdown $remark->{'body'} %></div> <div class="body">
<%== markdown $remark->{'body'} =%>
</div>
<nav class="flag"> <nav class="flag">
<%= link_to Flag => flag_remark => {remark_id => $remark->{'id'}} %> <%= link_to Flag => flag_remark => {remark_id => $remark->{'id'}} %>
</nav> </nav>

View file

@ -15,7 +15,9 @@
</h3> </h3>
<h4 class="date"><%= $thread->{'date'} %></h4> <h4 class="date"><%= $thread->{'date'} %></h4>
<h5 class="author"><%= $thread->{'author'} %></h5> <h5 class="author"><%= $thread->{'author'} %></h5>
<div class="body"><%== markdown truncate_text $thread->{'body'} %></div> <div class="body">
<%== markdown truncate_text $thread->{'body'} =%>
</div>
<nav> <nav>
<%= link_to Remark => post_remark => {thread_id => $thread->{'id'}} %> <%= link_to Remark => post_remark => {thread_id => $thread->{'id'}} %>
<%= link_to url_for(single_thread => {thread_id => $thread->{'id'}}) <%= link_to url_for(single_thread => {thread_id => $thread->{'id'}})