@@ -237,6 +237,20 @@ def test_blocked_fetch_in_async_iife(self) -> None:
237237 errors = exc_info .value .errors ()
238238 assert any ("fetch" in str (e .get ("msg" , "" )) for e in errors )
239239
240+ def test_allowed_prefetch (self ) -> None :
241+ """Test that prefetch() is not blocked by the fetch pattern."""
242+ operation = RoutineJsEvaluateOperation (
243+ js = "(function() { var link = document.createElement('link'); link.rel = 'prefetch'; return prefetch('/next-page'); })()"
244+ )
245+ assert operation .js is not None
246+
247+ def test_allowed_refetch (self ) -> None :
248+ """Test that refetch() is not blocked by the fetch pattern."""
249+ operation = RoutineJsEvaluateOperation (
250+ js = "(function() { return refetch(); })()"
251+ )
252+ assert operation .js is not None
253+
240254 def test_blocked_eval_in_async_iife (self ) -> None :
241255 """Test that eval() is blocked even in async IIFE."""
242256 with pytest .raises (ValidationError ) as exc_info :
@@ -287,16 +301,6 @@ def test_blocked_addeventlistener(self) -> None:
287301 errors = exc_info .value .errors ()
288302 assert any ("addEventListener" in str (e .get ("msg" , "" )) for e in errors )
289303
290- def test_blocked_onevent_handler (self ) -> None :
291- """Test that onclick= style handlers are blocked."""
292- with pytest .raises (ValidationError ) as exc_info :
293- RoutineJsEvaluateOperation (
294- js = "(function() { document.onclick = () => {}; })()"
295- )
296-
297- errors = exc_info .value .errors ()
298- assert any ("on" in str (e .get ("msg" , "" )) for e in errors )
299-
300304 def test_blocked_mutation_observer (self ) -> None :
301305 """Test that MutationObserver is blocked."""
302306 with pytest .raises (ValidationError ) as exc_info :
@@ -327,26 +331,6 @@ def test_blocked_window_close(self) -> None:
327331 errors = exc_info .value .errors ()
328332 assert any ("window\\ .close" in str (e .get ("msg" , "" )) or "window.close" in str (e .get ("msg" , "" )) for e in errors )
329333
330- def test_blocked_location (self ) -> None :
331- """Test that location.* is blocked."""
332- with pytest .raises (ValidationError ) as exc_info :
333- RoutineJsEvaluateOperation (
334- js = "(function() { location.href = 'http://example.com'; })()"
335- )
336-
337- errors = exc_info .value .errors ()
338- assert any ("location" in str (e .get ("msg" , "" )) for e in errors )
339-
340- def test_blocked_history (self ) -> None :
341- """Test that history.* is blocked."""
342- with pytest .raises (ValidationError ) as exc_info :
343- RoutineJsEvaluateOperation (
344- js = "(function() { history.pushState({}, '', '/new'); })()"
345- )
346-
347- errors = exc_info .value .errors ()
348- assert any ("history" in str (e .get ("msg" , "" )) for e in errors )
349-
350334 def test_allowed_promise (self ) -> None :
351335 """Test that Promise is allowed."""
352336 operation = RoutineJsEvaluateOperation (
@@ -660,6 +644,28 @@ def test_post_interpolation_validation_allows_safe_interpolation(self) -> None:
660644 # Complex Real-World Examples
661645 # ============================================================================
662646
647+ def test_allowed_string_containing_on_prefix (self ) -> None :
648+ """Test that string literals containing 'on' followed by word chars and '=' are not blocked.
649+
650+ The pattern r'on\\ w+\\ s*=' is meant to block event handlers like onclick=, onload=, etc.
651+ But it should NOT block occurrences inside string literals (e.g., 'lots_json_length=').
652+ """
653+ js_code = (
654+ "(function () { var r = window.chrComponents && window.chrComponents.lots; "
655+ "if (!(r && r.data && Array.isArray(r.data.lots) && r.data.lots.length)) { "
656+ "console.log('no_lots'); return null; } var a = r.data.lots; var o = []; "
657+ "for (var i = 0; i < a.length; i++) { var l = a[i]; o.push({ "
658+ "lot_number: l.lot_id_txt || null, artist: l.title_primary_txt || null, "
659+ "title: l.title_secondary_txt || null, estimate_text: l.estimate_txt || null, "
660+ "estimate_low: l.estimate_low || null, estimate_high: l.estimate_high || null, "
661+ "price_realised_text: l.price_realised_txt || null, price_realised: l.price_realised || null, "
662+ "url: l.url || null }); } var s = JSON.stringify(o); "
663+ "console.log('lots_json_length=' + s.length); "
664+ "sessionStorage.setItem('lots_json', s); return null; })()"
665+ )
666+ operation = RoutineJsEvaluateOperation (js = js_code )
667+ assert operation .js == js_code
668+
663669 def test_complex_valid_code (self ) -> None :
664670 """Test complex but valid JS code."""
665671 js_code = """(function() {
0 commit comments