Limit a story to 64 KB before HTML Purifier runs.
A preview purified any posted story, even without a token or a captcha. A few megabytes took seconds and hundreds of megabytes.
This commit is contained in:
@@ -3,6 +3,13 @@ require_once('open_db.lib.php');
|
||||
require_once(__DIR__ . '/lib/purify.inc.php');
|
||||
require_once(__DIR__ . '/html.lib.php');
|
||||
|
||||
// The longest story HTML, in bytes. HTML Purifier needs seconds for a few megabytes.
|
||||
define('MAX_STORY_BYTES', 65536);
|
||||
|
||||
function story_too_long($story) {
|
||||
return strlen((string)$story) > MAX_STORY_BYTES;
|
||||
}
|
||||
|
||||
function display_stories($id = NULL) {
|
||||
global $db, $is_admin, $editable, $action;
|
||||
if (is_null($id))
|
||||
@@ -10,17 +17,20 @@ function display_stories($id = NULL) {
|
||||
$search = get_search_args();
|
||||
$page = '';
|
||||
$nolink = NULL;
|
||||
$posted = FALSE;
|
||||
|
||||
if ($action == 'preview') {
|
||||
global $file_src;
|
||||
$date = 'Awaiting posting';
|
||||
$result = get_post_data(TRUE);
|
||||
$nolink = TRUE;
|
||||
$posted = TRUE;
|
||||
} else if ($action == 'submit') {
|
||||
$date = 'Posted '.gmdate("Y-m-d H:i");
|
||||
if (!$is_admin) {
|
||||
$result = get_post_data(TRUE);
|
||||
$nolink = TRUE;
|
||||
$posted = TRUE;
|
||||
} else {
|
||||
$result = get_query($id);
|
||||
}
|
||||
@@ -90,6 +100,8 @@ date("Y-m-d H:i", strtotime($entry['date'])) . '</time>';
|
||||
} else {
|
||||
$page .= '<img src="/display?id='.$entry_id.'" alt="'.$title.'" />';
|
||||
}
|
||||
} else if ($posted && story_too_long($entry['story'])) {
|
||||
$page .= '<p>The story is too long to show.</p>'."\n";
|
||||
} else {
|
||||
$page .= purify_story($entry['story'])."\n";
|
||||
}
|
||||
|
||||
@@ -206,6 +206,10 @@ function validate_data() {
|
||||
$validate['title'] = "You have not submitted a title.";
|
||||
}
|
||||
|
||||
if (story_too_long($story)) {
|
||||
$validate['story'] = 'The story is too long. Please shorten it.';
|
||||
}
|
||||
|
||||
if ($remove_img && !$story) {
|
||||
$validate['story'] = "You've tried to remove an image without replacing it with text.";
|
||||
}
|
||||
|
||||
@@ -111,6 +111,15 @@ class DisplayStoriesTest extends TestCase {
|
||||
$this->assertStringNotContainsString('x()', $page);
|
||||
}
|
||||
|
||||
// HTML Purifier needs seconds for a few megabytes, so a posted story over the limit is not shown
|
||||
public function testPreviewDoesNotShowATooLongStory(): void {
|
||||
$GLOBALS['action'] = 'preview';
|
||||
$this->request(array(), array('title' => 'Long', 'author' => 'Me', 'story' => '<p><b>x</b></p>' . str_repeat('a', 65536)));
|
||||
$page = display_stories();
|
||||
$this->assertStringContainsString('<p>The story is too long to show.</p>', $page);
|
||||
$this->assertStringNotContainsString('<b>x</b>', $page);
|
||||
}
|
||||
|
||||
public function testPreviewShowsAPendingImage(): void {
|
||||
$GLOBALS['action'] = 'preview';
|
||||
$GLOBALS['file_src'] = '/display?pending="><script>';
|
||||
|
||||
@@ -35,6 +35,12 @@ class ValidateDataTest extends TestCase {
|
||||
$this->assertSame(array(), $this->validate(array('remove_img' => '1')));
|
||||
}
|
||||
|
||||
public function testATooLongStoryIsRefused(): void {
|
||||
$this->assertSame(array(), $this->validate(array('story' => str_repeat('a', 65536))));
|
||||
$this->assertSame(array('story' => 'The story is too long. Please shorten it.'),
|
||||
$this->validate(array('story' => str_repeat('a', 65537))));
|
||||
}
|
||||
|
||||
public function testVisitorsNeedTheCaptcha(): void {
|
||||
FakeRecaptcha::install('{"tokenProperties":{"valid":false}}');
|
||||
$this->assertSame(array('captcha' => 'Please complete the captcha.'), $this->validate(array('g-recaptcha-response' => 'r'), FALSE));
|
||||
|
||||
Reference in New Issue
Block a user