2525// `enable` — so a refactor that drops one window into the mode-dependent group
2626// (or flips its argument to `enable`/`false`) fails here instead of silently
2727// re-introducing the screen-capture leak.
28+ //
29+ // IMPORTANT — why the positive assertions are scoped to a specific method body
30+ // rather than run against the whole file: `applyContentProtection` itself now
31+ // contains the literal text `win.setContentProtection(true)`, and the overlay
32+ // show path contains `this.overlayWindow.setContentProtection(true)`. A
33+ // whole-source regex for either string therefore passes even when the *creation*
34+ // site it claims to guard has been reverted to `this.contentProtection` (verified
35+ // with a mutation probe: reverting both creation sites still gave 8/8 green).
36+ // Each positive check below is anchored to the body of the method that owns the
37+ // site, so a revert there actually fails the test.
2838
2939import { test } from 'node:test' ;
3040import assert from 'node:assert/strict' ;
@@ -39,14 +49,12 @@ const source = readFileSync(windowHelperPath, 'utf8');
3949const OVERLAY_CHROME = [ 'this.overlayWindow' , 'this.pillWindow' , 'this.toggleWindow' ] ;
4050
4151/**
42- * Extract the body of `applyContentProtection(enable: boolean): void { ... }`
43- * via brace-balancing. Mirrors the extractor in
44- * SetContentProtectionDedupe.test.mjs.
52+ * Extract a method body via brace-balancing from the first match of `sigRe`.
53+ * Mirrors the extractor in SetContentProtectionDedupe.test.mjs.
4554 */
46- function extractApplyContentProtectionBody ( src ) {
47- const sigRe = / (?: p u b l i c \s + | p r i v a t e \s + | p r o t e c t e d \s + ) ? a p p l y C o n t e n t P r o t e c t i o n \s * \( \s * e n a b l e \s * : \s * b o o l e a n \s * \) \s * : \s * v o i d \s * \{ / ;
55+ function extractMethodBody ( src , sigRe , label ) {
4856 const m = sigRe . exec ( src ) ;
49- assert . ok ( m , ' could not locate applyContentProtection signature in WindowHelper' ) ;
57+ assert . ok ( m , ` could not locate ${ label } in WindowHelper` ) ;
5058 let i = m . index + m [ 0 ] . length ;
5159 let depth = 1 ;
5260 const start = i ;
@@ -56,7 +64,7 @@ function extractApplyContentProtectionBody(src) {
5664 else if ( ch === '}' ) depth -- ;
5765 i ++ ;
5866 }
59- assert . equal ( depth , 0 , ' unbalanced braces while extracting applyContentProtection' ) ;
67+ assert . equal ( depth , 0 , ` unbalanced braces while extracting ${ label } ` ) ;
6068 return src . slice ( start , i - 1 ) ;
6169}
6270
@@ -83,9 +91,28 @@ function parseProtectionGroups(body) {
8391 return groups ;
8492}
8593
86- const body = extractApplyContentProtectionBody ( source ) ;
94+ const body = extractMethodBody (
95+ source ,
96+ / (?: p u b l i c \s + | p r i v a t e \s + | p r o t e c t e d \s + ) ? a p p l y C o n t e n t P r o t e c t i o n \s * \( \s * e n a b l e \s * : \s * b o o l e a n \s * \) \s * : \s * v o i d \s * \{ / ,
97+ 'applyContentProtection' ,
98+ ) ;
8799const groups = parseProtectionGroups ( body ) ;
88100
101+ // The overlay body is created here (createWindow builds the launcher AND the
102+ // overlay); the pill/toggle are created in createOverlayAuxWindows. Scoping the
103+ // creation assertions to these bodies is what makes them bite — see the note at
104+ // the top of this file.
105+ const createWindowBody = extractMethodBody (
106+ source ,
107+ / (?: p u b l i c \s + | p r i v a t e \s + | p r o t e c t e d \s + ) ? c r e a t e W i n d o w \s * \( \s * \) \s * : \s * v o i d \s * \{ / ,
108+ 'createWindow' ,
109+ ) ;
110+ const createAuxBody = extractMethodBody (
111+ source ,
112+ / (?: p u b l i c \s + | p r i v a t e \s + | p r o t e c t e d \s + ) ? c r e a t e O v e r l a y A u x W i n d o w s \s * \( \s * s t a r t U r l \s * : \s * s t r i n g \s * \) \s * : \s * v o i d \s * \{ / ,
113+ 'createOverlayAuxWindows' ,
114+ ) ;
115+
89116test ( 'applyContentProtection maps at least two window groups (chrome + followers)' , ( ) => {
90117 assert . ok (
91118 groups . length >= 2 ,
@@ -138,7 +165,8 @@ test('applyContentProtection keeps the launcher on the undetectable-mode toggle
138165
139166test ( 'the overlay body is never protected via this.contentProtection' , ( ) => {
140167 // The exact leak: creating/showing the overlay with the undetectable-mode
141- // value flips sharingType back to ReadOnly in normal mode.
168+ // value flips sharingType back to ReadOnly in normal mode. Whole-source on
169+ // purpose — no site anywhere may reintroduce it.
142170 assert . ok (
143171 ! / t h i s \. o v e r l a y W i n d o w \. s e t C o n t e n t P r o t e c t i o n \s * \( \s * t h i s \. c o n t e n t P r o t e c t i o n \s * \) / . test ( source ) ,
144172 `BUG: WindowHelper still calls ` +
@@ -148,20 +176,44 @@ test('the overlay body is never protected via this.contentProtection', () => {
148176 ) ;
149177} ) ;
150178
151- test ( 'the overlay is created/shown with content protection forced on' , ( ) => {
179+ test ( 'the overlay body is CREATED with content protection forced on' , ( ) => {
180+ // Scoped to createWindow so the show-path call sites cannot satisfy it.
152181 assert . ok (
153- / t h i s \. o v e r l a y W i n d o w \. s e t C o n t e n t P r o t e c t i o n \s * \( \s * t r u e \s * \) / . test ( source ) ,
154- `BUG: the overlay window is not created/shown with \`setContentProtection(true)\`. It ` +
155- `must be protected from the first frame, independent of undetectable mode.` ,
182+ / t h i s \. o v e r l a y W i n d o w \. s e t C o n t e n t P r o t e c t i o n \s * \( \s * t r u e \s * \) / . test ( createWindowBody ) ,
183+ `BUG: the overlay window is not created with \`setContentProtection(true)\` in ` +
184+ `createWindow. It must be protected from the first frame, independent of undetectable ` +
185+ `mode — a show-site call is too late and does not cover a window created while hidden.` ,
186+ ) ;
187+ } ) ;
188+
189+ test ( 'the overlay body is SHOWN with content protection forced on' , ( ) => {
190+ // switchToOverlay re-asserts protection on both the Windows (opacity-shield)
191+ // and macOS/Linux branches. Scoped so the creation site cannot satisfy it.
192+ const switchToOverlayBody = extractMethodBody (
193+ source ,
194+ / (?: p u b l i c \s + | p r i v a t e \s + | p r o t e c t e d \s + ) ? s w i t c h T o O v e r l a y \s * \( \s * i n a c t i v e \s * \? ? \s * : \s * b o o l e a n [ ^ ) ] * \) \s * : \s * v o i d \s * \{ / ,
195+ 'switchToOverlay' ,
196+ ) ;
197+ const forced = switchToOverlayBody . match (
198+ / t h i s \. o v e r l a y W i n d o w \. s e t C o n t e n t P r o t e c t i o n \s * \( \s * t r u e \s * \) / g,
199+ ) ;
200+ assert . equal (
201+ forced ?. length ,
202+ 2 ,
203+ `BUG: switchToOverlay has ${ forced ?. length ?? 0 } \`setContentProtection(true)\` call(s) ` +
204+ `on the overlay, expected 2 (the win32 opacity-shield branch and the macOS/Linux branch). ` +
205+ `Both show paths must force protection on before the first painted frame.` ,
156206 ) ;
157207} ) ;
158208
159- test ( 'the pill/toggle aux windows are protected with a literal true, not this.contentProtection' , ( ) => {
209+ test ( 'the pill/toggle aux windows are CREATED with a literal true, not this.contentProtection' , ( ) => {
210+ // Scoped to createOverlayAuxWindows so applyContentProtection's own
211+ // `win.setContentProtection(true)` cannot satisfy it.
160212 assert . ok (
161- / w i n \. s e t C o n t e n t P r o t e c t i o n \s * \( \s * t r u e \s * \) / . test ( source ) ,
213+ / w i n \. s e t C o n t e n t P r o t e c t i o n \s * \( \s * t r u e \s * \) / . test ( createAuxBody ) ,
162214 `BUG: the overlay aux windows (pill/toggle) are not protected with ` +
163- `\`win.setContentProtection(true)\` at creation. They are on-screen meeting chrome and ` +
164- `must never leak into a shared screen.` ,
215+ `\`win.setContentProtection(true)\` at creation in createOverlayAuxWindows . They are ` +
216+ `on-screen meeting chrome and must never leak into a shared screen.` ,
165217 ) ;
166218 assert . ok (
167219 ! / w i n \. s e t C o n t e n t P r o t e c t i o n \s * \( \s * t h i s \. c o n t e n t P r o t e c t i o n \s * \) / . test ( source ) ,
0 commit comments