Return 404 for non-numeric like and bookmark IDs

Constrain the likes/{like} and bookmarks/{bookmark} routes to numeric
IDs. Previously a junk URL such as /likes/twcamp^share|twsrc^ios was
passed to route model binding, and Postgres rejected the text-vs-integer
comparison with a QueryException, returning a 500 instead of a 404.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jonny Barnes 2026-10-29 19:01:28 +00:00
commit 7ce256baa5
Signed by: jonny
SSH key fingerprint: SHA256:CTuSlns5U7qlD9jqHvtnVmfYV3Zwl2Z7WnJ4/dqOaL8
3 changed files with 16 additions and 2 deletions

View file

@ -211,14 +211,14 @@ Route::get('note/{id}', [NotesController::class, 'redirect']); // for legacy not
// Likes
Route::prefix('likes')->group(function () {
Route::get('/', [LikesController::class, 'index']);
Route::get('/{like}', [LikesController::class, 'show']);
Route::get('/{like}', [LikesController::class, 'show'])->whereNumber('like');
});
// Bookmarks
Route::prefix('bookmarks')->group(function () {
Route::get('/', [BookmarksController::class, 'index']);
Route::redirect('/tagged', '/bookmarks');
Route::get('/{bookmark}', [BookmarksController::class, 'show']);
Route::get('/{bookmark}', [BookmarksController::class, 'show'])->whereNumber('bookmark');
Route::get('/tagged/{tag}', [BookmarksController::class, 'tagged']);
});

View file

@ -32,6 +32,13 @@ class BookmarksTest extends TestCase
$response->assertViewIs('bookmarks.show');
}
#[Test]
public function non_numeric_bookmark_id_gives_not_found_response(): void
{
$response = $this->get('/bookmarks/twcamp^share|twsrc^ios');
$response->assertNotFound();
}
#[Test]
public function when_bookmark_is_added_using_http_syntax_check_job_to_take_screenshot_is_invoked(): void
{

View file

@ -178,4 +178,11 @@ class LikesTest extends TestCase
$response = $this->get('/likes/202');
$response->assertNotFound();
}
#[Test]
public function non_numeric_like_id_gives_not_found_response(): void
{
$response = $this->get('/likes/twcamp^share|twsrc^ios');
$response->assertNotFound();
}
}