Limit the images that previews keep.

A preview that fails the captcha or the form token keeps no image. A session keeps one image, the directory has a size limit, and abandoned images go at the next upload.
This commit is contained in:
tim
2026-09-26 15:27:51 +02:00
parent fe30a0aaa1
commit c4fcef21aa
8 changed files with 215 additions and 42 deletions
+78
View File
@@ -2,6 +2,7 @@
namespace Xxx\Tests\Pages;
use Xxx\Tests\Support\PageTestCase;
use Xxx\Tests\Support\TestCase;
// Sending a story from publish/ as a visitor
class SubmitTest extends PageTestCase {
@@ -125,4 +126,81 @@ class SubmitTest extends PageTestCase {
$this->assertStringNotContainsString('<script>alert(1)</script>', $page['body']);
$this->assertStringContainsString('value="&quot;&gt;&lt;script&gt;alert(1)&lt;/script&gt;"', $page['body']);
}
// Preview the story with an image; returns the answer
private function previewImage(array $fields = array()): array {
$token = $this->csrfToken($this->get('/publish')['body']);
return $this->postFiles('/publish', $this->story($fields + array('action' => 'preview', 'csrf_token' => $token, 'g-recaptcha-response' => 'pass')),
array('upload' => array('a.png', TestCase::png())));
}
public function testAPreviewKeepsTheImageForTheSession(): void {
$page = $this->previewImage();
$this->assertStringContainsString('/display?pending=', $page['body']);
$uploads = $this->uploads();
$this->assertCount(1, $uploads);
$this->assertSame(basename($uploads[0]), $this->session()['pending_upload']);
}
public function testANewPreviewReplacesTheImage(): void {
$this->previewImage();
$first = $this->uploads();
$this->postFiles('/publish', $this->story(array('action' => 'preview', 'csrf_token' => $this->session()['csrf_token'])),
array('upload' => array('b.png', TestCase::png())));
$second = $this->uploads();
$this->assertCount(1, $second);
$this->assertNotSame($first, $second);
}
public function testAFailedCaptchaKeepsNoImage(): void {
$page = $this->previewImage(array('g-recaptcha-response' => 'fail'));
$this->assertStringContainsString('Please complete the captcha.', $page['body']);
$this->assertSame(array(), $this->uploads());
$this->assertArrayNotHasKey('pending_upload', $this->session());
}
public function testAFailedTokenKeepsNoImage(): void {
$page = $this->previewImage(array('csrf_token' => 'wrong'));
$this->assertStringContainsString('Your session has expired.', $page['body']);
$this->assertSame(array(), $this->uploads());
}
public function testAFullUploadDirectoryKeepsNoImage(): void {
// tests/php/fixtures/test.env sets UPLOAD_DIR_LIMIT_MB=1
mkdir(self::$state . '/tmp/uploads', 0700);
$filler = self::$state . '/tmp/uploads/' . str_repeat('f', 32);
file_put_contents($filler, str_repeat('x', 1024 * 1024));
$page = $this->previewImage();
$this->assertStringContainsString('The site cannot keep more images just now.', $page['body']);
$this->assertSame(array($filler), $this->uploads());
}
public function testAbandonedUploadsGoAtTheNextUpload(): void {
mkdir(self::$state . '/tmp/uploads', 0700);
$old = self::$state . '/tmp/uploads/' . str_repeat('a', 32);
$recent = self::$state . '/tmp/uploads/' . str_repeat('b', 32);
file_put_contents($old, 'x');
file_put_contents($recent, 'x');
touch($old, time() - 86401);
$this->previewImage();
$this->assertFileDoesNotExist($old);
$this->assertFileExists($recent);
$this->assertCount(2, $this->uploads());
}
public function testThePreviewedImageIsSavedThenRemoved(): void {
$this->previewImage();
$this->post('/publish', $this->story(array('action' => 'submit', 'uploaded' => 'pending', 'csrf_token' => $this->session()['csrf_token'])));
$this->assertSame(TestCase::png(), $this->tables()['queue'][0]['img']);
$this->assertSame(array(), $this->uploads());
$this->assertArrayNotHasKey('pending_upload', $this->session());
}
public function testSubmittingWithAnImageKeepsNoFile(): void {
$token = $this->csrfToken($this->get('/publish')['body']);
$this->postFiles('/publish', $this->story(array('action' => 'submit', 'csrf_token' => $token, 'g-recaptcha-response' => 'pass')),
array('upload' => array('a.png', TestCase::png())));
$this->assertSame(TestCase::png(), $this->tables()['queue'][0]['img']);
$this->assertSame(array(), $this->uploads());
}
}