From 85aca0d022dabdf429f5f9628d3eb61fcf940000 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 17:45:41 +0000 Subject: [PATCH 1/2] Initial plan From ac9ba4776c27f8f8c0f0f5035c0dfea6983dd58d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 17:48:38 +0000 Subject: [PATCH 2/2] Detect and warn when machine was set up without factory seal Agent-Logs-Url: https://github.com/naturallaw777/staging_alpha/sessions/169de2bb-0655-4504-a270-8c0341c0d3dd Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com> --- app/sovran_systemsos_web/server.py | 33 ++++++++++++++----- .../static/js/features.js | 10 +++++- .../static/js/security.js | 5 +-- .../static/js/service-detail.js | 7 ++-- app/sovran_systemsos_web/static/js/state.js | 1 + modules/core/factory-seal.nix | 18 ++++++++-- 6 files changed, 58 insertions(+), 16 deletions(-) diff --git a/app/sovran_systemsos_web/server.py b/app/sovran_systemsos_web/server.py index 165aedc..bf3e6cf 100644 --- a/app/sovran_systemsos_web/server.py +++ b/app/sovran_systemsos_web/server.py @@ -2949,6 +2949,16 @@ async def api_security_status(): "The default system password may be known to the factory. " "Please change your system and application passwords immediately." ) + elif status == "unsealed": + try: + with open(SECURITY_WARNING_FILE, "r") as f: + warning = f.read().strip() + except FileNotFoundError: + warning = ( + "This machine was set up without the factory seal process. " + "Factory test data — including SSH keys, database contents, and wallet information — " + "may still be present on this system." + ) return {"status": status, "warning": warning} @@ -3027,14 +3037,21 @@ async def api_change_password(req: ChangePasswordRequest): except Exception as exc: raise HTTPException(status_code=500, detail=f"Failed to write secrets file: {exc}") - # Clear legacy security status so the warning banner is removed - for path in (SECURITY_STATUS_FILE, SECURITY_WARNING_FILE): - try: - os.remove(path) - except FileNotFoundError: - pass - except Exception: - pass # Non-fatal; don't block a successful password change + # Clear legacy security status so the warning banner is removed — but only + # for "legacy" machines (pre-seal era). For "unsealed" machines, changing + # passwords is not enough; the factory residue (SSH keys, wallet data, + # databases) remains until a proper re-seal or re-install is performed. + try: + with open(SECURITY_STATUS_FILE, "r") as f: + current_status = f.read().strip() + if current_status == "legacy": + os.remove(SECURITY_STATUS_FILE) + try: + os.remove(SECURITY_WARNING_FILE) + except FileNotFoundError: + pass + except (FileNotFoundError, OSError): + pass return {"ok": True} diff --git a/app/sovran_systemsos_web/static/js/features.js b/app/sovran_systemsos_web/static/js/features.js index 831ceff..a8c196b 100644 --- a/app/sovran_systemsos_web/static/js/features.js +++ b/app/sovran_systemsos_web/static/js/features.js @@ -602,11 +602,19 @@ function renderAutolaunchToggle(enabled) { var securityBanner = ""; if (_securityIsLegacy) { var msg = _securityWarningMessage || "Your system may have factory default passwords. Please change your passwords to secure your system."; + var linkText, linkAction; + if (_securityStatus === "unsealed") { + linkText = "Contact Support"; + linkAction = "openSupportModal(); return false;"; + } else { + linkText = "Change Passwords"; + linkAction = "openServiceDetailModal('root-password-setup.service', 'System Passwords', 'passwords'); return false;"; + } securityBanner = '
'; } diff --git a/app/sovran_systemsos_web/static/js/security.js b/app/sovran_systemsos_web/static/js/security.js index 0d21037..d716960 100644 --- a/app/sovran_systemsos_web/static/js/security.js +++ b/app/sovran_systemsos_web/static/js/security.js @@ -5,9 +5,10 @@ async function checkLegacySecurity() { try { var data = await apiFetch("/api/security/status"); - if (data && data.status === "legacy") { + if (data && (data.status === "legacy" || data.status === "unsealed")) { _securityIsLegacy = true; - _securityWarningMessage = data.warning || "This machine may have a known factory password. Please change your passwords immediately."; + _securityStatus = data.status; + _securityWarningMessage = data.warning || "This machine may have a security issue. Please review your system security."; } } catch (_) { // Non-fatal — silently ignore if the endpoint is unreachable diff --git a/app/sovran_systemsos_web/static/js/service-detail.js b/app/sovran_systemsos_web/static/js/service-detail.js index d53572f..0708870 100644 --- a/app/sovran_systemsos_web/static/js/service-detail.js +++ b/app/sovran_systemsos_web/static/js/service-detail.js @@ -626,8 +626,11 @@ function openSystemChangePasswordModal(unit, name, icon) { resultEl.textContent = "✅ System password changed successfully."; submitBtn.textContent = "Change Password"; submitBtn.disabled = false; - // Hide the legacy security banner if it's visible - if (typeof _securityIsLegacy !== "undefined" && _securityIsLegacy) { + // Hide the legacy security banner if it's visible — but only for + // "legacy" status machines. For "unsealed" machines, changing passwords + // is not enough; the factory residue remains until a proper re-seal or re-install. + if (typeof _securityIsLegacy !== "undefined" && _securityIsLegacy && + (typeof _securityStatus === "undefined" || _securityStatus !== "unsealed")) { _securityIsLegacy = false; var banner = document.querySelector(".security-inline-banner"); if (banner) banner.remove(); diff --git a/app/sovran_systemsos_web/static/js/state.js b/app/sovran_systemsos_web/static/js/state.js index 34ae694..c85947b 100644 --- a/app/sovran_systemsos_web/static/js/state.js +++ b/app/sovran_systemsos_web/static/js/state.js @@ -101,6 +101,7 @@ const $upgradeCloseBtn = document.getElementById("upgrade-close-btn"); // Legacy security warning state (populated by checkLegacySecurity in security.js) var _securityIsLegacy = false; +var _securityStatus = "ok"; // "ok", "legacy", or "unsealed" var _securityWarningMessage = ""; // System status banner diff --git a/modules/core/factory-seal.nix b/modules/core/factory-seal.nix index 781d010..74db537 100644 --- a/modules/core/factory-seal.nix +++ b/modules/core/factory-seal.nix @@ -100,11 +100,23 @@ in }; path = [ pkgs.coreutils ]; script = '' - # If already onboarded or sealed, nothing to do - [ -f /var/lib/sovran-customer-onboarded ] && exit 0 + # If sealed AND onboarded — fully clean, nothing to do + [ -f /var/lib/sovran-factory-sealed ] && [ -f /var/lib/sovran-customer-onboarded ] && exit 0 + + # If sealed but not yet onboarded — seal was run, customer hasn't finished setup yet, that's fine [ -f /var/lib/sovran-factory-sealed ] && exit 0 - # If secrets exist but no sealed/onboarded flag, this is a legacy machine + # If onboarded but NOT sealed — installer ran without factory seal! + if [ -f /var/lib/sovran-customer-onboarded ] && [ ! -f /var/lib/sovran-factory-sealed ]; then + mkdir -p /var/lib/sovran + echo "unsealed" > /var/lib/sovran/security-status + cat > /var/lib/sovran/security-warning << 'EOF' +This machine was set up without the factory seal process. Factory test data — including SSH keys, database contents, and wallet information — may still be present on this system. It is strongly recommended to back up any important data and re-install using a fresh ISO, or contact Sovran Systems support for assistance. +EOF + exit 0 + fi + + # No flags at all + secrets exist = legacy (pre-seal era) machine if [ -f /var/lib/secrets/root-password ]; then mkdir -p /var/lib/sovran echo "legacy" > /var/lib/sovran/security-status