diff --git a/.trivyignore b/.trivyignore index c87e1df..d7e87e2 100644 --- a/.trivyignore +++ b/.trivyignore @@ -16,3 +16,6 @@ CVE-2026-29786 # tar node-tar <7.5.11 vulnerability (npm bundled) CVE-2026-31802 + +# picomatch ReDoS via crafted glob patterns (npm bundled) +CVE-2026-33671 diff --git a/package-lock.json b/package-lock.json index e0f9520..95adf8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,7 @@ "lint-staged": "^16.4.0", "mermaid": "^11.13.0", "phosphor-svelte": "^3.1.0", + "picomatch": "^4.0.4", "pino-pretty": "^13.1.3", "prettier": "^3.8.1", "prettier-plugin-svelte": "^3.5.1", @@ -8040,9 +8041,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index a3dd083..763d88d 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "lint-staged": "^16.4.0", "mermaid": "^11.13.0", "phosphor-svelte": "^3.1.0", + "picomatch": "^4.0.4", "pino-pretty": "^13.1.3", "prettier": "^3.8.1", "prettier-plugin-svelte": "^3.5.1", diff --git a/src/lib/components/ProgressBar.svelte b/src/lib/components/ProgressBar.svelte index 67af893..42c50dd 100644 --- a/src/lib/components/ProgressBar.svelte +++ b/src/lib/components/ProgressBar.svelte @@ -36,38 +36,50 @@ return ratio * duration; } + // Track pointer type so we know whether to clear hover on end + let activePointerType: string | null = null; + function handlePointerDown(e: PointerEvent) { e.preventDefault(); e.stopPropagation(); scrubbing = true; - barEl?.setPointerCapture(e.pointerId); + activePointerType = e.pointerType; const t = getTimeFromX(e.clientX); scrubTime = t; onscrubstart?.(); onseek(t); + // Listen on window so the finger can drift anywhere and we still track X + window.addEventListener('pointermove', handleWindowPointerMove); + window.addEventListener('pointerup', handleWindowPointerUp); + window.addEventListener('pointercancel', handleWindowPointerUp); } - function handlePointerMove(e: PointerEvent) { - if (scrubbing) { - const t = getTimeFromX(e.clientX); - scrubTime = t; - // Video is paused during scrub, so seek directly on every move — no throttle needed - onseek(t); - } else if (e.pointerType === 'mouse') { - hoverProgress = getTimeFromX(e.clientX); - } + function handleWindowPointerMove(e: PointerEvent) { + const t = getTimeFromX(e.clientX); + scrubTime = t; + onseek(t); } - function handlePointerUp(e: PointerEvent) { + function handleWindowPointerUp(_e: PointerEvent) { if (!scrubbing) return; + window.removeEventListener('pointermove', handleWindowPointerMove); + window.removeEventListener('pointerup', handleWindowPointerUp); + window.removeEventListener('pointercancel', handleWindowPointerUp); if (scrubTime !== null) onseek(scrubTime); scrubbing = false; scrubTime = null; - barEl?.releasePointerCapture(e.pointerId); - if (e.pointerType !== 'mouse') hoverProgress = null; + if (activePointerType !== 'mouse') hoverProgress = null; + activePointerType = null; onscrubend?.(); } + function handlePointerMove(e: PointerEvent) { + // Only used for mouse hover preview when not scrubbing + if (!scrubbing && e.pointerType === 'mouse') { + hoverProgress = getTimeFromX(e.clientX); + } + } + function handlePointerLeave() { if (!scrubbing) hoverProgress = null; } @@ -81,8 +93,6 @@ bind:this={barEl} onpointerdown={handlePointerDown} onpointermove={handlePointerMove} - onpointerup={handlePointerUp} - onpointercancel={handlePointerUp} onpointerleave={handlePointerLeave} tabindex="0" role="slider" diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index cf15b9c..3e53173 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -5,7 +5,12 @@ import { addVideoModalOpen } from '$lib/stores/addVideoModal'; import { queueSheetOpen } from '$lib/stores/queueSheet'; import { homeTapSignal } from '$lib/stores/homeTap'; - import { unreadCount, startPolling, stopPolling } from '$lib/stores/notifications'; + import { + unreadCount, + unwatchedCount, + startPolling, + stopPolling + } from '$lib/stores/notifications'; import { queueCount } from '$lib/stores/queue'; import { globalMuted } from '$lib/stores/mute'; import { initAudioContext } from '$lib/audio/normalizer'; @@ -179,12 +184,22 @@