Skip to content

Commit e9d5618

Browse files
committed
Add and operators to Power PuPuter. Also, restrict filesystem save methods from numpy.ndarray.
1 parent 42e73c3 commit e9d5618

File tree

2 files changed

+39
-7
lines changed

2 files changed

+39
-7
lines changed

py/power_puter.py

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import time
1616
import operator as op
1717
import datetime
18+
import numpy as np
1819

1920
from typing import Any, Callable, Iterable, Optional, Union
2021
from 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)):

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
name = "rgthree-comfy"
33
description = "Making ComfyUI more comfortable."
4-
version = "1.0.2511270846"
4+
version = "1.0.2512060053"
55
license = { file = "LICENSE" }
66
dependencies = []
77

0 commit comments

Comments
 (0)