From 0bca140cd9dd7c1d44ec172a6ad0eeb166efda2e Mon Sep 17 00:00:00 2001 From: Paulo Castellano Date: Wed, 1 Apr 2026 13:21:22 -0300 Subject: [PATCH] fix: API scheduled_at validation, redirect allowlist, UUID model_id, UpdatePost transaction, safe resolveModel --- app/Actions/Post/UpdatePost.php | 43 ++++++++++--------- app/Http/Controllers/App/MediaController.php | 6 ++- app/Http/Controllers/App/PostController.php | 6 ++- .../Requests/Api/Post/UpdatePostRequest.php | 9 +++- .../App/Media/DuplicateMediaRequest.php | 2 +- .../App/Media/StoreChunkedMediaRequest.php | 2 +- .../Requests/App/Media/StoreMediaRequest.php | 2 +- 7 files changed, 44 insertions(+), 26 deletions(-) diff --git a/app/Actions/Post/UpdatePost.php b/app/Actions/Post/UpdatePost.php index acff5899..2123e455 100644 --- a/app/Actions/Post/UpdatePost.php +++ b/app/Actions/Post/UpdatePost.php @@ -11,6 +11,7 @@ use App\Models\Workspace; use Carbon\Carbon; use Illuminate\Support\Arr; +use Illuminate\Support\Facades\DB; class UpdatePost { @@ -40,30 +41,32 @@ public static function execute(Workspace $workspace, Post $post, array $data): a $post->labels()->sync(data_get($data, 'label_ids', [])); } - $post->postPlatforms()->update(['enabled' => false]); + DB::transaction(function () use ($post, $data) { + $post->postPlatforms()->update(['enabled' => false]); - foreach (data_get($data, 'platforms', []) as $platformData) { - $updateData = [ - 'enabled' => true, - 'content' => data_get($platformData, 'content'), - ]; + foreach (data_get($data, 'platforms', []) as $platformData) { + $updateData = [ + 'enabled' => true, + 'content' => data_get($platformData, 'content'), + ]; - if (data_get($platformData, 'content_type') !== null) { - $updateData['content_type'] = data_get($platformData, 'content_type'); - } - - if (data_get($platformData, 'meta') !== null) { - $postPlatform = $post->postPlatforms()->where('id', data_get($platformData, 'id'))->first(); - - if ($postPlatform) { - $updateData['meta'] = array_merge($postPlatform->meta ?? [], data_get($platformData, 'meta')); + if (data_get($platformData, 'content_type') !== null) { + $updateData['content_type'] = data_get($platformData, 'content_type'); } - } - $post->postPlatforms() - ->where('id', data_get($platformData, 'id')) - ->update($updateData); - } + if (data_get($platformData, 'meta') !== null) { + $postPlatform = $post->postPlatforms()->where('id', data_get($platformData, 'id'))->first(); + + if ($postPlatform) { + $updateData['meta'] = array_merge($postPlatform->meta ?? [], data_get($platformData, 'meta')); + } + } + + $post->postPlatforms() + ->where('id', data_get($platformData, 'id')) + ->update($updateData); + } + }); if ($status === PostStatus::Publishing->value) { $post->update(['scheduled_at' => now()]); diff --git a/app/Http/Controllers/App/MediaController.php b/app/Http/Controllers/App/MediaController.php index 916a83d4..e5ddac6d 100644 --- a/app/Http/Controllers/App/MediaController.php +++ b/app/Http/Controllers/App/MediaController.php @@ -180,7 +180,11 @@ private function authorizeModelOwnership(Model $model, Request $request): void private function resolveModel(string $alias, string $id): Model { - $modelClass = Relation::getMorphedModel($alias) ?? $alias; + $modelClass = Relation::getMorphedModel($alias); + + if (! $modelClass) { + abort(404); + } return $modelClass::findOrFail($id); } diff --git a/app/Http/Controllers/App/PostController.php b/app/Http/Controllers/App/PostController.php index 08606d8d..f41c446d 100644 --- a/app/Http/Controllers/App/PostController.php +++ b/app/Http/Controllers/App/PostController.php @@ -249,8 +249,12 @@ public function destroy(Request $request, Post $post): RedirectResponse session()->flash('flash.banner', __('posts.flash.deleted')); session()->flash('flash.bannerStyle', 'success'); + $allowedRedirects = ['app.posts.index', 'app.calendar']; + if ($redirect = $request->input('redirect')) { - return redirect()->route($redirect); + if (in_array($redirect, $allowedRedirects)) { + return redirect()->route($redirect); + } } return back(); diff --git a/app/Http/Requests/Api/Post/UpdatePostRequest.php b/app/Http/Requests/Api/Post/UpdatePostRequest.php index 52434859..2b23320e 100644 --- a/app/Http/Requests/Api/Post/UpdatePostRequest.php +++ b/app/Http/Requests/Api/Post/UpdatePostRequest.php @@ -27,7 +27,14 @@ public function rules(): array 'platforms.*.content' => ['nullable', 'string', 'max:63206'], 'platforms.*.content_type' => ['required', 'string', Rule::in(array_column(ContentType::cases(), 'value'))], 'platforms.*.meta' => ['nullable', 'array'], - 'scheduled_at' => ['nullable', 'date'], + 'scheduled_at' => [ + 'nullable', + 'date', + Rule::when( + in_array($this->input('status'), ['scheduled', 'publishing']), + ['after:now'] + ), + ], 'label_ids' => ['sometimes', 'array'], 'label_ids.*' => ['uuid', Rule::exists('workspace_labels', 'id')->where('workspace_id', $this->workspace->id)], ]; diff --git a/app/Http/Requests/App/Media/DuplicateMediaRequest.php b/app/Http/Requests/App/Media/DuplicateMediaRequest.php index e7fed1ab..58c40c26 100644 --- a/app/Http/Requests/App/Media/DuplicateMediaRequest.php +++ b/app/Http/Requests/App/Media/DuplicateMediaRequest.php @@ -19,7 +19,7 @@ public function rules(): array return [ 'targets' => ['required', 'array', 'max:50'], 'targets.*.model' => ['required', 'string', Rule::in(['postPlatform', 'workspace', 'user'])], - 'targets.*.model_id' => ['required', 'string'], + 'targets.*.model_id' => ['required', 'uuid'], ]; } } diff --git a/app/Http/Requests/App/Media/StoreChunkedMediaRequest.php b/app/Http/Requests/App/Media/StoreChunkedMediaRequest.php index 104179fa..57d6deee 100644 --- a/app/Http/Requests/App/Media/StoreChunkedMediaRequest.php +++ b/app/Http/Requests/App/Media/StoreChunkedMediaRequest.php @@ -38,7 +38,7 @@ public function rules(): array return [ 'content_range' => ['required', 'string', 'regex:'.self::CONTENT_RANGE_PATTERN], 'model' => ['required', 'string', Rule::in($this->allowedModels)], - 'model_id' => ['required', 'string'], + 'model_id' => ['required', 'uuid'], 'collection' => ['sometimes', 'string', 'max:255'], 'file_name' => [ 'required', diff --git a/app/Http/Requests/App/Media/StoreMediaRequest.php b/app/Http/Requests/App/Media/StoreMediaRequest.php index 5560b153..67de64bf 100644 --- a/app/Http/Requests/App/Media/StoreMediaRequest.php +++ b/app/Http/Requests/App/Media/StoreMediaRequest.php @@ -38,7 +38,7 @@ public function rules(): array ], 'model_id' => [ 'required', - 'string', + 'uuid', ], 'collection' => [ 'sometimes',