|
4 | 4 | from shutil import rmtree |
5 | 5 | from subprocess import call |
6 | 6 | from threading import Thread |
7 | | -from unittest.mock import Mock |
| 7 | +from unittest.mock import Mock, patch |
8 | 8 | from unittest.mock import call as MockCall |
9 | | -from unittest.mock import patch |
10 | 9 |
|
11 | 10 | from django.conf import settings |
12 | 11 | from django.template import Context, Template, engines |
|
18 | 17 | from django_jinja.backend import Template as Jinja2Template |
19 | 18 | from django_jinja.builtins import DEFAULT_EXTENSIONS |
20 | 19 |
|
21 | | -from webpack_loader.exceptions import (WebpackBundleLookupError, WebpackError, |
22 | | - WebpackLoaderBadStatsError, |
23 | | - WebpackLoaderTimeoutError) |
| 20 | +from webpack_loader.exceptions import ( |
| 21 | + WebpackBundleLookupError, |
| 22 | + WebpackError, |
| 23 | + WebpackLoaderBadStatsError, |
| 24 | + WebpackLoaderTimeoutError, |
| 25 | +) |
24 | 26 | from webpack_loader.templatetags.webpack_loader import _WARNING_MESSAGE |
25 | 27 | from webpack_loader.utils import get_as_tags, get_loader |
26 | 28 |
|
@@ -239,6 +241,93 @@ def test_integrity(self): |
239 | 241 | result.rendered_content |
240 | 242 | ) |
241 | 243 |
|
| 244 | + def test_integrity_with_crosorigin_empty(self): |
| 245 | + self.compile_bundles('webpack.config.integrity.js') |
| 246 | + |
| 247 | + loader = get_loader(DEFAULT_CONFIG) |
| 248 | + with patch.dict(loader.config, {'INTEGRITY': True, 'CROSSORIGIN': ''}): |
| 249 | + view = TemplateView.as_view(template_name='single.html') |
| 250 | + request = self.factory.get('/') |
| 251 | + request.META['HTTP_HOST'] = 'crossorigen-custom-static-host.com' |
| 252 | + result = view(request) |
| 253 | + |
| 254 | + self.assertIn(( |
| 255 | + '<script src="http://custom-static-host.com/main.js" ' |
| 256 | + 'integrity="sha256-Yk6uAc7SoE41LSNc9zTBxij8YhVqBIIuRpLCaTyqrlQ= ' |
| 257 | + 'sha384-cwtz5c2CaEK8Q8ZeraWgf3qo7eO5jUDE8XMo00QTUCcbmF/fLuDtQFm8' |
| 258 | + 'g4Jh9R5D sha512-s9uhbJTCZv4WfH/F81fgS6B6XNhOuH21Xouv5XPp35WlFR7' |
| 259 | + 'ykkIafUG8cma4vbEfheH1NVbjsON5BHm8U13I4g==" ' |
| 260 | + 'crossorigin ></script>' |
| 261 | + ), result.rendered_content) |
| 262 | + self.assertIn(( |
| 263 | + '<link href="http://custom-static-host.com/main.css" ' |
| 264 | + 'rel="stylesheet" ' |
| 265 | + 'integrity="sha256-cYWwRvS04/VsttQYx4BalKYrBDuw5t8vKFhWB/LKX30= ' |
| 266 | + 'sha384-V/UxbrsEy8BK5nd+sBlN31Emmq/WdDDdI01UR8wKIFkIr6vEaT5YRaeL' |
| 267 | + 'MfLcAQvS sha512-aigPxglXDA33t9s5i0vRap5b7dFwyp7cSN6x8rOXrPpCTMu' |
| 268 | + 'bOR7qTFpmTIa8z9B0wtXxbSheBPNCEURBHKLQPw==" ' |
| 269 | + 'crossorigin />'), |
| 270 | + result.rendered_content |
| 271 | + ) |
| 272 | + |
| 273 | + def test_integrity_with_crosorigin_anonymous(self): |
| 274 | + self.compile_bundles('webpack.config.integrity.js') |
| 275 | + |
| 276 | + loader = get_loader(DEFAULT_CONFIG) |
| 277 | + with patch.dict(loader.config, {'INTEGRITY': True, 'CROSSORIGIN': 'anonymous'}): |
| 278 | + view = TemplateView.as_view(template_name='single.html') |
| 279 | + request = self.factory.get('/') |
| 280 | + request.META['HTTP_HOST'] = 'crossorigen-custom-static-host.com' |
| 281 | + result = view(request) |
| 282 | + |
| 283 | + self.assertIn(( |
| 284 | + '<script src="http://custom-static-host.com/main.js" ' |
| 285 | + 'integrity="sha256-Yk6uAc7SoE41LSNc9zTBxij8YhVqBIIuRpLCaTyqrlQ= ' |
| 286 | + 'sha384-cwtz5c2CaEK8Q8ZeraWgf3qo7eO5jUDE8XMo00QTUCcbmF/fLuDtQFm8' |
| 287 | + 'g4Jh9R5D sha512-s9uhbJTCZv4WfH/F81fgS6B6XNhOuH21Xouv5XPp35WlFR7' |
| 288 | + 'ykkIafUG8cma4vbEfheH1NVbjsON5BHm8U13I4g==" ' |
| 289 | + 'crossorigin="anonymous" ></script>' |
| 290 | + ), result.rendered_content) |
| 291 | + self.assertIn(( |
| 292 | + '<link href="http://custom-static-host.com/main.css" ' |
| 293 | + 'rel="stylesheet" ' |
| 294 | + 'integrity="sha256-cYWwRvS04/VsttQYx4BalKYrBDuw5t8vKFhWB/LKX30= ' |
| 295 | + 'sha384-V/UxbrsEy8BK5nd+sBlN31Emmq/WdDDdI01UR8wKIFkIr6vEaT5YRaeL' |
| 296 | + 'MfLcAQvS sha512-aigPxglXDA33t9s5i0vRap5b7dFwyp7cSN6x8rOXrPpCTMu' |
| 297 | + 'bOR7qTFpmTIa8z9B0wtXxbSheBPNCEURBHKLQPw==" ' |
| 298 | + 'crossorigin="anonymous" />'), |
| 299 | + result.rendered_content |
| 300 | + ) |
| 301 | + |
| 302 | + def test_integrity_with_crosorigin_use_credentials(self): |
| 303 | + self.compile_bundles('webpack.config.integrity.js') |
| 304 | + |
| 305 | + loader = get_loader(DEFAULT_CONFIG) |
| 306 | + with patch.dict(loader.config, {'INTEGRITY': True, 'CROSSORIGIN': 'use-credentials'}): |
| 307 | + view = TemplateView.as_view(template_name='single.html') |
| 308 | + request = self.factory.get('/') |
| 309 | + request.META['HTTP_HOST'] = 'crossorigen-custom-static-host.com' |
| 310 | + result = view(request) |
| 311 | + |
| 312 | + self.assertIn(( |
| 313 | + '<script src="http://custom-static-host.com/main.js" ' |
| 314 | + 'integrity="sha256-Yk6uAc7SoE41LSNc9zTBxij8YhVqBIIuRpLCaTyqrlQ= ' |
| 315 | + 'sha384-cwtz5c2CaEK8Q8ZeraWgf3qo7eO5jUDE8XMo00QTUCcbmF/fLuDtQFm8' |
| 316 | + 'g4Jh9R5D sha512-s9uhbJTCZv4WfH/F81fgS6B6XNhOuH21Xouv5XPp35WlFR7' |
| 317 | + 'ykkIafUG8cma4vbEfheH1NVbjsON5BHm8U13I4g==" ' |
| 318 | + 'crossorigin="use-credentials" ></script>' |
| 319 | + ), result.rendered_content) |
| 320 | + self.assertIn(( |
| 321 | + '<link href="http://custom-static-host.com/main.css" ' |
| 322 | + 'rel="stylesheet" ' |
| 323 | + 'integrity="sha256-cYWwRvS04/VsttQYx4BalKYrBDuw5t8vKFhWB/LKX30= ' |
| 324 | + 'sha384-V/UxbrsEy8BK5nd+sBlN31Emmq/WdDDdI01UR8wKIFkIr6vEaT5YRaeL' |
| 325 | + 'MfLcAQvS sha512-aigPxglXDA33t9s5i0vRap5b7dFwyp7cSN6x8rOXrPpCTMu' |
| 326 | + 'bOR7qTFpmTIa8z9B0wtXxbSheBPNCEURBHKLQPw==" ' |
| 327 | + 'crossorigin="use-credentials" />'), |
| 328 | + result.rendered_content |
| 329 | + ) |
| 330 | + |
242 | 331 | def test_integrity_missing_config(self): |
243 | 332 | self.compile_bundles('webpack.config.integrity.js') |
244 | 333 |
|
@@ -857,3 +946,90 @@ def test_get_as_tags_direct_usage(self): |
857 | 946 | self.assertEqual(tags[0], asset_vendor) |
858 | 947 | self.assertEqual(tags[1], asset_app1) |
859 | 948 | self.assertEqual(tags[2], asset_app2) |
| 949 | + |
| 950 | + def test_get_url_to_tag_dict_with_nonce(self): |
| 951 | + """Test the get_as_url_to_tag_dict function with nonce attribute handling.""" |
| 952 | + # Setup FakeWebpackLoader with CSP_NONCE enabled |
| 953 | + |
| 954 | + with self.settings( |
| 955 | + WEBPACK_LOADER={ |
| 956 | + "DEFAULT": { |
| 957 | + "CSP_NONCE": True, |
| 958 | + }, |
| 959 | + } |
| 960 | + ): |
| 961 | + from webpack_loader.utils import get_as_url_to_tag_dict, get_loader |
| 962 | + |
| 963 | + self.compile_bundles('webpack.config.simple.js') |
| 964 | + |
| 965 | + # Use default config but enable CSP_NONCE |
| 966 | + loader = get_loader(DEFAULT_CONFIG) |
| 967 | + original_config = loader.config.copy() |
| 968 | + try: |
| 969 | + # Test with CSP_NONCE enabled |
| 970 | + loader.config['CSP_NONCE'] = True |
| 971 | + |
| 972 | + # Create a request with csp_nonce |
| 973 | + request = self.factory.get('/') |
| 974 | + request.csp_nonce = "test-nonce-123" |
| 975 | + |
| 976 | + # Get tag dict with nonce enabled |
| 977 | + tag_dict = get_as_url_to_tag_dict('main', extension='js', attrs='', request=request) |
| 978 | + |
| 979 | + # Verify nonce is in the tag |
| 980 | + self.assertIn('nonce="test-nonce-123"', tag_dict['/static/webpack_bundles/main.js']) |
| 981 | + |
| 982 | + # Test with existing nonce in attrs - should not duplicate |
| 983 | + tag_dict = get_as_url_to_tag_dict('main', extension='js', attrs='nonce="existing-nonce"', request=request) |
| 984 | + self.assertIn('nonce="existing-nonce"', tag_dict['/static/webpack_bundles/main.js']) |
| 985 | + self.assertNotIn('nonce="test-nonce-123"', tag_dict['/static/webpack_bundles/main.js']) |
| 986 | + |
| 987 | + # Test without request - should not have nonce and should emit warning |
| 988 | + tag_dict = get_as_url_to_tag_dict('main', extension='js', attrs='', request=None) |
| 989 | + self.assertNotIn('nonce=', tag_dict['/static/webpack_bundles/main.js']) |
| 990 | + |
| 991 | + # Test with request but no csp_nonce attribute - should not have nonce and should emit warning |
| 992 | + request_without_nonce = self.factory.get('/') |
| 993 | + tag_dict = get_as_url_to_tag_dict('main', extension='js', attrs='', request=request_without_nonce) |
| 994 | + self.assertNotIn('nonce=', tag_dict['/static/webpack_bundles/main.js']) |
| 995 | + |
| 996 | + # Test with CSP_NONCE disabled - should not have nonce |
| 997 | + loader.config['CSP_NONCE'] = False |
| 998 | + tag_dict = get_as_url_to_tag_dict('main', extension='js', attrs='', request=request) |
| 999 | + self.assertNotIn('nonce=', tag_dict['/static/webpack_bundles/main.js']) |
| 1000 | + |
| 1001 | + finally: |
| 1002 | + # Restore original config |
| 1003 | + loader.config = original_config |
| 1004 | + |
| 1005 | + def test_get_url_to_tag_dict_with_different_extensions(self): |
| 1006 | + """Test the get_as_url_to_tag_dict function with different file extensions.""" |
| 1007 | + |
| 1008 | + |
| 1009 | + with self.settings( |
| 1010 | + WEBPACK_LOADER={ |
| 1011 | + "DEFAULT": { |
| 1012 | + "CSP_NONCE": True, |
| 1013 | + }, |
| 1014 | + } |
| 1015 | + ): |
| 1016 | + from webpack_loader.utils import get_as_url_to_tag_dict |
| 1017 | + self.compile_bundles('webpack.config.simple.js') |
| 1018 | + |
| 1019 | + # Create a request with csp_nonce |
| 1020 | + request = self.factory.get('/') |
| 1021 | + request.csp_nonce = "test-nonce-123" |
| 1022 | + |
| 1023 | + # Test with different extensions |
| 1024 | + |
| 1025 | + # JavaScript file |
| 1026 | + tag_dict = get_as_url_to_tag_dict('main', extension='js', attrs='', request=request) |
| 1027 | + self.assertIn('<script src="/static/webpack_bundles/main.js"', |
| 1028 | + tag_dict['/static/webpack_bundles/main.js']) |
| 1029 | + self.assertIn('nonce="test-nonce-123"', tag_dict['/static/webpack_bundles/main.js']) |
| 1030 | + |
| 1031 | + # CSS file |
| 1032 | + tag_dict = get_as_url_to_tag_dict('main', extension='css', attrs='', request=request) |
| 1033 | + self.assertIn('<link href="/static/webpack_bundles/main.css" rel="stylesheet"', |
| 1034 | + tag_dict['/static/webpack_bundles/main.css']) |
| 1035 | + self.assertIn('nonce="test-nonce-123"', tag_dict['/static/webpack_bundles/main.css']) |
0 commit comments