77import re
88import subprocess
99import sys
10+ import warnings
1011import zipfile
1112from datetime import datetime
1213from enum import IntEnum
2122from selenium .common import SessionNotCreatedException
2223from selenium .webdriver .chrome .options import Options
2324from selenium .webdriver .chrome .service import Service
25+ from urllib3 .exceptions import InsecureRequestWarning
2426from webdriver_manager .core .os_manager import ChromeType , OperationSystemManager
2527
2628from . import (
3739# https://stackoverflow.com/questions/3597480/how-to-make-python-3-print-utf8
3840sys .stdout = open (sys .stdout .fileno (), mode = "w" , encoding = "utf8" , closefd = False )
3941
42+ SSL_CHECK_DISABLED_WARNING_PRINTED = False
43+
4044
4145@contextlib .contextmanager
4246def measure_performance (title : str ) -> Iterator [None ]:
@@ -60,6 +64,23 @@ def extract_page_count(logs: List[Dict[str, str]]) -> int:
6064 raise ValueError ("No page count found in logs." )
6165
6266
67+ def print_ssl_check_disabled_warning () -> None :
68+ global SSL_CHECK_DISABLED_WARNING_PRINTED # noqa: PLW0603
69+
70+ if SSL_CHECK_DISABLED_WARNING_PRINTED :
71+ return
72+
73+ print ( # noqa: T201
74+ "warning: html2pdf4doc: SSL certificate verification is disabled "
75+ "for HTTP downloads. This is insecure and should only be used "
76+ "in trusted environments. Re-enable verification by removing "
77+ "--disable-ssl-check." ,
78+ file = sys .stderr ,
79+ flush = True ,
80+ )
81+ SSL_CHECK_DISABLED_WARNING_PRINTED = True
82+
83+
6384class HPDExitCode (IntEnum ):
6485 GENERAL_ERROR = 1
6586 COULD_NOT_FIND_CHROME = 5
@@ -96,7 +117,9 @@ def __str__(self) -> str:
96117
97118
98119class ChromeDriverManager :
99- def get_chrome_driver (self , path_to_cache_dir : str ) -> str :
120+ def get_chrome_driver (
121+ self , path_to_cache_dir : str , verify_ssl : bool = True
122+ ) -> str :
100123 chrome_version : Optional [str ] = self .get_chrome_version ()
101124
102125 # If Web Driver Manager cannot detect Chrome, it returns None.
@@ -153,6 +176,7 @@ def get_chrome_driver(self, path_to_cache_dir: str) -> str:
153176 os_type ,
154177 path_to_cached_chrome_driver_dir ,
155178 path_to_cached_chrome_driver ,
179+ verify_ssl ,
156180 )
157181 assert os .path .isfile (path_to_downloaded_chrome_driver )
158182 os .chmod (path_to_downloaded_chrome_driver , 0o755 )
@@ -166,9 +190,10 @@ def _download_chromedriver(
166190 os_type : str ,
167191 path_to_driver_cache_dir : str ,
168192 path_to_cached_chrome_driver : str ,
193+ verify_ssl : bool = True ,
169194 ) -> str :
170195 url = "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json"
171- response = cls .send_http_get_request (url )
196+ response = cls .send_http_get_request (url , verify_ssl = verify_ssl )
172197 if response is None :
173198 raise RuntimeError (
174199 "Could not download known-good-versions-with-downloads.json"
@@ -210,7 +235,7 @@ def _download_chromedriver(
210235 print ( # noqa: T201
211236 f"html2pdf4doc: downloading ChromeDriver from: { driver_url } "
212237 )
213- response = cls .send_http_get_request (driver_url )
238+ response = cls .send_http_get_request (driver_url , verify_ssl = verify_ssl )
214239
215240 if response is None :
216241 raise RuntimeError (
@@ -234,22 +259,34 @@ def _download_chromedriver(
234259 return path_to_cached_chrome_driver
235260
236261 @staticmethod
237- def send_http_get_request (url : str ) -> Response :
262+ def send_http_get_request (url : str , verify_ssl : bool = True ) -> Response :
238263 last_error : Optional [Exception ] = None
239264 for attempt in range (1 , 4 ):
240265 print ( # noqa: T201
241266 f"html2pdf4doc: sending GET request attempt { attempt } : { url } "
242267 )
243268 try :
244- return requests .get (url , timeout = (5 , 5 ))
269+ if verify_ssl :
270+ return requests .get (url , timeout = (5 , 5 ), verify = True )
271+
272+ print_ssl_check_disabled_warning ()
273+ with warnings .catch_warnings ():
274+ warnings .simplefilter ("ignore" , InsecureRequestWarning )
275+ return requests .get (url , timeout = (5 , 5 ), verify = False )
276+ except requests .exceptions .SSLError as ssl_error_ :
277+ raise RuntimeError (
278+ "SSL certificate verification failed for URL: "
279+ f"{ url } . If you trust the target and need to bypass "
280+ "certificate verification, rerun the command with "
281+ "--disable-ssl-check."
282+ ) from ssl_error_
245283 except requests .exceptions .ConnectTimeout as connect_timeout_ :
246284 last_error = connect_timeout_
247285 except requests .exceptions .ReadTimeout as read_timeout_ :
248286 last_error = read_timeout_
249- except Exception as exception_ :
250- raise AssertionError (
251- "html2pdf4doc: unknown exception" , exception_
252- ) from None
287+ except requests .exceptions .RequestException as request_error_ :
288+ last_error = request_error_
289+ break
253290 print ( # noqa: T201
254291 f"html2pdf4doc: "
255292 f"failed to get response for URL: { url } with error: { last_error } "
@@ -423,14 +460,15 @@ def create_webdriver(
423460 chromedriver_argument : Optional [str ],
424461 path_to_cache_dir : str ,
425462 page_load_timeout : int ,
463+ verify_ssl : bool = True ,
426464 debug : bool = False ,
427465) -> webdriver .Chrome :
428466 print ("html2pdf4doc: Creating ChromeDriver service." , flush = True ) # noqa: T201
429467
430468 path_to_chrome_driver : str
431469 if chromedriver_argument is None :
432470 path_to_chrome_driver = chrome_driver_manager .get_chrome_driver (
433- path_to_cache_dir
471+ path_to_cache_dir , verify_ssl = verify_ssl
434472 )
435473 else :
436474 path_to_chrome_driver = chromedriver_argument
@@ -545,6 +583,14 @@ def _main() -> None:
545583 type = str ,
546584 help = "Optional path to a cache directory whereto the ChromeDriver is downloaded." ,
547585 )
586+ command_parser_get_driver .add_argument (
587+ "--disable-ssl-check" ,
588+ action = "store_true" ,
589+ help = (
590+ "Disables SSL certificate verification for HTTP downloads. "
591+ "By default SSL certificate verification is enabled."
592+ ),
593+ )
548594
549595 #
550596 # Print command.
@@ -564,6 +610,14 @@ def _main() -> None:
564610 type = str ,
565611 help = "Optional path to a cache directory whereto the ChromeDriver is downloaded." ,
566612 )
613+ command_parser_print .add_argument (
614+ "--disable-ssl-check" ,
615+ action = "store_true" ,
616+ help = (
617+ "Disables SSL certificate verification for HTTP downloads. "
618+ "By default SSL certificate verification is enabled."
619+ ),
620+ )
567621 command_parser_print .add_argument (
568622 "--page-load-timeout" ,
569623 # 60 minutes should be enough to print even the largest documents.
@@ -620,7 +674,8 @@ def _main() -> None:
620674 )
621675
622676 path_to_chrome = chrome_driver_manager .get_chrome_driver (
623- path_to_cache_dir
677+ path_to_cache_dir ,
678+ verify_ssl = not args .disable_ssl_check ,
624679 )
625680 print (f"html2pdf4doc: ChromeDriver available at path: { path_to_chrome } " ) # noqa: T201
626681 sys .exit (0 )
@@ -638,6 +693,7 @@ def _main() -> None:
638693 args .chromedriver ,
639694 path_to_cache_dir ,
640695 page_load_timeout ,
696+ verify_ssl = not args .disable_ssl_check ,
641697 debug = args .debug ,
642698 )
643699
0 commit comments