8
8
from _string import formatter_field_name_split # type: ignore
9
9
from collections import abc
10
10
from collections import deque
11
+ from functools import update_wrapper
11
12
from string import Formatter
12
13
13
14
from markupsafe import EscapeFormatter
83
84
)
84
85
85
86
86
- def inspect_format_method (callable : t .Callable [..., t .Any ]) -> t .Optional [str ]:
87
- if not isinstance (
88
- callable , (types .MethodType , types .BuiltinMethodType )
89
- ) or callable .__name__ not in ("format" , "format_map" ):
90
- return None
91
-
92
- obj = callable .__self__
93
-
94
- if isinstance (obj , str ):
95
- return obj
96
-
97
- return None
98
-
99
-
100
87
def safe_range (* args : int ) -> range :
101
88
"""A range that can't generate ranges with a length of more than
102
89
MAX_RANGE items.
@@ -316,6 +303,9 @@ def getitem(
316
303
except AttributeError :
317
304
pass
318
305
else :
306
+ fmt = self .wrap_str_format (value )
307
+ if fmt is not None :
308
+ return fmt
319
309
if self .is_safe_attribute (obj , argument , value ):
320
310
return value
321
311
return self .unsafe_undefined (obj , argument )
@@ -333,6 +323,9 @@ def getattr(self, obj: t.Any, attribute: str) -> t.Union[t.Any, Undefined]:
333
323
except (TypeError , LookupError ):
334
324
pass
335
325
else :
326
+ fmt = self .wrap_str_format (value )
327
+ if fmt is not None :
328
+ return fmt
336
329
if self .is_safe_attribute (obj , attribute , value ):
337
330
return value
338
331
return self .unsafe_undefined (obj , attribute )
@@ -348,34 +341,49 @@ def unsafe_undefined(self, obj: t.Any, attribute: str) -> Undefined:
348
341
exc = SecurityError ,
349
342
)
350
343
351
- def format_string (
352
- self ,
353
- s : str ,
354
- args : t .Tuple [t .Any , ...],
355
- kwargs : t .Dict [str , t .Any ],
356
- format_func : t .Optional [t .Callable [..., t .Any ]] = None ,
357
- ) -> str :
358
- """If a format call is detected, then this is routed through this
359
- method so that our safety sandbox can be used for it.
344
+ def wrap_str_format (self , value : t .Any ) -> t .Optional [t .Callable [..., str ]]:
345
+ """If the given value is a ``str.format`` or ``str.format_map`` method,
346
+ return a new function than handles sandboxing. This is done at access
347
+ rather than in :meth:`call`, so that calls made without ``call`` are
348
+ also sandboxed.
360
349
"""
350
+ if not isinstance (
351
+ value , (types .MethodType , types .BuiltinMethodType )
352
+ ) or value .__name__ not in ("format" , "format_map" ):
353
+ return None
354
+
355
+ f_self : t .Any = value .__self__
356
+
357
+ if not isinstance (f_self , str ):
358
+ return None
359
+
360
+ str_type : t .Type [str ] = type (f_self )
361
+ is_format_map = value .__name__ == "format_map"
361
362
formatter : SandboxedFormatter
362
- if isinstance (s , Markup ):
363
- formatter = SandboxedEscapeFormatter (self , escape = s .escape )
363
+
364
+ if isinstance (f_self , Markup ):
365
+ formatter = SandboxedEscapeFormatter (self , escape = f_self .escape )
364
366
else :
365
367
formatter = SandboxedFormatter (self )
366
368
367
- if format_func is not None and format_func .__name__ == "format_map" :
368
- if len (args ) != 1 or kwargs :
369
- raise TypeError (
370
- "format_map() takes exactly one argument"
371
- f" { len (args ) + (kwargs is not None )} given"
372
- )
369
+ vformat = formatter .vformat
370
+
371
+ def wrapper (* args : t .Any , ** kwargs : t .Any ) -> str :
372
+ if is_format_map :
373
+ if kwargs :
374
+ raise TypeError ("format_map() takes no keyword arguments" )
375
+
376
+ if len (args ) != 1 :
377
+ raise TypeError (
378
+ f"format_map() takes exactly one argument ({ len (args )} given)"
379
+ )
380
+
381
+ kwargs = args [0 ]
382
+ args = ()
373
383
374
- kwargs = args [0 ]
375
- args = ()
384
+ return str_type (vformat (f_self , args , kwargs ))
376
385
377
- rv = formatter .vformat (s , args , kwargs )
378
- return type (s )(rv )
386
+ return update_wrapper (wrapper , value )
379
387
380
388
def call (
381
389
__self , # noqa: B902
@@ -385,9 +393,6 @@ def call(
385
393
** kwargs : t .Any ,
386
394
) -> t .Any :
387
395
"""Call an object from sandboxed code."""
388
- fmt = inspect_format_method (__obj )
389
- if fmt is not None :
390
- return __self .format_string (fmt , args , kwargs , __obj )
391
396
392
397
# the double prefixes are to avoid double keyword argument
393
398
# errors when proxying the call.
0 commit comments