refactor: simplify remote process chain and harden ActivityMonitor

- Inline PrepareCoolifyTask and CoolifyTaskArgs into remote_process(),
  removing two single-consumer abstraction layers
- Add #[Locked] attribute to ActivityMonitor $activityId property
- Add team ownership verification in ActivityMonitor.hydrateActivity()
  with server_uuid fallback and fail-closed default
- Store team_id in activity properties for proper scoping
- Update CLAUDE.md to remove stale reference
- Add comprehensive tests for activity monitor authorization

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Andras BacsaiandClaude Opus 4.6 committed 2026-03-26 13:26:16 +01:00
1 parent c8efbf107a
commit 3e0d48faea
7 files changed
+155 -131

No files matched your search

+38 -13
View File
@@ -2,7 +2,9 @@
namespace App\Livewire;
use App\Models\Server;
use App\Models\User;
use Livewire\Attributes\Locked;
use Livewire\Component;
use Spatie\Activitylog\Models\Activity;
@@ -10,6 +12,7 @@ class ActivityMonitor extends Component
{
public ?string $header = null;
#[Locked]
public $activityId = null;
public $eventToDispatch = 'activityFinished';
@@ -57,25 +60,47 @@ class ActivityMonitor extends Component
$activity = Activity::find($this->activityId);
if ($activity) {
$teamId = data_get($activity, 'properties.team_id');
if ($teamId && $teamId !== currentTeam()?->id) {
if (! $activity) {
$this->activity = null;
return;
}
$currentTeamId = currentTeam()?->id;
// Check team_id stored directly in activity properties
$activityTeamId = data_get($activity, 'properties.team_id');
if ($activityTeamId !== null) {
if ((int) $activityTeamId !== (int) $currentTeamId) {
$this->activity = null;
return;
}
$this->activity = $activity;
return;
}
// Fallback: verify ownership via the server that ran the command
$serverUuid = data_get($activity, 'properties.server_uuid');
if ($serverUuid) {
$server = Server::where('uuid', $serverUuid)->first();
if ($server && (int) $server->team_id !== (int) $currentTeamId) {
$this->activity = null;
return;
}
if ($server) {
$this->activity = $activity;
return;
}
}
$this->activity = $activity;
}
public function updatedActivityId($value)
{
if ($value) {
$this->hydrateActivity();
$this->isPollingActive = true;
self::$eventDispatched = false;
}
// Fail closed: no team_id and no server_uuid means we cannot verify ownership
$this->activity = null;
}
public function polling()