From fe30a0aaa1d2a6c8c7555e216a6257af33f46d71 Mon Sep 17 00:00:00 2001 From: Timothy Allen Date: Sat, 26 Sep 2026 10:04:41 +0200 Subject: [PATCH] Do not let caches keep queued or hidden images. --- display.php | 7 ++++++- tests/php/Pages/FeedAndImageTest.php | 7 +++++++ tests/php/Support/SiteData.php | 5 +++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/display.php b/display.php index 2515583..37ad0c8 100644 --- a/display.php +++ b/display.php @@ -59,7 +59,8 @@ function display_img($id) { $hidden = $is_admin ? "" : " AND hidden IS NOT TRUE"; } - $query = "SELECT img, mime FROM $table WHERE 1 AND ID = ?".$hidden." LIMIT 0, 1"; + $columns = $queue ? "img, mime" : "img, mime, hidden"; + $query = "SELECT $columns FROM $table WHERE 1 AND ID = ?".$hidden." LIMIT 0, 1"; $result = $db->execute_query($query, array((int)$id)) or db_fail(); $row = $result->fetch_assoc(); @@ -78,6 +79,10 @@ function display_img($id) { header("Content-type: ".$info['mime']); header("Content-length: ".strlen($img_data)); header("X-Content-Type-Options: nosniff"); + // Only this session can see a queued or hidden image: no cache may keep it + if ($queue || !empty($row['hidden'])) { + header("Cache-Control: private, no-store"); + } print $img_data; } else { diff --git a/tests/php/Pages/FeedAndImageTest.php b/tests/php/Pages/FeedAndImageTest.php index 49d67e2..381fca6 100644 --- a/tests/php/Pages/FeedAndImageTest.php +++ b/tests/php/Pages/FeedAndImageTest.php @@ -84,6 +84,13 @@ class FeedAndImageTest extends PageTestCase { $this->assertSame(200, $this->get('/display?id=9&queue=1')['status']); } + public function testRestrictedImagesAreNotCached(): void { + $this->assertNull($this->header($this->get('/display?id=2'), 'Cache-Control')); + $this->loginAs(); + $this->assertSame('private, no-store', $this->header($this->get('/display?id=3'), 'Cache-Control')); + $this->assertSame('private, no-store', $this->header($this->get('/display?id=9&queue=1'), 'Cache-Control')); + } + public function testThePendingUploadIsShownToItsSession(): void { $name = bin2hex(random_bytes(16)); mkdir(self::$state . '/tmp/uploads', 0700); diff --git a/tests/php/Support/SiteData.php b/tests/php/Support/SiteData.php index fb288d1..6d1abe2 100644 --- a/tests/php/Support/SiteData.php +++ b/tests/php/Support/SiteData.php @@ -30,9 +30,10 @@ class SiteData { $dates = array_column(self::$tables['xxx'], 'date'); return array(array('MAX(`date`)' => $dates ? max($dates) : NULL)); }); - FakeDb::on("/^SELECT img, mime FROM $stories\\b/i", function ($sql, $params) { + FakeDb::on("/^SELECT img, mime(, hidden)? FROM $stories\\b/i", function ($sql, $params) { $rows = self::filterStories($sql, $params); - return array_map(function ($row) { return array('img' => $row['img'], 'mime' => $row['mime']); }, array_slice($rows, 0, 1)); + $columns = str_starts_with($sql, 'SELECT img, mime, hidden') ? array('img', 'mime', 'hidden') : array('img', 'mime'); + return array_map(function ($row) use ($columns) { return array_intersect_key($row, array_flip($columns)); }, array_slice($rows, 0, 1)); }); FakeDb::on("/^INSERT INTO $stories SET /", array(self::class, 'insertStory')); FakeDb::on("/^UPDATE $stories SET /", array(self::class, 'updateStory'));