Coverage for src/backend/InvenTree/report/fetcher.py: 21%
31 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 17:47 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 17:47 +0000
1"""WeasyPrint URL fetcher with security restrictions for report generation."""
3from urllib.parse import urlparse
5import structlog
6from weasyprint.urls import URLFetcher
8logger = structlog.get_logger('inventree')
11class InvenTreeURLFetcher(URLFetcher):
12 """WeasyPrint URL fetcher restricted to safe origins."""
14 def __init__(self, **kwargs):
15 """Disable redirect following so a same-origin URL cannot be used for SSRF."""
16 kwargs.setdefault('allow_redirects', False)
17 super().__init__(**kwargs)
19 def fetch(self, url, headers=None):
20 """Validate *url* before delegating to the parent fetcher."""
21 parsed = urlparse(url)
22 scheme = parsed.scheme.lower()
24 if scheme in ('data', 'http', 'https'):
25 self._validate_http_url(url, parsed)
26 return super().fetch(url, headers)
28 if scheme == 'file':
29 logger.warning("InvenTreeURLFetcher: blocked file:// URL: '%s'", url)
30 raise ValueError(
31 f'file:// URLs are not permitted in report templates: {url}'
32 )
34 raise ValueError(f"URL scheme '{scheme}' is not permitted in report templates")
36 def _validate_http_url(self, url: str, parsed) -> None:
37 """Raise if HTTP/HTTPS fetching is disabled or the URL is an SSRF risk."""
38 from common.settings import get_global_setting
39 from InvenTree.helpers_model import validate_url_no_ssrf
41 if not parsed.netloc:
42 # data: URIs — self-contained, no network access required.
43 return
45 if not get_global_setting('REPORT_FETCH_URLS', cache=False):
46 logger.warning(
47 "InvenTreeURLFetcher: blocked URL '%s': remote fetching is disabled (REPORT_FETCH_URLS=False)",
48 url,
49 )
50 raise ValueError(
51 f'Remote URL fetching is disabled in report templates: {url}'
52 )
54 try:
55 validate_url_no_ssrf(url)
56 except ValueError:
57 logger.warning(
58 "InvenTreeURLFetcher: blocked URL '%s': resolves to a private or reserved address",
59 url,
60 )
61 raise