Security hardening: token-at-rest, rate limiting, XSS, transactions

- auth: impersonate stores empty data instead of raw cookie; unimpersonate
  issues a fresh session rather than replaying a stored token
- api/index.php: restore rate limiting (10 req/min auth, 120 general)
- nova.js: 401 redirects to login instead of silently returning error;
  escHtml now escapes single quotes to prevent onclick XSS
- accounts: wrap ownership-change 4-write path in beginTransaction/commit;
  restore audit body on account.update
- reseller/user login cards: use $_pname instead of hardcoded 'NovaCPX'

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 07:51:21 +00:00
parent d29b8b9d65
commit 89c9bfdc49
6 changed files with 69 additions and 28 deletions
+2 -2
View File
@@ -56,7 +56,7 @@ window.Nova = (() => {
return { success: false, message: 'Network error — check your connection' };
}
_barDone();
if (res.status === 401) { try { return await res.json(); } catch { return { success: false, message: 'Unauthorized' }; } }
if (res.status === 401) { location.href = '/?redirect=' + encodeURIComponent(location.pathname); return null; }
if (res.status === 429) {
const reset = res.headers.get('X-RateLimit-Reset');
const wait = reset ? Math.max(0, Math.ceil(Number(reset) - Date.now() / 1000)) : 60;
@@ -170,7 +170,7 @@ window.Nova = (() => {
}
function escHtml(str) {
return String(str ?? '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
return String(str ?? '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');
}
// ── Loading overlay ───────────────────────────────────────────────────────