diff --git a/core/lib/presentation/utils/html_transformer/base/dom_transformer.dart b/core/lib/presentation/utils/html_transformer/base/dom_transformer.dart index 28e5fcb57..ceeac3d1d 100644 --- a/core/lib/presentation/utils/html_transformer/base/dom_transformer.dart +++ b/core/lib/presentation/utils/html_transformer/base/dom_transformer.dart @@ -2,6 +2,7 @@ import 'package:core/data/network/dio_client.dart'; import 'package:core/utils/app_logger.dart'; import 'package:dartz/dartz.dart'; +import 'package:flutter/widgets.dart' show visibleForTesting; import 'package:html/dom.dart'; /// Transforms the HTML DOM. @@ -11,6 +12,9 @@ abstract class DomTransformer { static final _backgroundImageRegex = RegExp(r'''\bbackground-image\s*:\s*url\(\s*['"]?([^' ")]+)['"]?\s*\)'''); + @visibleForTesting + static RegExp get backgroundImageRegex => _backgroundImageRegex; + /// Uses the `DOM` [document] to transform the `document`. /// /// All changes will be visible to subsequent transformers. diff --git a/core/lib/presentation/utils/html_transformer/dom/normalize_line_height_in_style_transformer.dart b/core/lib/presentation/utils/html_transformer/dom/normalize_line_height_in_style_transformer.dart index 53eccd7c8..f78d132bd 100644 --- a/core/lib/presentation/utils/html_transformer/dom/normalize_line_height_in_style_transformer.dart +++ b/core/lib/presentation/utils/html_transformer/dom/normalize_line_height_in_style_transformer.dart @@ -1,6 +1,7 @@ import 'package:core/data/network/dio_client.dart'; import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart'; import 'package:core/utils/app_logger.dart'; +import 'package:flutter/widgets.dart' show visibleForTesting; import 'package:html/dom.dart'; class NormalizeLineHeightInStyleTransformer extends DomTransformer { @@ -15,6 +16,9 @@ class NormalizeLineHeightInStyleTransformer extends DomTransformer { caseSensitive: false, ); + @visibleForTesting + static RegExp get lineHeightPattern => _lineHeightPattern; + @override Future process({ required Document document, diff --git a/core/lib/utils/html/html_utils.dart b/core/lib/utils/html/html_utils.dart index 794f5fb9c..627ea2580 100644 --- a/core/lib/utils/html/html_utils.dart +++ b/core/lib/utils/html/html_utils.dart @@ -39,6 +39,15 @@ class HtmlUtils { caseSensitive: false, ); + @visibleForTesting + static RegExp get htmlStartTagRegex => _htmlStartTagRegex; + + @visibleForTesting + static RegExp get htmlEndTagRegex => _htmlEndTagRegex; + + @visibleForTesting + static RegExp get urlRegex => _urlRegex; + static const removeLineHeight1px = ( script: ''' document.querySelectorAll('[style*="line-height"]').forEach(el => { diff --git a/core/test/utils/redos_vulnerability_test.dart b/core/test/utils/redos_vulnerability_test.dart index 2cba1ab11..bd1b78310 100644 --- a/core/test/utils/redos_vulnerability_test.dart +++ b/core/test/utils/redos_vulnerability_test.dart @@ -1,4 +1,5 @@ import 'package:core/data/network/dio_client.dart'; +import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart'; import 'package:core/presentation/utils/html_transformer/dom/image_transformers.dart'; import 'package:core/presentation/utils/html_transformer/dom/normalize_line_height_in_style_transformer.dart'; import 'package:core/utils/html/html_utils.dart'; @@ -684,10 +685,47 @@ void main() { group('Regex initialization optimization', () { test('regex patterns should be reused, not recreated', () { - // This test verifies that regex patterns are static and reused - final regex1 = StringConvert.base64ValidationRegex; - final regex2 = StringConvert.base64ValidationRegex; - expect(identical(regex1, regex2), true); + // This test verifies that all refactored regex patterns are static and reused + // across multiple calls, preventing unnecessary regex compilation overhead + + // StringConvert patterns + final base64_1 = StringConvert.base64ValidationRegex; + final base64_2 = StringConvert.base64ValidationRegex; + expect(identical(base64_1, base64_2), true, + reason: 'base64ValidationRegex should be static and reused'); + + final emailLocalhost1 = StringConvert.emailLocalhostRegex; + final emailLocalhost2 = StringConvert.emailLocalhostRegex; + expect(identical(emailLocalhost1, emailLocalhost2), true, + reason: 'emailLocalhostRegex should be static and reused'); + + // DomTransformer patterns + final bgImage1 = DomTransformer.backgroundImageRegex; + final bgImage2 = DomTransformer.backgroundImageRegex; + expect(identical(bgImage1, bgImage2), true, + reason: 'backgroundImageRegex should be static and reused'); + + // NormalizeLineHeightInStyleTransformer patterns + final lineHeight1 = NormalizeLineHeightInStyleTransformer.lineHeightPattern; + final lineHeight2 = NormalizeLineHeightInStyleTransformer.lineHeightPattern; + expect(identical(lineHeight1, lineHeight2), true, + reason: 'lineHeightPattern should be static and reused'); + + // HtmlUtils patterns + final htmlStartTag1 = HtmlUtils.htmlStartTagRegex; + final htmlStartTag2 = HtmlUtils.htmlStartTagRegex; + expect(identical(htmlStartTag1, htmlStartTag2), true, + reason: 'htmlStartTagRegex should be static and reused'); + + final htmlEndTag1 = HtmlUtils.htmlEndTagRegex; + final htmlEndTag2 = HtmlUtils.htmlEndTagRegex; + expect(identical(htmlEndTag1, htmlEndTag2), true, + reason: 'htmlEndTagRegex should be static and reused'); + + final urlRegex1 = HtmlUtils.urlRegex; + final urlRegex2 = HtmlUtils.urlRegex; + expect(identical(urlRegex1, urlRegex2), true, + reason: 'urlRegex should be static and reused'); }); }); });