From 34ee7eff60a785c715c7ddd7f8efa8d05cefa669 Mon Sep 17 00:00:00 2001 From: Ty Balascio Date: Sun, 20 Sep 2026 07:38:38 -0700 Subject: [PATCH] Settings: temporary UAT secret-unmask gate (SLAB_UAT_UNMASK_SECRETS) Owner-directed, LAN/HTTP dev-test only. When the env flag is set, GET /api/v1/settings returns decrypted secret values (string) instead of {configured: bool}; the UI prefills and shows them in clear text. Default off -> SETTINGS-75 masking and the existing tests stay intact. Revert before any non-UAT deployment. --- backend/settings.py | 28 +++++++--- frontend/src/create/settings/Settings.svelte | 58 ++++++++++---------- 2 files changed, 49 insertions(+), 37 deletions(-) diff --git a/backend/settings.py b/backend/settings.py index a5b190b..b3999fe 100644 --- a/backend/settings.py +++ b/backend/settings.py @@ -59,18 +59,32 @@ def get_settings() -> dict[str, Any]: result: dict[str, Any] = {} result["woo_base_url"] = os.environ.get("WOO_BASE_URL", "") + # TEMPORARY UAT override (owner-directed, LAN/HTTP only): when set, secrets are + # returned as their decrypted plaintext so they can be inspected on the dev-test + # box. Default OFF -> SETTINGS-75 masking intact. Remove before prod. + unmask = os.environ.get("SLAB_UAT_UNMASK_SECRETS") == "1" + for k in sorted(ALL_KEYS): row = rows.get(k, {}) if k in SECRET_KEYS: enc = row.get("value_enc") - if enc: - try: - decrypt(enc) # verify roundtrip possible - result[k] = {"configured": True} - except Exception: - result[k] = {"configured": False} + if unmask: + if enc: + try: + result[k] = decrypt(enc) + except Exception: + result[k] = "" + else: + result[k] = "" else: - result[k] = {"configured": False} + if enc: + try: + decrypt(enc) # verify roundtrip possible + result[k] = {"configured": True} + except Exception: + result[k] = {"configured": False} + else: + result[k] = {"configured": False} else: if k in rows: val = _row_to_value(row) diff --git a/frontend/src/create/settings/Settings.svelte b/frontend/src/create/settings/Settings.svelte index 97be974..5226541 100644 --- a/frontend/src/create/settings/Settings.svelte +++ b/frontend/src/create/settings/Settings.svelte @@ -2,12 +2,14 @@ import { onMount } from 'svelte'; interface SecretState { configured: boolean } + // Masked mode: object {configured}. Unmasked UAT mode (SLAB_UAT_UNMASK_SECRETS): plain string. + type Secret = SecretState | string; interface Settings { woo_base_url: string; woo_wp_username: string | null; - woo_app_password: SecretState; - inference_base_url: SecretState; - inference_api_key: SecretState; + woo_app_password: Secret; + inference_base_url: Secret; + inference_api_key: Secret; inference_model: string | null; inference_enabled: boolean; content_llm_enabled: boolean; @@ -24,6 +26,11 @@ // local edit buffers (write-only for secrets) let form: any = {}; + // Unmasked UAT mode returns the secret as a plain string; masked mode returns {configured}. + const isUnmasked = (s: Secret) => typeof s === 'string'; + const secretValue = (s: Secret): string => (isUnmasked(s) ? (s as string) : ''); + const secretConfigured = (s: Secret): boolean => (isUnmasked(s) ? (s as string).length > 0 : s.configured); + async function load() { loading = true; try { @@ -39,10 +46,10 @@ brand_voice: settings.brand_voice || '', geo_context: settings.geo_context || '', woo_create_status: settings.woo_create_status || 'draft', - // secrets: only send if user types - woo_app_password: '', - inference_base_url: '', - inference_api_key: '', + // secrets: prefill when unmasked (UAT), else blank (masked, write-only) + woo_app_password: secretValue(settings.woo_app_password), + inference_base_url: secretValue(settings.inference_base_url), + inference_api_key: secretValue(settings.inference_api_key), }; } } catch (e) { @@ -77,10 +84,10 @@ if (res.ok) { settings = await res.json(); message = 'Saved'; - // clear secret inputs after save - form.woo_app_password = ''; - form.inference_base_url = ''; - form.inference_api_key = ''; + // refresh secret buffers from the (possibly unmasked) server state + form.woo_app_password = secretValue(settings.woo_app_password); + form.inference_base_url = secretValue(settings.inference_base_url); + form.inference_api_key = secretValue(settings.inference_api_key); } else { const err = await res.json(); message = err.error?.message || 'Save failed'; @@ -124,18 +131,18 @@
- - + +
- - + +
- - + +
@@ -182,17 +189,8 @@