comfy-env 0.0.64__py3-none-any.whl → 0.0.66__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- comfy_env/__init__.py +70 -122
- comfy_env/cli.py +78 -7
- comfy_env/config/__init__.py +19 -0
- comfy_env/config/parser.py +151 -0
- comfy_env/config/types.py +64 -0
- comfy_env/install.py +83 -361
- comfy_env/isolation/__init__.py +9 -0
- comfy_env/isolation/wrap.py +351 -0
- comfy_env/nodes.py +2 -2
- comfy_env/pixi/__init__.py +48 -0
- comfy_env/pixi/core.py +356 -0
- comfy_env/{resolver.py → pixi/resolver.py} +1 -14
- comfy_env/prestartup.py +60 -0
- comfy_env/templates/comfy-env-instructions.txt +30 -87
- comfy_env/templates/comfy-env.toml +68 -136
- comfy_env/workers/__init__.py +21 -32
- comfy_env/workers/base.py +1 -1
- comfy_env/workers/{torch_mp.py → mp.py} +47 -14
- comfy_env/workers/{venv.py → subprocess.py} +405 -441
- {comfy_env-0.0.64.dist-info → comfy_env-0.0.66.dist-info}/METADATA +2 -1
- comfy_env-0.0.66.dist-info/RECORD +34 -0
- comfy_env/decorator.py +0 -700
- comfy_env/env/__init__.py +0 -47
- comfy_env/env/config.py +0 -201
- comfy_env/env/config_file.py +0 -740
- comfy_env/env/manager.py +0 -636
- comfy_env/env/security.py +0 -267
- comfy_env/ipc/__init__.py +0 -55
- comfy_env/ipc/bridge.py +0 -476
- comfy_env/ipc/protocol.py +0 -265
- comfy_env/ipc/tensor.py +0 -371
- comfy_env/ipc/torch_bridge.py +0 -401
- comfy_env/ipc/transport.py +0 -318
- comfy_env/ipc/worker.py +0 -221
- comfy_env/isolation.py +0 -310
- comfy_env/pixi.py +0 -760
- comfy_env/stub_imports.py +0 -270
- comfy_env/stubs/__init__.py +0 -1
- comfy_env/stubs/comfy/__init__.py +0 -6
- comfy_env/stubs/comfy/model_management.py +0 -58
- comfy_env/stubs/comfy/utils.py +0 -29
- comfy_env/stubs/folder_paths.py +0 -71
- comfy_env/workers/pool.py +0 -241
- comfy_env-0.0.64.dist-info/RECORD +0 -48
- /comfy_env/{env/cuda_gpu_detection.py → pixi/cuda_detection.py} +0 -0
- /comfy_env/{env → pixi}/platform/__init__.py +0 -0
- /comfy_env/{env → pixi}/platform/base.py +0 -0
- /comfy_env/{env → pixi}/platform/darwin.py +0 -0
- /comfy_env/{env → pixi}/platform/linux.py +0 -0
- /comfy_env/{env → pixi}/platform/windows.py +0 -0
- /comfy_env/{registry.py → pixi/registry.py} +0 -0
- /comfy_env/{wheel_sources.yml → pixi/wheel_sources.yml} +0 -0
- {comfy_env-0.0.64.dist-info → comfy_env-0.0.66.dist-info}/WHEEL +0 -0
- {comfy_env-0.0.64.dist-info → comfy_env-0.0.66.dist-info}/entry_points.txt +0 -0
- {comfy_env-0.0.64.dist-info → comfy_env-0.0.66.dist-info}/licenses/LICENSE +0 -0
comfy_env/env/security.py
DELETED
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Security validation utilities for comfyui-isolation.
|
|
3
|
-
|
|
4
|
-
Provides functions to validate and sanitize user inputs before they are
|
|
5
|
-
used in filesystem operations or shell commands. This prevents common
|
|
6
|
-
security vulnerabilities like directory traversal and command injection.
|
|
7
|
-
|
|
8
|
-
Ported from Comfy-Org/pyisolate with modifications.
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
import re
|
|
12
|
-
from pathlib import Path
|
|
13
|
-
from typing import List, Optional
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def normalize_env_name(name: str) -> str:
|
|
17
|
-
"""
|
|
18
|
-
Normalize an environment name to be safe for use in filesystem paths.
|
|
19
|
-
|
|
20
|
-
This function:
|
|
21
|
-
- Replaces spaces and unsafe characters with underscores
|
|
22
|
-
- Removes directory traversal attempts (../, ./, etc.)
|
|
23
|
-
- Ensures the name is not empty
|
|
24
|
-
- Preserves Unicode characters (for non-English names)
|
|
25
|
-
|
|
26
|
-
Args:
|
|
27
|
-
name: The original environment name
|
|
28
|
-
|
|
29
|
-
Returns:
|
|
30
|
-
A normalized, filesystem-safe version of the name
|
|
31
|
-
|
|
32
|
-
Raises:
|
|
33
|
-
ValueError: If the name is empty or only contains invalid characters
|
|
34
|
-
|
|
35
|
-
Examples:
|
|
36
|
-
>>> normalize_env_name("my-node")
|
|
37
|
-
'my-node'
|
|
38
|
-
>>> normalize_env_name("../../../etc/passwd")
|
|
39
|
-
'etc_passwd'
|
|
40
|
-
>>> normalize_env_name("node; rm -rf /")
|
|
41
|
-
'node_rm_-rf'
|
|
42
|
-
"""
|
|
43
|
-
if not name:
|
|
44
|
-
raise ValueError("Environment name cannot be empty")
|
|
45
|
-
|
|
46
|
-
# Remove any directory traversal attempts or absolute path indicators
|
|
47
|
-
# Replace path separators with underscores
|
|
48
|
-
name = name.replace("/", "_").replace("\\", "_")
|
|
49
|
-
|
|
50
|
-
# Remove leading dots to prevent hidden files
|
|
51
|
-
while name.startswith("."):
|
|
52
|
-
name = name[1:]
|
|
53
|
-
|
|
54
|
-
# Replace consecutive dots that are part of directory traversal
|
|
55
|
-
name = name.replace("..", "_")
|
|
56
|
-
|
|
57
|
-
# Replace problematic characters with underscores
|
|
58
|
-
# This includes spaces, shell metacharacters, and control characters
|
|
59
|
-
# But preserves Unicode letters, numbers, and some safe punctuation
|
|
60
|
-
unsafe_chars = [
|
|
61
|
-
" ", # Spaces
|
|
62
|
-
"\t", # Tabs
|
|
63
|
-
"\n", # Newlines
|
|
64
|
-
"\r", # Carriage returns
|
|
65
|
-
";", # Command separator
|
|
66
|
-
"|", # Pipe
|
|
67
|
-
"&", # Background/and
|
|
68
|
-
"$", # Variable expansion
|
|
69
|
-
"`", # Command substitution
|
|
70
|
-
"(", # Subshell
|
|
71
|
-
")", # Subshell
|
|
72
|
-
"<", # Redirect
|
|
73
|
-
">", # Redirect
|
|
74
|
-
'"', # Quote
|
|
75
|
-
"'", # Quote
|
|
76
|
-
"!", # History expansion
|
|
77
|
-
"{", # Brace expansion
|
|
78
|
-
"}", # Brace expansion
|
|
79
|
-
"[", # Glob
|
|
80
|
-
"]", # Glob
|
|
81
|
-
"*", # Glob
|
|
82
|
-
"?", # Glob
|
|
83
|
-
"~", # Home directory
|
|
84
|
-
"#", # Comment
|
|
85
|
-
"%", # Job control
|
|
86
|
-
"=", # Assignment
|
|
87
|
-
":", # Path separator
|
|
88
|
-
",", # Various uses
|
|
89
|
-
"\0", # Null byte
|
|
90
|
-
]
|
|
91
|
-
|
|
92
|
-
for char in unsafe_chars:
|
|
93
|
-
name = name.replace(char, "_")
|
|
94
|
-
|
|
95
|
-
# Replace multiple consecutive underscores with a single underscore
|
|
96
|
-
name = re.sub(r"_+", "_", name)
|
|
97
|
-
|
|
98
|
-
# Remove leading and trailing underscores
|
|
99
|
-
name = name.strip("_")
|
|
100
|
-
|
|
101
|
-
# If the name is now empty (was all invalid chars), raise an error
|
|
102
|
-
if not name:
|
|
103
|
-
raise ValueError("Environment name contains only invalid characters")
|
|
104
|
-
|
|
105
|
-
return name
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def validate_dependency(dep: str) -> None:
|
|
109
|
-
"""
|
|
110
|
-
Validate a single pip dependency specification is safe.
|
|
111
|
-
|
|
112
|
-
Checks for command injection patterns that could be exploited
|
|
113
|
-
when the dependency string is passed to pip.
|
|
114
|
-
|
|
115
|
-
Args:
|
|
116
|
-
dep: A pip dependency string (e.g., "torch>=2.0.0", "numpy==1.24.0")
|
|
117
|
-
|
|
118
|
-
Raises:
|
|
119
|
-
ValueError: If the dependency contains potentially dangerous patterns
|
|
120
|
-
|
|
121
|
-
Examples:
|
|
122
|
-
>>> validate_dependency("torch>=2.0.0") # OK
|
|
123
|
-
>>> validate_dependency("numpy==1.24.0") # OK
|
|
124
|
-
>>> validate_dependency("package && rm -rf /") # Raises ValueError
|
|
125
|
-
"""
|
|
126
|
-
if not dep:
|
|
127
|
-
return
|
|
128
|
-
|
|
129
|
-
# Strip whitespace
|
|
130
|
-
dep = dep.strip()
|
|
131
|
-
if not dep:
|
|
132
|
-
return
|
|
133
|
-
|
|
134
|
-
# Skip comments
|
|
135
|
-
if dep.startswith("#"):
|
|
136
|
-
return
|
|
137
|
-
|
|
138
|
-
# Special case: allow "-e" for editable installs followed by a path
|
|
139
|
-
if dep == "-e":
|
|
140
|
-
# This is OK, it should be followed by a path in the next argument
|
|
141
|
-
return
|
|
142
|
-
|
|
143
|
-
# Allow editable installs with path
|
|
144
|
-
if dep.startswith("-e "):
|
|
145
|
-
# Validate the path part
|
|
146
|
-
path_part = dep[3:].strip()
|
|
147
|
-
if path_part:
|
|
148
|
-
# Check for dangerous patterns in the path
|
|
149
|
-
_check_dangerous_patterns(path_part, dep)
|
|
150
|
-
return
|
|
151
|
-
|
|
152
|
-
# Check if it looks like a command-line option (but allow -e)
|
|
153
|
-
if dep.startswith("-") and not dep.startswith("-e"):
|
|
154
|
-
raise ValueError(
|
|
155
|
-
f"Invalid dependency '{dep}'. "
|
|
156
|
-
"Dependencies cannot start with '-' as this could be a command option."
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
# Check for dangerous patterns
|
|
160
|
-
_check_dangerous_patterns(dep, dep)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
def _check_dangerous_patterns(text: str, original: str) -> None:
|
|
164
|
-
"""Check for command injection patterns in text."""
|
|
165
|
-
dangerous_patterns = [
|
|
166
|
-
"&&", # Command chaining
|
|
167
|
-
"||", # Command chaining
|
|
168
|
-
";", # Command separator
|
|
169
|
-
"|", # Pipe
|
|
170
|
-
"`", # Command substitution
|
|
171
|
-
"$(", # Command substitution
|
|
172
|
-
"${", # Variable expansion
|
|
173
|
-
"\n", # Newline (command separator)
|
|
174
|
-
"\r", # Carriage return
|
|
175
|
-
"\0", # Null byte
|
|
176
|
-
]
|
|
177
|
-
|
|
178
|
-
for pattern in dangerous_patterns:
|
|
179
|
-
if pattern in text:
|
|
180
|
-
raise ValueError(
|
|
181
|
-
f"Invalid dependency '{original}'. "
|
|
182
|
-
f"Contains potentially dangerous pattern: '{pattern}'"
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
def validate_dependencies(deps: List[str]) -> None:
|
|
187
|
-
"""
|
|
188
|
-
Validate a list of pip dependency specifications.
|
|
189
|
-
|
|
190
|
-
Args:
|
|
191
|
-
deps: List of pip dependency strings
|
|
192
|
-
|
|
193
|
-
Raises:
|
|
194
|
-
ValueError: If any dependency contains dangerous patterns
|
|
195
|
-
"""
|
|
196
|
-
for dep in deps:
|
|
197
|
-
validate_dependency(dep)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
def validate_path_within_root(path: Path, root: Path) -> None:
|
|
201
|
-
"""
|
|
202
|
-
Ensure a path is contained within the expected root directory.
|
|
203
|
-
|
|
204
|
-
This prevents directory traversal attacks where a malicious path
|
|
205
|
-
could escape the intended directory.
|
|
206
|
-
|
|
207
|
-
Args:
|
|
208
|
-
path: The path to validate
|
|
209
|
-
root: The root directory that should contain the path
|
|
210
|
-
|
|
211
|
-
Raises:
|
|
212
|
-
ValueError: If the path escapes the root directory
|
|
213
|
-
|
|
214
|
-
Examples:
|
|
215
|
-
>>> validate_path_within_root(Path("/app/envs/my-node"), Path("/app/envs")) # OK
|
|
216
|
-
>>> validate_path_within_root(Path("/app/envs/../../../etc"), Path("/app/envs")) # Raises
|
|
217
|
-
"""
|
|
218
|
-
try:
|
|
219
|
-
# Resolve both paths to absolute paths (follows symlinks)
|
|
220
|
-
resolved_path = path.resolve()
|
|
221
|
-
resolved_root = root.resolve()
|
|
222
|
-
|
|
223
|
-
# Check if the path is within the root
|
|
224
|
-
resolved_path.relative_to(resolved_root)
|
|
225
|
-
except ValueError as err:
|
|
226
|
-
raise ValueError(
|
|
227
|
-
f"Path '{path}' is not within the expected root directory '{root}'"
|
|
228
|
-
) from err
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
def validate_wheel_url(url: str) -> None:
|
|
232
|
-
"""
|
|
233
|
-
Validate a wheel source URL is safe.
|
|
234
|
-
|
|
235
|
-
Args:
|
|
236
|
-
url: The URL to validate
|
|
237
|
-
|
|
238
|
-
Raises:
|
|
239
|
-
ValueError: If the URL contains dangerous patterns
|
|
240
|
-
"""
|
|
241
|
-
if not url:
|
|
242
|
-
return
|
|
243
|
-
|
|
244
|
-
# Check for dangerous patterns
|
|
245
|
-
dangerous_patterns = [
|
|
246
|
-
";", # Command separator
|
|
247
|
-
"&&", # Command chaining
|
|
248
|
-
"`", # Command substitution
|
|
249
|
-
"$(", # Command substitution
|
|
250
|
-
"\n", # Newline
|
|
251
|
-
"\r", # Carriage return
|
|
252
|
-
"\0", # Null byte
|
|
253
|
-
]
|
|
254
|
-
|
|
255
|
-
for pattern in dangerous_patterns:
|
|
256
|
-
if pattern in url:
|
|
257
|
-
raise ValueError(
|
|
258
|
-
f"Invalid wheel URL '{url}'. "
|
|
259
|
-
f"Contains potentially dangerous pattern: '{pattern}'"
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
# Must start with http:// or https://
|
|
263
|
-
if not url.startswith(("http://", "https://", "file://")):
|
|
264
|
-
raise ValueError(
|
|
265
|
-
f"Invalid wheel URL '{url}'. "
|
|
266
|
-
"URL must start with http://, https://, or file://"
|
|
267
|
-
)
|
comfy_env/ipc/__init__.py
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
"""IPC (Inter-Process Communication) for comfyui-isolation."""
|
|
2
|
-
|
|
3
|
-
from .bridge import WorkerBridge
|
|
4
|
-
from .worker import BaseWorker, register
|
|
5
|
-
from .protocol import (
|
|
6
|
-
Request,
|
|
7
|
-
Response,
|
|
8
|
-
encode_object,
|
|
9
|
-
decode_object,
|
|
10
|
-
set_tensor_ipc_enabled,
|
|
11
|
-
get_tensor_ipc_enabled,
|
|
12
|
-
)
|
|
13
|
-
from .transport import (
|
|
14
|
-
Transport,
|
|
15
|
-
QueueTransport,
|
|
16
|
-
create_queue_pair,
|
|
17
|
-
UnixSocketTransport,
|
|
18
|
-
StdioTransport,
|
|
19
|
-
get_socket_path,
|
|
20
|
-
cleanup_socket,
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
# TorchBridge is optional (requires PyTorch)
|
|
24
|
-
try:
|
|
25
|
-
from .torch_bridge import TorchBridge, TorchWorker
|
|
26
|
-
_TORCH_EXPORTS = ["TorchBridge", "TorchWorker"]
|
|
27
|
-
except ImportError:
|
|
28
|
-
_TORCH_EXPORTS = []
|
|
29
|
-
|
|
30
|
-
# Tensor IPC is optional (requires PyTorch)
|
|
31
|
-
try:
|
|
32
|
-
from .tensor import serialize_tensor, deserialize_tensor, TensorKeeper
|
|
33
|
-
_TENSOR_EXPORTS = ["serialize_tensor", "deserialize_tensor", "TensorKeeper"]
|
|
34
|
-
except ImportError:
|
|
35
|
-
_TENSOR_EXPORTS = []
|
|
36
|
-
|
|
37
|
-
__all__ = [
|
|
38
|
-
"WorkerBridge",
|
|
39
|
-
"BaseWorker",
|
|
40
|
-
"register",
|
|
41
|
-
"Request",
|
|
42
|
-
"Response",
|
|
43
|
-
"encode_object",
|
|
44
|
-
"decode_object",
|
|
45
|
-
"set_tensor_ipc_enabled",
|
|
46
|
-
"get_tensor_ipc_enabled",
|
|
47
|
-
# Transport
|
|
48
|
-
"Transport",
|
|
49
|
-
"QueueTransport",
|
|
50
|
-
"create_queue_pair",
|
|
51
|
-
"UnixSocketTransport",
|
|
52
|
-
"StdioTransport",
|
|
53
|
-
"get_socket_path",
|
|
54
|
-
"cleanup_socket",
|
|
55
|
-
] + _TORCH_EXPORTS + _TENSOR_EXPORTS
|