perf,feat(events),hardening: second review pass
Fleet overview N+1: - InstanceResourceService gains a 30s TTL cache with single-flight coalescing; the overview no longer fires six live upstream requests per device on every render (plus the re-login storm), while the per-device detail route probes live via force:true Event journal becomes live: - job terminal transitions (manual executions and the interrupted sweep) now append to the journal, so /api/v1/events SSE feeds the frontend's invalidation controller that was built but never received events - journal pruning moves off the append hot path (was an unindexable full-table json_extract scan per insert) onto the retention timer Console auth hardening: - scrypt upgraded from N=16384 to N=2^16 (OWASP interactive guidance); a new password_kdf column records the derivation per row and legacy hashes rehash transparently on the next successful login without invalidating sessions (migration 18) Legacy stack: - instance URL validation blocks IPv4-compatible IPv6 after WHATWG canonicalization (::a9fe:a9fe metadata, ::7f00:1 loopback slipped past) - status polls cool down auto-login for 60s after a failed attempt so a stale saved password cannot hammer the device into an account lockout Build hygiene: - web bundle splits app (410kB) from vendor (212kB) so framework code stays cacheable across releases; stale root package-lock.json removed (pnpm is the only lockfile)
This commit is contained in:
@@ -14,7 +14,10 @@ export function normalizeBaseUrl(rawUrl) {
|
||||
(ipv4[0] === 169 && ipv4[1] === 254) ||
|
||||
(ipv4[0] >= 224)
|
||||
)
|
||||
const prohibitedIpv6 = hostname === '::' || hostname === '::1' || /^fe[89ab][0-9a-f]:/.test(hostname) || hostname.startsWith('ff') || hostname.startsWith('::ffff:') || hostname.startsWith('64:ff9b:')
|
||||
// WHATWG canonicalization turns ::127.0.0.1 into ::7f00:1 and ::169.254.169.254 into
|
||||
// ::a9fe:a9fe; the IPv4-compatible form (:: <1-2 groups>) must be matched after
|
||||
// canonicalization or it smuggles loopback/metadata targets past the list.
|
||||
const prohibitedIpv6 = hostname === '::' || hostname === '::1' || /^::[0-9a-f]{1,4}(:[0-9a-f]{1,4})?$/.test(hostname) || /^fe[89ab][0-9a-f]:/.test(hostname) || hostname.startsWith('ff') || hostname.startsWith('::ffff:') || hostname.startsWith('64:ff9b:')
|
||||
const prohibitedMetadata = hostname === '100.100.100.200' || hostname === '168.63.129.16'
|
||||
const prohibitedTransition = hostname.startsWith('2002:')
|
||||
const isIpLiteral = isIP(hostname) !== 0
|
||||
|
||||
@@ -190,6 +190,7 @@ export function createSimAdminClient(instance, { fetchImpl = fetch, timeoutMs =
|
||||
}
|
||||
|
||||
let ephemeralSecret = ''
|
||||
let lastFailedAutoLoginAt = 0
|
||||
async function ensureAuthenticated({ credential } = {}) {
|
||||
const status = await fetchJson('/api/auth/status')
|
||||
const auth = apiData(status.data) || {}
|
||||
@@ -203,12 +204,20 @@ export function createSimAdminClient(instance, { fetchImpl = fetch, timeoutMs =
|
||||
return { configured: auth.configured ?? null, authenticated: false, loginAttempted: false, statusCode: status.status, reason: 'password_required' }
|
||||
}
|
||||
if (credential !== undefined) ephemeralSecret = ''
|
||||
// Status polls auto-login with the saved password; without a cooldown a
|
||||
// stale password turns every refresh into a fresh login attempt, which
|
||||
// some devices punish with account lockouts.
|
||||
if (credential === undefined && !ephemeralSecret && Date.now() - lastFailedAutoLoginAt < 60_000) {
|
||||
return { configured: auth.configured ?? null, authenticated: false, loginAttempted: false, statusCode: status.status, reason: 'login_cooldown' }
|
||||
}
|
||||
const login = await request('/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||||
body: JSON.stringify({ password }),
|
||||
})
|
||||
if (credential !== undefined && login.ok) ephemeralSecret = password
|
||||
if (login.ok) lastFailedAutoLoginAt = 0
|
||||
else if (credential === undefined) lastFailedAutoLoginAt = Date.now()
|
||||
return { configured: auth.configured ?? null, authenticated: login.ok, loginAttempted: true, statusCode: login.status, reason: login.ok ? null : 'login_failed' }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user