Add Micropub q=source and reject malformed requests
Servers supporting updates must support the source content query. It was falling through to the token response, so clients got a 200 with no post in it. Return the raw note as microformats2 JSON, honouring properties[]. Malformed requests could cause a 500 or worse. A replace value sent as a string overwrote the note with its first character, because $value[0] indexes the string. Validate update operations before applying any of them, and return invalid_request for wrongly shaped properties, unknown or malformed q values, and missing update URLs. Share URL resolution between updates and q=source in NoteResolver, which also rejects URLs on other hosts and invalid note IDs. Those IDs used to decode to a real note because invalid characters were skipped. Allow photo objects without alt text, and parse the published date before saving so that an invalid date doesn't leave a stray note behind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
6e96ca8304
commit
3dce7d3a60
1 changed files with 450 additions and 29 deletions
43
app/Services/Micropub/NoteResolver.php
Normal file
43
app/Services/Micropub/NoteResolver.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Micropub;
|
||||
|
||||
use App\Exceptions\MicropubUnsupportedModelException;
|
||||
use App\Models\Note;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Finds the note a Micropub request refers to by its URL.
|
||||
*/
|
||||
class NoteResolver
|
||||
{
|
||||
/**
|
||||
* @throws MicropubUnsupportedModelException if the URL is not a note on this site
|
||||
* @throws ModelNotFoundException if no such note exists
|
||||
*/
|
||||
public function fromUrl(string $url): Note
|
||||
{
|
||||
$host = parse_url($url, PHP_URL_HOST);
|
||||
$ourHost = parse_url((string) config('app.url'), PHP_URL_HOST);
|
||||
|
||||
if (! is_string($host) || ! is_string($ourHost) || strcasecmp($host, $ourHost) !== 0) {
|
||||
throw new MicropubUnsupportedModelException('The URL is not a post on this site');
|
||||
}
|
||||
|
||||
$path = (string) parse_url($url, PHP_URL_PATH);
|
||||
|
||||
if (preg_match('#^/notes/([0-9A-Za-z_]+)/?$#', $path, $matches) !== 1) {
|
||||
throw new MicropubUnsupportedModelException('This implementation currently only supports notes');
|
||||
}
|
||||
|
||||
try {
|
||||
return Note::nb60($matches[1])->firstOrFail();
|
||||
} catch (NotFoundHttpException) {
|
||||
// Note::scopeNb60() aborts when the ID is too big for the database
|
||||
throw (new ModelNotFoundException)->setModel(Note::class);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue