1515import time
1616import operator as op
1717import datetime
18+ import numpy as np
1819
1920from typing import Any , Callable , Iterable , Optional , Union
2021from types import MappingProxyType
@@ -96,16 +97,16 @@ def check_is_latent(item) -> bool:
9697_BUILTIN_FN_PREFIX = '__rgthreefn.'
9798
9899
99- def _get_built_in_fn_key (fn : Function ):
100+ def _get_built_in_fn_key (fn : Function ) -> str :
100101 """Returns a key for a built-in function."""
101102 return f'{ _BUILTIN_FN_PREFIX } { hash (fn .name )} '
102103
103104
104- def _get_built_in_fn_by_key (key : str ):
105+ def _get_built_in_fn_by_key (fn_key : str ):
105106 """Returns the `Function` for the provided key (purposefully, not name)."""
106- if not key .startswith (_BUILTIN_FN_PREFIX ) or key not in _BUILT_INS_BY_NAME_AND_KEY :
107+ if not fn_key .startswith (_BUILTIN_FN_PREFIX ) or fn_key not in _BUILT_INS_BY_NAME_AND_KEY :
107108 raise ValueError ('No built in function found.' )
108- return _BUILT_INS_BY_NAME_AND_KEY [key ]
109+ return _BUILT_INS_BY_NAME_AND_KEY [fn_key ]
109110
110111
111112_BUILT_IN_FNS_LIST = [
@@ -158,6 +159,17 @@ def _get_built_in_fn_by_key(key: str):
158159 }
159160)
160161
162+ # A dict of types to blocked attributes/methods. Used to disallow file system access or other
163+ # invocations we may want to block. Necessary for any instance type that is possible to create from
164+ # the code or standard ComfyUI inputs.
165+ #
166+ # For instance, a user does not have access to the numpy module directly, so they cannot invoke
167+ # `numpy.save`. However, a user can access a numpy.ndarray instance from a tensor and, from there,
168+ # an attempt to call `tofile` or `dump` etc. would need to be blocked.
169+ _BLOCKED_METHODS_OR_ATTRS = MappingProxyType ({
170+ np .ndarray : ['tofile' , 'dump' ]
171+ })
172+
161173# Special functions by class type (called from the Attrs.)
162174_SPECIAL_FUNCTIONS = {
163175 RgthreePowerLoraLoader .NAME : {
@@ -241,6 +253,7 @@ def INPUT_TYPES(cls): # pylint: disable = invalid-name, missing-function-docstr
241253 "unique_id" : "UNIQUE_ID" ,
242254 "extra_pnginfo" : "EXTRA_PNGINFO" ,
243255 "prompt" : "PROMPT" ,
256+ "dynprompt" : "DYNPROMPT"
244257 },
245258 }
246259
@@ -299,6 +312,7 @@ def main(self, **kwargs):
299312 pnginfo = kwargs ['extra_pnginfo' ]
300313 workflow = pnginfo ["workflow" ] if "workflow" in pnginfo else {"nodes" : []}
301314 prompt = kwargs ['prompt' ]
315+ dynprompt = kwargs ['dynprompt' ]
302316
303317 outputs = get_dict_value (kwargs , 'outputs.outputs' , None )
304318 if not outputs :
@@ -314,7 +328,14 @@ def main(self, **kwargs):
314328
315329 code = _update_code (kwargs ['code' ], unique_id = kwargs ['unique_id' ], log = True )
316330
317- eva = _Puter (code = code , ctx = ctx , workflow = workflow , prompt = prompt , unique_id = unique_id )
331+ eva = _Puter (
332+ code = code ,
333+ ctx = ctx ,
334+ workflow = workflow ,
335+ prompt = prompt ,
336+ dynprompt = dynprompt ,
337+ unique_id = unique_id
338+ )
318339 values = eva .execute ()
319340
320341 # Check if we have multiple outputs that the returned value is a tuple and raise if not.
@@ -376,12 +397,13 @@ class _Puter:
376397 See https://www.basicexamples.com/example/python/ast for examples.
377398 """
378399
379- def __init__ (self , * , code : str , ctx : dict [str , Any ], workflow , prompt , unique_id ):
400+ def __init__ (self , * , code : str , ctx : dict [str , Any ], workflow , prompt , dynprompt , unique_id ):
380401 ctx = ctx or {}
381402 self ._ctx = {** ctx }
382403 self ._code = code
383404 self ._workflow = workflow
384405 self ._prompt = prompt
406+ self ._dynprompt = dynprompt
385407 self ._prompt_nodes = []
386408 if self ._prompt :
387409 self ._prompt_nodes = [{'id' : id } | {** node } for id , node in self ._prompt .items ()]
@@ -488,6 +510,12 @@ def _eval_statement(self, stmt: ast.AST, ctx: dict, prev_stmt: Union[ast.AST, No
488510 else :
489511 # Slice could be a name or a constant; evaluate it
490512 attr = self ._eval_statement (stmt .slice , ctx = ctx )
513+ if not isinstance (attr , str ):
514+ raise ValueError (f'Disallowed access to "{ attr } "' )
515+ # Check if we're blocking access to this attribute/method on this item type.
516+ for typ , names in _BLOCKED_METHODS_OR_ATTRS .items ():
517+ if isinstance (item , typ ) and attr in names :
518+ raise ValueError (f'Disallowed access to "{ attr } " for type { typ } .' )
491519 try :
492520 val = item [attr ]
493521 except (TypeError , IndexError , KeyError ):
@@ -704,6 +732,10 @@ def handle_gen(generators: list[ast.comprehension]):
704732 return 1 if l <= r else 0
705733 if isinstance (stmt .ops [0 ], ast .In ):
706734 return 1 if l in r else 0
735+ if isinstance (stmt .ops [0 ], ast .Is ):
736+ return 1 if l is r else 0
737+ if isinstance (stmt .ops [0 ], ast .IsNot ):
738+ return 1 if l is not r else 0
707739 raise NotImplementedError ("Operator " + stmt .ops [0 ].__class__ .__name__ + " not supported." )
708740
709741 if isinstance (stmt , (ast .If , ast .IfExp )):
0 commit comments