/* eslint-disable */
/* © 2026 K2B NETWORK SOLUTIONS LLC. All rights reserved.
Proprietary and confidential. Unauthorized copying, distribution, or use of
this file, via any medium, is strictly prohibited. Original work product. */
// JBREWS DMS — Security features (loads right after DMSCore.jsx.txt).
// • K2BSecurityHost — idle auto-lock + manual lock overlay (mounted by app.jsx)
// • jbLockNow() — lock this station immediately (also Ctrl+Shift+L)
// • jbMaskPII / JbSecret — PII masking with an audited 20-second reveal
// • jbPasswordPolicy — shared password rules (Settings → Users (Server))
// • SecurityPanel — Settings → Security tab (admin only)
//
// Audit actions logged here: "Lock", "Unlock", "Failed Unlock", "Sign Off"
// (from the lock screen), "PII Revealed".
// localStorage: jb_sec_idle_min (idle minutes, 0 = off). sessionStorage:
// jb_sec_locked ("1" while this tab is locked — survives a reload).
const JB_SEC_IDLE_KEY = "jb_sec_idle_min";
const JB_SEC_LOCKED_KEY = "jb_sec_locked";
const JB_SEC_IDLE_DEFAULT = 15;
const JB_SEC_MAX_FAILS = 5;
const JB_SEC_COOLDOWN_MS = 60 * 1000;
const JB_SEC_REVEAL_MS = 20 * 1000;
const jbSecIdleMinutes = () => {
try {
const raw = localStorage.getItem(JB_SEC_IDLE_KEY);
if (raw === null || raw === "") return JB_SEC_IDLE_DEFAULT;
const n = Number(raw);
return Number.isFinite(n) && n >= 0 ? n : JB_SEC_IDLE_DEFAULT;
} catch (e) { return JB_SEC_IDLE_DEFAULT; }
};
const jbSecSetIdleMinutes = (min) => {
try { localStorage.setItem(JB_SEC_IDLE_KEY, String(Math.max(0, Number(min) || 0))); } catch (e) {}
try { window.dispatchEvent(new CustomEvent("jb-sec-idle")); } catch (e) {}
};
const jbSecDMS = () => (typeof window !== "undefined" && window.DMS) ? window.DMS : null;
const jbSecUser = () => { const D = jbSecDMS(); try { return (D && D.userId) ? D.userId() : ""; } catch (e) { return ""; } };
const jbSecAudit = (action, detail) => { const D = jbSecDMS(); try { if (D && D.logAudit) D.logAudit(action, detail || ""); } catch (e) {} };
/* ── Lock this station now (also bound to Ctrl+Shift+L by the host) ── */
const jbLockNow = () => {
if (!jbSecUser()) { window.k2bToast && window.k2bToast("Nobody is signed in on this station.", "info"); return false; }
if (!window.__jbSecHostMounted) { window.k2bToast && window.k2bToast("Lock screen is not mounted on this page.", "warn"); return false; }
try { window.dispatchEvent(new CustomEvent("jb-sec-lock", { detail: { reason: "manual" } })); } catch (e) {}
return true;
};
/* ── Sign off exactly the way the app shell does (app.jsx Sign Off button):
presence cleared → DMS.signOut() → per-session working keys cleared →
reload (the token is gone, so the reload lands on SignOn). ── */
const jbSecSignOff = async () => {
const D = jbSecDMS();
jbSecAudit("Sign Off", jbSecUser() + " · from lock screen");
try { sessionStorage.removeItem(JB_SEC_LOCKED_KEY); } catch (e) {}
// A pop-out window only closes itself — the main window keeps its sign-in
// and its deal pointers (same rule as the pop-out bar's Sign Off).
const isPopout = /[?&]popout=/.test(String(window.location.search || ""));
if (isPopout) { try { window.close(); } catch (e) {} return; }
if (D) {
try { if (D.clearPresence) D.clearPresence(); } catch (e) {}
if (typeof D.signOut === "function") { try { await D.signOut(); } catch (e) {} }
}
try { if (typeof jbClearSessionWork === "function") jbClearSessionWork(); } catch (e) {}
try { window.location.reload(); } catch (e) {}
};
/* ═══════════════════════ K2BSecurityHost ═══════════════════════ */
const K2BSecurityHost = ({ go }) => {
const [locked, setLocked] = React.useState(() => { try { return sessionStorage.getItem(JB_SEC_LOCKED_KEY) === "1" && !!jbSecUser(); } catch (e) { return false; } });
const [idleMin, setIdleMin] = React.useState(jbSecIdleMinutes);
const lastActive = React.useRef(Date.now());
const hiddenAt = React.useRef(0);
const lockedRef = React.useRef(locked);
const overlayRef = React.useRef(null);
lockedRef.current = locked;
const lock = React.useCallback((reason) => {
if (lockedRef.current) return;
if (!jbSecUser()) return;
lockedRef.current = true;
setLocked(true);
try { sessionStorage.setItem(JB_SEC_LOCKED_KEY, "1"); } catch (e) {}
try { if (document.activeElement && document.activeElement.blur) document.activeElement.blur(); } catch (e) {}
jbSecAudit("Lock", jbSecUser() + " · " + (reason || "manual"));
}, []);
const unlock = React.useCallback(() => {
lockedRef.current = false;
lastActive.current = Date.now();
setLocked(false);
try { sessionStorage.removeItem(JB_SEC_LOCKED_KEY); } catch (e) {}
}, []);
// Activity tracking (throttled to one write / 2s), idle timer, visibility,
// manual lock event, Ctrl+Shift+L, and the keyboard fence while locked.
React.useEffect(() => {
window.__jbSecHostMounted = true;
let lastMark = 0;
const mark = () => { const now = Date.now(); if (now - lastMark > 2000) { lastMark = now; lastActive.current = now; } };
const EV = ["mousemove", "keydown", "mousedown", "touchstart", "scroll"];
EV.forEach(ev => window.addEventListener(ev, mark, { passive: true, capture: true }));
const onIdlePref = () => setIdleMin(jbSecIdleMinutes());
window.addEventListener("jb-sec-idle", onIdlePref);
const onLockEvt = (e) => lock((e && e.detail && e.detail.reason) || "manual");
window.addEventListener("jb-sec-lock", onLockEvt);
const onVis = () => {
if (document.visibilityState === "hidden") { hiddenAt.current = Date.now(); return; }
const m = jbSecIdleMinutes();
if (m > 0 && hiddenAt.current && Date.now() - hiddenAt.current > m * 60000) lock("away " + Math.round((Date.now() - hiddenAt.current) / 60000) + " min");
hiddenAt.current = 0;
};
document.addEventListener("visibilitychange", onVis);
// Capture-phase key fence: Ctrl+Shift+L locks; while locked, F-keys and
// any key outside the overlay never reach the app's shortcut handlers.
const onKeyCapture = (e) => {
if ((e.ctrlKey || e.metaKey) && e.shiftKey && String(e.key || "").toLowerCase() === "l") {
if (jbSecUser() && !lockedRef.current) { e.preventDefault(); e.stopPropagation(); lock("Ctrl+Shift+L"); }
return;
}
if (!lockedRef.current) return;
if (/^F\d{1,2}$/.test(String(e.key || ""))) { e.preventDefault(); e.stopPropagation(); return; }
const ov = overlayRef.current;
if (!ov || !(e.target instanceof Node) || !ov.contains(e.target)) { e.stopPropagation(); }
};
window.addEventListener("keydown", onKeyCapture, true);
const tick = setInterval(() => {
if (lockedRef.current) return;
const m = jbSecIdleMinutes();
if (m <= 0 || !jbSecUser()) return;
if (Date.now() - lastActive.current > m * 60000) lock("idle " + m + " min");
}, 5000);
return () => {
window.__jbSecHostMounted = false;
EV.forEach(ev => window.removeEventListener(ev, mark, { capture: true }));
window.removeEventListener("jb-sec-idle", onIdlePref);
window.removeEventListener("jb-sec-lock", onLockEvt);
document.removeEventListener("visibilitychange", onVis);
window.removeEventListener("keydown", onKeyCapture, true);
clearInterval(tick);
};
}, [lock]);
if (!locked) return null;
return ;
};
/* ── The lock overlay itself ── */
const JbLockScreen = ({ overlayRef, onUnlocked, idleMin }) => {
const D = jbSecDMS();
const user = jbSecUser();
const ti = (D && D.tokenInfo) ? (() => { try { return D.tokenInfo(); } catch (e) { return null; } })() : null;
const name = (ti && ti.name) ? String(ti.name) : "";
const offline = !!(D && D.offlineByChoice && D.offlineByChoice());
const noServer = !D || typeof D.login !== "function";
const canVerify = !offline && !noServer;
const [pw, setPw] = React.useState("");
const [err, setErr] = React.useState("");
const [busy, setBusy] = React.useState(false);
const [fails, setFails] = React.useState(0);
const [shake, setShake] = React.useState(false);
const [coolUntil, setCoolUntil] = React.useState(0);
const [now, setNow] = React.useState(Date.now());
const [caps, setCaps] = React.useState(false);
const inputRef = React.useRef(null);
React.useEffect(() => { if (!coolUntil) return; const t = setInterval(() => setNow(Date.now()), 500); return () => clearInterval(t); }, [coolUntil]);
const coolLeft = coolUntil > now ? Math.ceil((coolUntil - now) / 1000) : 0;
React.useEffect(() => { if (coolUntil && !coolLeft) { setCoolUntil(0); setFails(0); setErr(""); setTimeout(() => inputRef.current && inputRef.current.focus(), 0); } }, [coolLeft, coolUntil]);
const fail = (msg) => {
const n = fails + 1;
setFails(n); setPw("");
setShake(true); setTimeout(() => setShake(false), 450);
jbSecAudit("Failed Unlock", user + " · attempt " + n);
if (n >= JB_SEC_MAX_FAILS) { setCoolUntil(Date.now() + JB_SEC_COOLDOWN_MS); setErr("Too many failed attempts — wait 60 seconds."); }
else setErr(msg || "Wrong password");
setTimeout(() => inputRef.current && inputRef.current.focus(), 0);
};
const tryUnlock = async () => {
if (busy || coolLeft || !canVerify) return;
if (!pw) { setErr("Enter your password."); return; }
setBusy(true); setErr("");
try {
const r = await D.login(user, pw);
if (r && r.ok && r.mfaRequired) {
// Unlock re-checks the password only; the MFA step was already
// satisfied when this session signed on.
jbSecAudit("Unlock", user + " · password verified (MFA session)");
onUnlocked(); return;
}
if (r && r.ok) { jbSecAudit("Unlock", user); onUnlocked(); return; }
if (r && r.serverReached) { fail("Wrong password"); return; }
setErr("Server unreachable — your password cannot be verified right now. Try again in a moment or sign off.");
setPw("");
} catch (e) { setErr(e.message || "Unlock failed."); }
finally { setBusy(false); }
};
const onKey = (e) => {
try { setCaps(!!(e.getModifierState && e.getModifierState("CapsLock"))); } catch (x) {}
if (e.key === "Enter") { e.preventDefault(); tryUnlock(); }
};
const card = {
width: 400, background: "var(--jb-surface, #FFFFFF)", borderRadius: "var(--jb-r-lg, 10px)",
boxShadow: "var(--jb-shadow-lg, 0 24px 80px rgba(0,0,0,0.55))", overflow: "hidden",
fontFamily: "var(--jb-font, var(--font-ui))", color: "var(--jb-text, #0F1A33)",
animation: shake ? "jbSecShake 0.4s" : "none",
};
const btn = (primary) => ({
flex: 1, padding: "10px 14px", cursor: "pointer", fontFamily: "var(--jb-font, var(--font-ui))",
fontSize: 13, fontWeight: 700, borderRadius: "var(--jb-r-sm, 4px)",
border: primary ? "1px solid var(--jb-navy, #0E1430)" : "1px solid var(--jb-border-strong, #BCC4D0)",
background: primary ? "var(--jb-navy, #0E1430)" : "var(--jb-surface, #FFF)",
color: primary ? "var(--jb-yellow, #FFD24A)" : "var(--jb-text, #0F1A33)",
});
return (
e.stopPropagation()} onKeyUp={(e) => e.stopPropagation()}
onContextMenu={(e) => e.preventDefault()}
style={{
position: "fixed", inset: 0, zIndex: 99990,
background: "linear-gradient(160deg, var(--jb-navy-deep, #070B1F) 0%, var(--jb-navy, #0E1430) 45%, var(--jb-blue, #1B66A8) 100%)",
display: "flex", alignItems: "center", justifyContent: "center", padding: 24,
}}>
🔒 Locked — {name ? `${name} (${user})` : user}
Dealer Management System · Station Locked
{canVerify ? (
<>
{idleMin > 0 ? `This station locks after ${idleMin} minutes without activity. ` : ""}Enter your password to continue where you left off.
Password
{ setPw(e.target.value); if (err && !coolLeft) setErr(""); }}
onKeyDown={onKey} onKeyUp={onKey}
placeholder="••••••••••"
style={{ width: "100%", padding: "10px 12px", boxSizing: "border-box", fontSize: 15,
border: "1px solid " + (err ? "var(--jb-red, #C03546)" : "var(--jb-border-strong, #BCC4D0)"),
borderRadius: "var(--jb-r-sm, 4px)", outline: 0, fontFamily: "var(--jb-font, var(--font-ui))",
background: coolLeft ? "var(--jb-surface-2, #F9FAFC)" : "#FFF", color: "var(--jb-text, #0F1A33)" }} />
{caps &&
⇪ Caps Lock is on
}
{err && (
⚠ {coolLeft ? `Too many failed attempts — try again in ${coolLeft}s.` : err}
)}
{!coolLeft && fails > 0 && fails < JB_SEC_MAX_FAILS && (
{JB_SEC_MAX_FAILS - fails} attempt{JB_SEC_MAX_FAILS - fails === 1 ? "" : "s"} left before a 60-second cooldown.
)}
{busy ? "Checking…" : coolLeft ? `Wait ${coolLeft}s` : "Unlock"}
Sign off instead
>
) : (
<>
{offline
? "This is an offline session — there is no server to verify a password against, so the station can only be signed off."
: "The DMS server is not available on this page, so a password cannot be verified — sign off to continue."}
Sign off
>
)}
Ctrl+Shift+L locks any time · every lock and unlock is written to the audit trail
);
};
/* ═══════════════════════ PII masking ═══════════════════════ */
const jbMaskPII = (value, kind) => {
const s = String(value == null ? "" : value).trim();
if (!s) return "";
const clean = s.replace(/[^A-Za-z0-9]/g, "");
const last4 = clean.slice(-4) || s.slice(-4);
const k = String(kind || "").toLowerCase();
if (k === "ssn") return "•••-••-" + last4;
if (k === "dl") return "••••" + last4;
return "••••" + last4; // "acct" / default
};
const JbSecret = ({ value, kind, label, style }) => {
const [shown, setShown] = React.useState(false);
const timer = React.useRef(null);
const s = String(value == null ? "" : value);
React.useEffect(() => () => { if (timer.current) clearTimeout(timer.current); }, []);
const reveal = (e) => {
if (e) { e.preventDefault(); e.stopPropagation(); }
if (shown) { setShown(false); if (timer.current) clearTimeout(timer.current); return; }
setShown(true);
jbSecAudit("PII Revealed", String(kind || "acct") + " · " + (label || ""));
if (timer.current) clearTimeout(timer.current);
timer.current = setTimeout(() => setShown(false), JB_SEC_REVEAL_MS);
};
if (!s.trim()) return — ;
return (
{shown ? s : jbMaskPII(s, kind)}
{shown ? "🙈" : "👁"}
);
};
/* ═══════════════════════ Password policy ═══════════════════════ */
const JB_SEC_COMMON_PW = [
"password", "password1", "password123", "passw0rd", "123456", "1234567", "12345678", "123456789", "1234567890",
"qwerty", "qwerty123", "qwertyuiop", "abc123", "abcd1234", "welcome", "welcome1", "welcome123", "letmein", "letmein1",
"iloveyou", "monkey", "dragon", "football", "baseball", "sunshine", "princess", "master", "shadow", "superman",
"trustno1", "111111", "000000", "654321", "696969", "changeme", "temp1234", "chevrolet", "chevy", "silverado",
"dealer", "dealership", "admin", "admin123", "administrator", "manager", "finance", "sales", "service",
"parts", "k2b", "dms", "gmail", "spring2026", "summer2026", "winter2026", "fall2026",
];
const jbPasswordPolicy = (pw, username) => {
const s = String(pw || "");
const u = String(username || "").trim().toLowerCase();
const problems = [];
if (s.length < 10) problems.push("At least 10 characters");
if (!/[A-Z]/.test(s)) problems.push("An uppercase letter");
if (!/[a-z]/.test(s)) problems.push("A lowercase letter");
if (!/[0-9]/.test(s)) problems.push("A digit");
const low = s.toLowerCase();
if (u && u.length >= 2 && low.includes(u)) problems.push("Must not contain the employee # / user name");
const stripped = low.replace(/[^a-z0-9]/g, "");
// RC35 (brd): + every word of the names of the stores this login sees (no store name is written in this file)
const storeWords = (() => { try { const st = window.K2BStore; const names = st ? st.list().map(x => x.dba).concat([st.branding().dba, st.branding().legalName]) : [];
return names.join(" ").toLowerCase().split(/[^a-z0-9]+/).filter(w => w.length >= 4); } catch (e) { return []; } })();
if (JB_SEC_COMMON_PW.concat(storeWords).some(c => stripped === c || low === c)) problems.push("Too common — pick something less guessable");
let score = 0;
if (s.length >= 10) score++;
if (/[A-Z]/.test(s) && /[a-z]/.test(s)) score++;
if (/[0-9]/.test(s)) score++;
if (/[^A-Za-z0-9]/.test(s) || s.length >= 14) score++;
if (problems.length) score = Math.min(score, problems.length >= 3 ? 1 : 2);
if (!s) score = 0;
return { ok: problems.length === 0, score: Math.max(0, Math.min(4, score)), problems };
};
/* Live strength meter + problem list (used by Settings → Users (Server)). */
const JbPasswordMeter = ({ pw, username, confirm, showConfirm }) => {
const r = jbPasswordPolicy(pw, username);
const colors = ["var(--jb-border, #DDE2EA)", "var(--jb-red, #C03546)", "var(--jb-amber, #B26A00)", "var(--jb-blue, #1B66A8)", "var(--jb-green, #16A34A)"];
const words = ["", "Weak", "Fair", "Good", "Strong"];
const mismatch = showConfirm && confirm !== undefined && confirm !== "" && confirm !== pw;
if (!pw) return null;
return (
{[1, 2, 3, 4].map(i => (
))}
{words[r.score]}
{r.problems.length > 0 && (
{r.problems.map((p, i) => {p} )}
)}
{r.ok && !mismatch &&
✔ Meets the password policy
}
{mismatch &&
Passwords do not match
}
);
};
/* ═══════════════════════ Settings → Security ═══════════════════════ */
const JB_SEC_EVENT_RE = /^(Sign On|Sign Off|Lock|Unlock|PII Revealed|Post Override|Password|Failed Sign-On|Failed Unlock|Encryption Enabled|MFA)/i;
const JbSecHead = ({ title, sub }) => (
);
const JbSecGroup = ({ label, children }) => (
);
const JbSecCheck = ({ ok, warn, label, detail }) => (
{ok ? "✔" : warn ? "!" : "✖"}
{label}
{detail &&
{detail}
}
);
const SecurityPanel = ({ go }) => {
const D = jbSecDMS();
const isAdmin = !!(D && D.isAdmin && D.isAdmin());
const [idle, setIdle] = React.useState(jbSecIdleMinutes);
const [tickN, setTick] = React.useState(0);
React.useEffect(() => {
const refresh = () => setTick(n => n + 1);
const onIdle = () => setIdle(jbSecIdleMinutes());
window.addEventListener("k2b-conn", refresh);
window.addEventListener("jb-sec-idle", onIdle);
const off = (D && D.subscribe) ? D.subscribe(refresh) : null;
const t = setInterval(refresh, 15000);
return () => { window.removeEventListener("k2b-conn", refresh); window.removeEventListener("jb-sec-idle", onIdle); if (off) off(); clearInterval(t); };
}, []);
if (!isAdmin) {
return (
Security settings are available to the dealership Admin only.
);
}
const user = jbSecUser();
const role = (D && D.userRole) ? D.userRole() : "";
const ti = (D && D.tokenInfo) ? (() => { try { return D.tokenInfo(); } catch (e) { return null; } })() : null;
const cs = (D && D.connState) ? D.connState() : "offline";
const offline = !!(D && D.offlineByChoice && D.offlineByChoice());
const station = (() => { try { const s = D && D.session ? D.session().station : ""; const a = window.jbLoadAuth ? window.jbLoadAuth() : {}; return a.station ? `${a.station} (${s})` : s; } catch (e) { return ""; } })();
const https = (typeof location !== "undefined") && location.protocol === "https:";
const localhost = (typeof location !== "undefined") && /^(localhost|127\.0\.0\.1|\[::1\])$/.test(location.hostname);
// (blocker 4) the setting + what is actually on disk + whether the vault is open, from the one cache path
const cst = (D && D.cacheStatus) ? (() => { try { return D.cacheStatus(); } catch (e) { return null; } })() : null;
const encrypted = cst ? (cst.encryption === "on" && cst.stored !== "plaintext")
: !!(D && D.isEncryptedAtRest && (() => { try { return D.isEncryptedAtRest(); } catch (e) { return false; } })());
const encDetail = !encrypted
? (cst && cst.encryption === "on" ? "Encryption is on, but this PC still holds a plaintext copy — it is re-encrypted at the next save." : "Turn on encryption at rest in Settings → Data & Backup to protect local data on this PC.")
: (cst && cst.vault === "locked" ? "AES-256-GCM vault — LOCKED: changes are not saved on this PC until it is unlocked (reload and enter the passphrase)."
: "AES-256-GCM vault — local data needs the dealership passphrase to open.");
const audit = (D && D.audit) ? D.audit() : [];
const events = audit.filter(e => JB_SEC_EVENT_RE.test(String(e.action || ""))).slice(0, 40);
const connLabel = { online: "Online — server session", offline: offline ? "Offline by choice — local session" : "Offline — not connected", "auth-expired": "Session expired", connecting: "Connecting…" }[cs] || cs;
const connColor = cs === "online" ? "var(--jb-green, #16A34A)" : cs === "connecting" ? "var(--jb-text-dim)" : "var(--jb-amber, #B26A00)";
const expText = ti ? (ti.expired ? "EXPIRED" : (ti.exp ? new Date(ti.exp * 1000).toLocaleString() : "n/a")) : "— (no server token)";
const cell = { padding: "5px 10px", fontSize: 12.5, verticalAlign: "top" };
const lab = { ...cell, color: "var(--jb-text-dim, #58637A)", fontWeight: 600, width: 130, whiteSpace: "nowrap" };
return (
Lock this station after
{ const v = Number(e.target.value); setIdle(v); jbSecSetIdleMinutes(v); window.k2bToast && window.k2bToast(v ? `Auto-lock set to ${v} minutes.` : "Auto-lock turned off on this station.", "ok"); }}
style={{ padding: "6px 10px", border: "1px solid var(--jb-border-strong, #BCC4D0)", borderRadius: "var(--jb-r-sm, 4px)", background: "#FFF", fontFamily: "var(--jb-font, var(--font-ui))", fontSize: 13 }}>
Off
5 minutes
10 minutes
15 minutes
30 minutes
jbLockNow()}>🔒 Lock now
Shortcut: Ctrl+Shift+L · setting is per workstation
User {user || "—"} {ti && ti.name ? ` · ${ti.name}` : ""}
Role {role || (no role — least privilege) }
Station {station || "—"}
Connection ● {connLabel}
Token expires {expText}
0} warn={idle === 0} label={idle > 0 ? `Auto-lock on (${idle} min)` : "Auto-lock off"}
detail="An unattended station locks itself and needs the user's password to resume." />
{!events.length && No security events recorded on this station yet.
}
{events.length > 0 && (
When Action
User Detail
{events.map(e => {
const bad = /Failed|Override/i.test(e.action || "");
return (
{e.ts ? new Date(e.ts).toLocaleString() : ""}
{e.action}
{e.user || "—"}
{e.detail}
);
})}
)}
{go && go("controls")}>Full audit trail ▸ }
HTTPS — every request to the DMS server is encrypted in transit.
Hashed passwords — the server stores password hashes only; no workstation ever holds a password.
Encrypted PII fields — SSNs, license numbers and account numbers are encrypted on the server and masked on screen (👁 reveals for 20 seconds and is audited).
Role fences — Sales sees CRM and Customers only; deal posting, funding and Settings are limited to managers, F&I, Accounting and Admin.
Audit trail — sign-ons, sign-offs, locks, reveals, posting overrides and password changes are logged with user, role and time.
Auto-lock — an idle station locks itself; resuming needs the signed-in user's password.
);
};
Object.assign(window, { K2BSecurityHost, jbLockNow, jbMaskPII, JbSecret, jbPasswordPolicy, JbPasswordMeter, SecurityPanel, jbSecIdleMinutes, jbSecSetIdleMinutes });