make(Kernel::class)->bootstrap(); $identity = portalQaAssertTarget($target); $actor = User::findOrFail(21); throw_unless(str_ends_with($actor->email, '@example.invalid'), RuntimeException::class, 'Only synthetic QA actor'); $count = DB::table('sessions')->where('user_id', 21)->where('last_activity', '>=', strtotime('2026-10-10 20:15:00 UTC'))->delete(); $qaSessionRoot = realpath(storage_path('app/private/investmoz-v2/qa-opportunity-resources-sessions')); throw_unless($qaSessionRoot && str_starts_with($qaSessionRoot, realpath(storage_path('app/private/investmoz-v2'))), RuntimeException::class, 'QA session directory outside allowed target'); $cutoff = 0; foreach (glob(storage_path('app/private/investmoz-v2/portal-audit/consumer-workflow-chromium-20261010-attempt-*.json')) as $receipt) { $r = json_decode(file_get_contents($receipt), true); if (($r['result'] ?? null) === 'failed') { $cutoff = max($cutoff, strtotime($r['finished_at'] ?? '1970-01-01')); } } throw_unless($cutoff > 0, RuntimeException::class, 'Failed browser batch cutoff required'); $filesRevoked = 0; foreach (glob($qaSessionRoot.DIRECTORY_SEPARATOR.'*') as $file) { if (! is_file($file) || filemtime($file) < strtotime('2026-10-10 20:15:00 UTC') || filemtime($file) > $cutoff) { continue; } $raw = file_get_contents($file); $data = json_decode($raw, true) ?? @unserialize($raw, ['allowed_classes' => false]); if (! is_array($data)) { try { $raw = Crypt::decrypt($raw); $data = json_decode($raw, true) ?? @unserialize($raw, ['allowed_classes' => false]); } catch (Throwable) { continue; } } if (! is_array($data)) { continue; } foreach ($data as $key => $value) { if (str_starts_with((string) $key, 'login_web_') && (int) $value === 21) { throw_unless(unlink($file), RuntimeException::class, 'Could not revoke synthetic QA session'); $filesRevoked++; break; } } } file_put_contents(storage_path('app/private/investmoz-v2/portal-audit/consumer-browser-session-revocation-qa.json'), json_encode(['at' => gmdate('c'), 'target' => $identity, 'actor_id' => 21, 'revoked_database_sessions' => $count, 'revoked_file_sessions' => $filesRevoked, 'failed_batch_cutoff_utc' => gmdate('c', $cutoff), 'scope' => 'synthetic QA21 failed batch since20:15UTC; later sessions excluded', 'mfa_changed' => false, 'active_writes' => 0], JSON_PRETTY_PRINT)); echo json_encode(['actor_id' => 21, 'revoked_database_sessions' => $count, 'revoked_file_sessions' => $filesRevoked, 'target' => $identity, 'active_writes' => 0]).PHP_EOL;