fix(instanda): set env on go-live, correct secrets docblock, add feed advisory
- F-03: switch_to_live() now sets espinstanda_env=live so environment() actually resolves to Live after go-live (was leaving real quotes on the Test endpoint). - F-04: correct the class-config docblock overstating the at-rest guarantee; it only holds when SPG_INSTANDA_KEY is a constant (else the key is self-provisioned into the same DB as the ciphertext). - F-02: add advisory A5 "exactly one active INSTANDA feed" to catch a stray test-form feed that also creates quotes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,8 +9,11 @@
|
|||||||
* an explicit, gated switch to Live - see the Go-Live checklist.
|
* an explicit, gated switch to Live - see the Go-Live checklist.
|
||||||
*
|
*
|
||||||
* Secrets entered in the admin UI are encrypted at rest with libsodium
|
* Secrets entered in the admin UI are encrypted at rest with libsodium
|
||||||
* (sodium_crypto_secretbox, XSalsa20-Poly1305). The key comes from the
|
* (sodium_crypto_secretbox, XSalsa20-Poly1305). The "a database dump alone never
|
||||||
* SPG_INSTANDA_KEY constant, so a database dump alone never exposes a usable secret.
|
* exposes a usable secret" guarantee holds ONLY when the SPG_INSTANDA_KEY constant is
|
||||||
|
* set: without it the key is self-provisioned into the DB (see db_key()), so the key
|
||||||
|
* and ciphertext live in the same database and a dump recovers the password. Set
|
||||||
|
* SPG_INSTANDA_KEY (and the credential constants) in production for the hardened posture.
|
||||||
* Live credentials are expected as constants and are never written to the database.
|
* Live credentials are expected as constants and are never written to the database.
|
||||||
*
|
*
|
||||||
* @package ESP\Instanda
|
* @package ESP\Instanda
|
||||||
|
|||||||
@@ -106,9 +106,29 @@ final class Go_Live {
|
|||||||
['key' => 'A2', 'label' => 'Alert email recipient(s)', 'detail' => $emails !== '' ? $emails : 'None set', 'pass' => $emails !== ''],
|
['key' => 'A2', 'label' => 'Alert email recipient(s)', 'detail' => $emails !== '' ? $emails : 'None set', 'pass' => $emails !== ''],
|
||||||
['key' => 'A3', 'label' => 'Front-end form submit + redirect verified in a browser', 'detail' => $this->ack_detail('espinstanda_frontend_ack'), 'pass' => (bool) get_option('espinstanda_frontend_ack')],
|
['key' => 'A3', 'label' => 'Front-end form submit + redirect verified in a browser', 'detail' => $this->ack_detail('espinstanda_frontend_ack'), 'pass' => (bool) get_option('espinstanda_frontend_ack')],
|
||||||
['key' => 'A4', 'label' => 'No unresolved failures', 'detail' => $unresolved === 0 ? 'Clean' : "{$unresolved} unresolved", 'pass' => $unresolved === 0],
|
['key' => 'A4', 'label' => 'No unresolved failures', 'detail' => $unresolved === 0 ? 'Clean' : "{$unresolved} unresolved", 'pass' => $unresolved === 0],
|
||||||
|
['key' => 'A5', 'label' => 'Exactly one active INSTANDA feed', 'detail' => $this->feed_summary(), 'pass' => $this->active_feed_count() === 1],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Count active INSTANDA feeds across all forms (should be exactly one at go-live). */
|
||||||
|
private function active_feed_count(): int {
|
||||||
|
if (!class_exists(__NAMESPACE__ . '\\Instanda_AddOn')) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
$feeds = Instanda_AddOn::get_instance()->get_feeds();
|
||||||
|
return count(array_filter((array) $feeds, static fn ($f) => !empty($f['is_active'])));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function feed_summary(): string {
|
||||||
|
$n = $this->active_feed_count();
|
||||||
|
if ($n === 1) {
|
||||||
|
return '1 active feed';
|
||||||
|
}
|
||||||
|
return $n === 0
|
||||||
|
? 'No active feed - the quote form will not push to INSTANDA'
|
||||||
|
: "{$n} active feeds - deactivate all but the canonical quote form (a stray test-form feed also creates quotes)";
|
||||||
|
}
|
||||||
|
|
||||||
/** @return array{complete:int,total:int,ready:bool} */
|
/** @return array{complete:int,total:int,ready:bool} */
|
||||||
public function readiness(): array {
|
public function readiness(): array {
|
||||||
$blocking = $this->blocking_items();
|
$blocking = $this->blocking_items();
|
||||||
@@ -129,6 +149,10 @@ final class Go_Live {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
update_option('espinstanda_operating_mode', 'live');
|
update_option('espinstanda_operating_mode', 'live');
|
||||||
|
// Switching the mode must also select the Live environment, mirroring
|
||||||
|
// return_to_test(); otherwise environment() stays clamped to Test and real
|
||||||
|
// customer quotes would keep hitting the Test endpoint after go-live.
|
||||||
|
update_option('espinstanda_env', 'live');
|
||||||
update_option('espinstanda_operating_mode_audit', [
|
update_option('espinstanda_operating_mode_audit', [
|
||||||
'actor' => get_current_user_id(),
|
'actor' => get_current_user_id(),
|
||||||
'ts' => current_time('mysql'),
|
'ts' => current_time('mysql'),
|
||||||
|
|||||||
Reference in New Issue
Block a user