14
14
15
15
from readthedocs .doc_builder .exceptions import (
16
16
FileIsNotRegularFile ,
17
+ FileTooLarge ,
17
18
SymlinkOutsideBasePath ,
18
19
UnsupportedSymlinkFileError ,
19
20
)
20
21
21
22
log = structlog .get_logger (__name__ )
22
23
24
+ MAX_FILE_SIZE_BYTES = 1024 * 1024 * 1024 * 1 # 1 GB
23
25
24
26
def _assert_path_is_inside_docroot (path ):
25
27
resolved_path = path .absolute ().resolve ()
@@ -32,13 +34,21 @@ def _assert_path_is_inside_docroot(path):
32
34
raise SuspiciousFileOperation (path )
33
35
34
36
35
- def safe_open (path , * args , allow_symlinks = False , base_path = None , ** kwargs ):
37
+ def safe_open (
38
+ path ,
39
+ * args ,
40
+ allow_symlinks = False ,
41
+ base_path = None ,
42
+ max_size_bytes = MAX_FILE_SIZE_BYTES ,
43
+ ** kwargs
44
+ ):
36
45
"""
37
46
Wrapper around path.open() to check for symlinks.
38
47
39
48
- Checks for symlinks to avoid symlink following vulnerabilities
40
49
like GHSA-368m-86q9-m99w.
41
50
- Checks that files aren't out of the DOCROOT directory.
51
+ - Checks that files aren't too large.
42
52
43
53
:param allow_symlinks: If `False` and the path is a symlink, raise `FileIsSymlink`
44
54
This prevents reading the contents of other files users shouldn't have access to.
@@ -47,6 +57,7 @@ def safe_open(path, *args, allow_symlinks=False, base_path=None, **kwargs):
47
57
(usually the directory where the project was cloned).
48
58
It must be given if `allow_symlinks` is set to `True`.
49
59
This prevents path traversal attacks (even when using symlinks).
60
+ :param max_size_bytes: Maximum file size allowed in bytes when reading a file.
50
61
51
62
The extra *args and **kwargs will be passed to the open() method.
52
63
@@ -56,7 +67,7 @@ def safe_open(path, *args, allow_symlinks=False, base_path=None, **kwargs):
56
67
Users shouldn't be able to change files while this operation is done.
57
68
"""
58
69
if allow_symlinks and not base_path :
59
- raise ValueError ("base_path mut be given if symlinks are allowed." )
70
+ raise ValueError ("base_path must be given if symlinks are allowed." )
60
71
61
72
path = Path (path ).absolute ()
62
73
@@ -74,6 +85,12 @@ def safe_open(path, *args, allow_symlinks=False, base_path=None, **kwargs):
74
85
# Expand symlinks.
75
86
resolved_path = path .resolve ()
76
87
88
+ if resolved_path .exists ():
89
+ file_size = resolved_path .stat ().st_size
90
+ if file_size > max_size_bytes :
91
+ log .info ("File is too large." , size_bytes = file_size )
92
+ raise FileTooLarge (path )
93
+
77
94
if allow_symlinks and base_path :
78
95
base_path = Path (base_path ).absolute ()
79
96
if not resolved_path .is_relative_to (base_path ):
0 commit comments