mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 13:15:43 +00:00 
			
		
		
		
	Fixes for SITE_URL validity checks (#10619)
* [docker] Allow HTTPS port to be specified for Caddy proxy * Fix naming collision for INVENTREE_WEB_PORT * Push InvenTree version first * Adjust Caddyfile - Change backup server * Fix docstring * Tweak for site URL check: - Ignore port if SITE_LAX_PROTOCOL_CHECK is set - Invert logic for readability * Additional checks for port mismatch * Adjust middleware checks - Allow for less strict checking of CSRF_TRUSTED_ORIGINS * Slight refactor
This commit is contained in:
		| @@ -239,13 +239,29 @@ class InvenTreeHostSettingsMiddleware(MiddlewareMixin): | ||||
|         accessed_scheme = request._current_scheme_host | ||||
|         referer = urlsplit(accessed_scheme) | ||||
|  | ||||
|         # Ensure that the settings are set correctly with the current request | ||||
|         matches = ( | ||||
|             (accessed_scheme and not accessed_scheme.startswith(settings.SITE_URL)) | ||||
|             if not settings.SITE_LAX_PROTOCOL_CHECK | ||||
|             else not is_same_domain(referer.netloc, urlsplit(settings.SITE_URL).netloc) | ||||
|         site_url = urlsplit(settings.SITE_URL) | ||||
|  | ||||
|         # Check if the accessed URL matches the SITE_URL setting | ||||
|         site_url_match = ( | ||||
|             ( | ||||
|                 # Exact match on domain | ||||
|                 is_same_domain(referer.netloc, site_url.netloc) | ||||
|                 and referer.scheme == site_url.scheme | ||||
|             ) | ||||
|             or ( | ||||
|                 # Lax protocol match, accessed URL starts with SITE_URL | ||||
|                 settings.SITE_LAX_PROTOCOL_CHECK | ||||
|                 and accessed_scheme.startswith(settings.SITE_URL) | ||||
|             ) | ||||
|             or ( | ||||
|                 # Lax protocol match, same domain | ||||
|                 settings.SITE_LAX_PROTOCOL_CHECK | ||||
|                 and referer.hostname == site_url.hostname | ||||
|             ) | ||||
|         ) | ||||
|         if matches: | ||||
|  | ||||
|         if not site_url_match: | ||||
|             # The accessed URL does not match the SITE_URL setting | ||||
|             if ( | ||||
|                 isinstance(settings.CSRF_TRUSTED_ORIGINS, list) | ||||
|                 and len(settings.CSRF_TRUSTED_ORIGINS) > 1 | ||||
| @@ -263,17 +279,31 @@ class InvenTreeHostSettingsMiddleware(MiddlewareMixin): | ||||
|                     request, 'config_error.html', {'error_message': msg}, status=500 | ||||
|                 ) | ||||
|  | ||||
|         # Check trusted origins | ||||
|         if not any( | ||||
|             is_same_domain(referer.netloc, host) | ||||
|             for host in [ | ||||
|                 urlsplit(origin).netloc.lstrip('*') | ||||
|         trusted_origins_match = ( | ||||
|             # Matching domain found in allowed origins | ||||
|             any( | ||||
|                 is_same_domain(referer.netloc, host) | ||||
|                 for host in [ | ||||
|                     urlsplit(origin).netloc.lstrip('*') | ||||
|                     for origin in settings.CSRF_TRUSTED_ORIGINS | ||||
|                 ] | ||||
|             ) | ||||
|         ) or ( | ||||
|             # Lax protocol match allowed | ||||
|             settings.SITE_LAX_PROTOCOL_CHECK | ||||
|             and any( | ||||
|                 referer.hostname == urlsplit(origin).hostname | ||||
|                 for origin in settings.CSRF_TRUSTED_ORIGINS | ||||
|             ] | ||||
|         ): | ||||
|             ) | ||||
|         ) | ||||
|  | ||||
|         # Check trusted origins | ||||
|         if not trusted_origins_match: | ||||
|             msg = f'INVE-E7: The used path `{accessed_scheme}` is not in the TRUSTED_ORIGINS' | ||||
|             logger.error(msg) | ||||
|             return render( | ||||
|                 request, 'config_error.html', {'error_message': msg}, status=500 | ||||
|             ) | ||||
|  | ||||
|         # All checks passed | ||||
|         return None | ||||
|   | ||||
| @@ -112,6 +112,15 @@ class MiddlewareTests(InvenTreeTestCase): | ||||
|  | ||||
|     def test_site_lax_protocol(self): | ||||
|         """Test that the site URL check is correctly working with/without lax protocol check.""" | ||||
|         # Test that a completely different host fails | ||||
|         with self.settings( | ||||
|             SITE_URL='https://testserver', CSRF_TRUSTED_ORIGINS=['https://testserver'] | ||||
|         ): | ||||
|             response = self.client.get( | ||||
|                 reverse('web'), HTTP_HOST='otherhost.example.com' | ||||
|             ) | ||||
|             self.assertContains(response, 'INVE-E7: The visited path', status_code=500) | ||||
|  | ||||
|         # Simple setup with proxy | ||||
|         with self.settings( | ||||
|             SITE_URL='https://testserver', CSRF_TRUSTED_ORIGINS=['https://testserver'] | ||||
| @@ -128,6 +137,24 @@ class MiddlewareTests(InvenTreeTestCase): | ||||
|             response = self.client.get(reverse('web')) | ||||
|             self.assertContains(response, 'INVE-E7: The visited path', status_code=500) | ||||
|  | ||||
|     def test_site_url_port(self): | ||||
|         """URL checks with different ports.""" | ||||
|         with self.settings( | ||||
|             SITE_URL='https://testserver:8000', | ||||
|             CSRF_TRUSTED_ORIGINS=['https://testserver:8000'], | ||||
|         ): | ||||
|             response = self.client.get(reverse('web'), HTTP_HOST='testserver:8008') | ||||
|             self.do_positive_test(response) | ||||
|  | ||||
|         # Try again with strict protocol check | ||||
|         with self.settings( | ||||
|             SITE_URL='https://testserver:8000', | ||||
|             CSRF_TRUSTED_ORIGINS=['https://testserver:8000'], | ||||
|             SITE_LAX_PROTOCOL_CHECK=False, | ||||
|         ): | ||||
|             response = self.client.get(reverse('web'), HTTP_HOST='testserver:8008') | ||||
|             self.assertContains(response, 'INVE-E7: The visited path', status_code=500) | ||||
|  | ||||
|     def test_site_url_checks_multi(self): | ||||
|         """Test that the site URL check is correctly working in a multi-site setup.""" | ||||
|         # multi-site setup with trusted origins | ||||
| @@ -149,7 +176,7 @@ class MiddlewareTests(InvenTreeTestCase): | ||||
|             ) | ||||
|             self.do_positive_test(response) | ||||
|  | ||||
|             # A non-trsuted origin must still fail in multi - origin setup | ||||
|             # A non-trusted origin must still fail in multi - origin setup | ||||
|             response = self.client.get( | ||||
|                 'https://not-my-testserver.example.com/web/', | ||||
|                 SERVER_NAME='not-my-testserver.example.com', | ||||
|   | ||||
		Reference in New Issue
	
	Block a user