From 778bb7f1172d667f02bd9af5265696d911b7519d Mon Sep 17 00:00:00 2001 From: nexxo Date: Sat, 25 Jul 2026 12:46:34 +0200 Subject: [PATCH] fix(engine-b): always scrub credentials once delivered; idempotent monitoring lookup - CompleteProvisioning scrubs admin_password whenever credentials_sent exists, so a crash between recording and scrubbing can't leave the password in context. - HttpMonitoringClient looks up an existing monitor by URL before creating one, so a retry after a crashed POST doesn't create a duplicate external monitor. Co-Authored-By: Claude Opus 4.8 --- .../Steps/Customer/CompleteProvisioning.php | 8 ++++++-- app/Services/Monitoring/HttpMonitoringClient.php | 15 +++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/app/Provisioning/Steps/Customer/CompleteProvisioning.php b/app/Provisioning/Steps/Customer/CompleteProvisioning.php index d3a2054..7b51ffb 100644 --- a/app/Provisioning/Steps/Customer/CompleteProvisioning.php +++ b/app/Provisioning/Steps/Customer/CompleteProvisioning.php @@ -32,13 +32,17 @@ class CompleteProvisioning extends CustomerStep $encrypted = $run->context('admin_password'); if ($encrypted !== null && ! $this->hasResource($run, 'credentials_sent')) { - // Pass the ENCRYPTED password — CloudReady is queued, so plaintext - // would land in the queue payload; it decrypts only when building the mail. + // Pass the ENCRYPTED password (decrypted only when building the mail). Notification::route('mail', $order->customer->email)->notify( new CloudReady($instance, (string) $instance->nc_admin_ref, $encrypted), ); // Record delivery so a retry after a crash doesn't re-send. $this->recordResource($run, $instance->host, 'credentials_sent', (string) $instance->nc_admin_ref); + } + + // Always scrub once delivery is recorded — even if a crash interrupted a + // previous attempt between recording and scrubbing. + if ($this->hasResource($run, 'credentials_sent')) { $run->forgetContext('admin_password'); } diff --git a/app/Services/Monitoring/HttpMonitoringClient.php b/app/Services/Monitoring/HttpMonitoringClient.php index 01f3721..503cef1 100644 --- a/app/Services/Monitoring/HttpMonitoringClient.php +++ b/app/Services/Monitoring/HttpMonitoringClient.php @@ -18,8 +18,19 @@ class HttpMonitoringClient implements MonitoringClient return 'mon-'.substr(sha1($url), 0, 12); } - return (string) Http::withToken((string) config('services.monitoring.token')) - ->post(rtrim($endpoint, '/').'/monitors', ['friendly_name' => $name, 'url' => $url, 'type' => 'https']) + $base = rtrim($endpoint, '/'); + $token = (string) config('services.monitoring.token'); + + // Idempotent: reuse an existing monitor for this URL rather than creating + // a duplicate when a crash retries after a prior successful POST. + $existing = collect(Http::withToken($token)->get($base.'/monitors')->throw()->json('monitors', [])) + ->first(fn ($monitor) => ($monitor['url'] ?? null) === $url); + if ($existing !== null) { + return (string) $existing['id']; + } + + return (string) Http::withToken($token) + ->post($base.'/monitors', ['friendly_name' => $name, 'url' => $url, 'type' => 'https']) ->throw()->json('monitor.id'); }