fix(gallery): stop devtools protection from breaking the whole page

With enable_devtools_protection on, every click on the gallery failed and
trivial script evaluation hung -- confirmed on two independent events. A
guest with DevTools open for an unrelated reason (network tab, a CDP-attaching
extension) got a silently unresponsive gallery with no error shown.

Mechanisms found, all in the hook (both callsites were innocent):

1. detectByDebugger ran a bare `debugger;` on every tick at medium/high
   sensitivity -- and the per-event flag maps to medium. With any debugger or
   CDP client attached the renderer paused there continuously. This is why
   Runtime.evaluate hung on 1+1 and clicks reported their target gone.
2. Four separate detectors called console.clear() -- the observed clear loop.
3. handleDevToolsDetected was useCallback([options]) over a fresh object
   literal, so runDetection changed identity every render and the effect tore
   down, rebound and re-ran detection on every render -- a 1s interval turned
   into a tight loop.
4. detectByConsole monkey-patched console.log/error/warn/info every tick
   inside a try/catch that swallowed throws, so a throw between patch and
   restore left the guest's console permanently hijacked.
5. contextmenu was preventDefault'd document-wide regardless of target,
   killing the menu on text, links and form fields -- disable_right_click is
   the separate setting meant to cover the whole page.

Kept: the DevTools shortcut keys (only those exact combos; everything else
passes through), the docked-DevTools viewport heuristic as a pure measurement
on resize plus one check at mount, right-click blocked on IMG/CANVAS/VIDEO
targets only. The public API (onDevToolsDetected, redirectOnDetection,
redirectUrl, isDetected, reset) is unchanged, so PhotoLightbox needed no edit.

Removed: debugger traps, console.clear, console monkey-patching, the
timing/element/toString probes, the polling interval, document-wide
contextmenu blocking. Undocked DevTools is now deliberately undetectable --
every technique that catches it costs the page its responsiveness for
everyone. This is a deterrent, not a security boundary.

Also raised the viewport threshold (100 -> 160/200/260 by sensitivity):
browser chrome with a bookmarks bar is ~140px, so the old check false-positived
on ordinary windows, which at protectionLevel 'maximum' redirected legitimate
guests off the gallery.

Refs testplan REPORT.md #3 (Part 4).
This commit is contained in:
Paul Nothaft
2026-09-01 16:30:06 +02:00
parent c6cb01865e
commit 9d4bd7ab30
3 changed files with 244 additions and 214 deletions
+9 -7
View File
@@ -41,7 +41,7 @@ The image protection system provides multiple layers of security to prevent unau
### useDevToolsProtection Hook
Detects when developer tools are opened using multiple methods:
Detects when developer tools are opened, passively:
```typescript
import { useDevToolsProtection } from '../hooks/useDevToolsProtection';
@@ -56,12 +56,14 @@ const { isDetected, reset } = useDevToolsProtection({
```
**Detection Methods:**
- Timing-based detection (console.log performance)
- Window size monitoring
- Console usage tracking
- Debugger statement timing
- Element inspection detection
- Function toString override
- Window size monitoring (docked DevTools panel)
- DevTools shortcut keys (F12, Ctrl+Shift+I/J/C, Ctrl+U)
Detection is deliberately passive: `debugger` traps, console overrides and
`console.clear()` probes were removed because they freeze/spam the page for
every visitor who has DevTools open, breaking navigation, buttons and forms —
far beyond the image protection this is meant to be. Undocked DevTools is not
detected. This is a deterrent, not a security boundary.
### Enhanced useImageProtection Hook