23 lines
1.9 KiB
PHP
23 lines
1.9 KiB
PHP
<?php
|
|
// Guarded QA-only security check. Authenticated actor without recent MFA cannot confirm or replay.
|
|
$app = (static fn ($p) => require $p)(__DIR__.'/portal-service-followup-runtime.php');
|
|
$db = Illuminate\Support\Facades\DB::connection();
|
|
$row = $db->table('portal_security_approvals')->where('kind','bootstrap')->where('requested_by',15)->where('subject_user_id',16)->first();
|
|
throw_unless($row && $row->consumed_at, RuntimeException::class);
|
|
$installation = app(App\Services\PortalProfileInstallation::class);
|
|
$second = App\Models\User::findOrFail(16);
|
|
$session = app('session')->driver();$session->start();$session->forget(['portal.mfa_actor','portal.mfa_at']);
|
|
$pairBefore = $db->table('portal_superuser_pair')->first();
|
|
$auditBefore = $db->table('audit_events')->count();
|
|
$denied = false;
|
|
try {
|
|
$installation->confirmBootstrap($second,$row->id,['hash'=>$installation->bootstrapPreview($second,$row->id)['hash'],
|
|
'confirm'=>true,'idempotency_key'=>(string)Illuminate\Support\Str::uuid(),'reason'=>'SIMULAÇÃO QA: uma sessão sem MFA recente nunca confirma nem recupera a operação protegida.']);
|
|
} catch (Symfony\Component\HttpKernel\Exception\HttpException $error) {
|
|
$denied = $error->getStatusCode() === 403;
|
|
}
|
|
throw_unless($denied && $pairBefore == $db->table('portal_superuser_pair')->first() && $auditBefore === $db->table('audit_events')->count(), RuntimeException::class,'Missing MFA must deny before side effects');
|
|
$proof=['result'=>'passed','scope'=>'Service security check without recent MFA; browser normal-MFA ceremony is proved separately','status'=>403,'pair_unchanged'=>true,'audit_unchanged'=>true,'active_writes'=>0];
|
|
file_put_contents(dirname(__DIR__).'/storage/app/private/investmoz-v2/portal-audit/service-followup-bootstrap-mfa-negative-qa.json',json_encode($proof,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));
|
|
echo json_encode($proof,JSON_THROW_ON_ERROR).PHP_EOL;
|