security+feat: v1.3.0 — pentest remediation, container reliability, UI overhaul
Security (33 pentest findings addressed): - CRITICAL: backend binds 127.0.0.1, path traversal in tor.rs/dwn fixed - HIGH: federation requires signatures, XSS login redirect, RBAC viewer restricted - HIGH: tar slip prevention, S3 SSRF validation, backup ID validation - MEDIUM: remember-me random secret, TOTP session rotation, password re-auth - LOW: CSP unsafe-inline removed, CORS dev-only, onion/webhook validation Container reliability: - Memory limits on all 37 containers (OOM prevention) - Exited vs stopped state distinction with health-aware status badges - Crash recovery coordination (no more restart cascade) - User-stopped tracking survives reboots - Tiered boot recovery (databases → core → services → apps) UI: - Wallet TransactionsModal, health-aware app status badges - Restart button on containers, exited/crashed red state - Mesh view overhaul, glass button updates, BaseModal/ToggleSwitch - Apps sticky header removed, dev faucet, mutable mock wallet Infrastructure: - LND REST port 8080 exposed over Tor (LND Connect fix) - Nginx cookie_session fix, deploy script Tor config updated - Dev environment: podman auto-start, boot mode simulation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
d1b48388fb
commit
1a74a930f7
@@ -92,8 +92,9 @@ export const useAIPermissionsStore = defineStore('aiPermissions', () => {
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY)
|
||||
if (stored) {
|
||||
const parsed = JSON.parse(stored) as AIContextCategory[]
|
||||
return new Set(parsed.filter(c => AI_PERMISSION_CATEGORIES.some(cat => cat.id === c)))
|
||||
const parsed: unknown = JSON.parse(stored)
|
||||
if (!Array.isArray(parsed)) return new Set()
|
||||
return new Set(parsed.filter((c: unknown) => typeof c === 'string' && AI_PERMISSION_CATEGORIES.some(cat => cat.id === c)) as AIContextCategory[])
|
||||
}
|
||||
} catch (e) {
|
||||
if (import.meta.env.DEV) console.warn('Failed to load AI permissions from storage', e)
|
||||
|
||||
@@ -63,7 +63,10 @@ const APPROVED_ORIGINS_KEY = 'neode_nostr_approved_origins'
|
||||
function getApprovedOrigins(): Set<string> {
|
||||
try {
|
||||
const stored = localStorage.getItem(APPROVED_ORIGINS_KEY)
|
||||
return stored ? new Set(JSON.parse(stored) as string[]) : new Set()
|
||||
if (!stored) return new Set()
|
||||
const parsed: unknown = JSON.parse(stored)
|
||||
if (!Array.isArray(parsed)) return new Set()
|
||||
return new Set(parsed.filter((s: unknown) => typeof s === 'string'))
|
||||
} catch {
|
||||
return new Set()
|
||||
}
|
||||
@@ -205,8 +208,11 @@ export const useAppLauncherStore = defineStore('appLauncher', () => {
|
||||
try {
|
||||
const stored = localStorage.getItem(appKey)
|
||||
if (stored) {
|
||||
const parsed = JSON.parse(stored) as { id?: string }
|
||||
appIdentityId = parsed.id || null
|
||||
const parsed: unknown = JSON.parse(stored)
|
||||
if (typeof parsed === 'object' && parsed !== null && 'id' in parsed) {
|
||||
const idVal = (parsed as Record<string, unknown>).id
|
||||
appIdentityId = typeof idVal === 'string' ? idVal : null
|
||||
}
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
|
||||
|
||||
@@ -23,8 +23,17 @@ export const useSpotlightStore = defineStore('spotlight', () => {
|
||||
try {
|
||||
const raw = localStorage.getItem(RECENT_ITEMS_KEY)
|
||||
if (raw) {
|
||||
const parsed = JSON.parse(raw) as RecentItem[]
|
||||
recentItems.value = parsed.slice(0, MAX_RECENT_ITEMS)
|
||||
const parsed: unknown = JSON.parse(raw)
|
||||
if (!Array.isArray(parsed)) {
|
||||
recentItems.value = []
|
||||
return
|
||||
}
|
||||
recentItems.value = parsed
|
||||
.filter((item: unknown) =>
|
||||
typeof item === 'object' && item !== null &&
|
||||
'id' in item && 'label' in item && 'type' in item && 'timestamp' in item
|
||||
)
|
||||
.slice(0, MAX_RECENT_ITEMS) as RecentItem[]
|
||||
} else {
|
||||
recentItems.value = []
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user