flock-core 0.3.4__py3-none-any.whl → 0.3.6__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.
Potentially problematic release.
This version of flock-core might be problematic. Click here for more details.
- flock/core/context/context.py +1 -1
- flock/core/serialization/secure_serializer.py +175 -0
- {flock_core-0.3.4.dist-info → flock_core-0.3.6.dist-info}/METADATA +2 -1
- {flock_core-0.3.4.dist-info → flock_core-0.3.6.dist-info}/RECORD +8 -7
- /flock/core/{util → serialization}/serializable.py +0 -0
- {flock_core-0.3.4.dist-info → flock_core-0.3.6.dist-info}/WHEEL +0 -0
- {flock_core-0.3.4.dist-info → flock_core-0.3.6.dist-info}/entry_points.txt +0 -0
- {flock_core-0.3.4.dist-info → flock_core-0.3.6.dist-info}/licenses/LICENSE +0 -0
flock/core/context/context.py
CHANGED
|
@@ -7,7 +7,7 @@ from opentelemetry import trace
|
|
|
7
7
|
|
|
8
8
|
from flock.core.context.context_vars import FLOCK_LAST_AGENT, FLOCK_LAST_RESULT
|
|
9
9
|
from flock.core.logging.logging import get_logger
|
|
10
|
-
from flock.core.
|
|
10
|
+
from flock.core.serialization.serializable import Serializable
|
|
11
11
|
|
|
12
12
|
logger = get_logger("context")
|
|
13
13
|
tracer = trace.get_tracer(__name__)
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import cloudpickle
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SecureSerializer:
|
|
5
|
+
"""Security-focused serialization system with capability controls for Flock objects."""
|
|
6
|
+
|
|
7
|
+
# Define capability levels for different modules
|
|
8
|
+
MODULE_CAPABILITIES = {
|
|
9
|
+
# Core Python - unrestricted
|
|
10
|
+
"builtins": "unrestricted",
|
|
11
|
+
"datetime": "unrestricted",
|
|
12
|
+
"re": "unrestricted",
|
|
13
|
+
"math": "unrestricted",
|
|
14
|
+
"json": "unrestricted",
|
|
15
|
+
# Framework modules - unrestricted
|
|
16
|
+
"flock": "unrestricted",
|
|
17
|
+
# System modules - restricted but allowed
|
|
18
|
+
"os": "restricted",
|
|
19
|
+
"io": "restricted",
|
|
20
|
+
"sys": "restricted",
|
|
21
|
+
"subprocess": "high_risk",
|
|
22
|
+
# Network modules - high risk
|
|
23
|
+
"socket": "high_risk",
|
|
24
|
+
"requests": "high_risk",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Functions that should never be serialized
|
|
28
|
+
BLOCKED_FUNCTIONS = {
|
|
29
|
+
"os.system",
|
|
30
|
+
"os.popen",
|
|
31
|
+
"os.spawn",
|
|
32
|
+
"os.exec",
|
|
33
|
+
"subprocess.call",
|
|
34
|
+
"subprocess.run",
|
|
35
|
+
"subprocess.Popen",
|
|
36
|
+
"eval",
|
|
37
|
+
"exec",
|
|
38
|
+
"__import__",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def _get_module_capability(module_name):
|
|
43
|
+
"""Get the capability level for a module."""
|
|
44
|
+
for prefix, level in SecureSerializer.MODULE_CAPABILITIES.items():
|
|
45
|
+
if module_name == prefix or module_name.startswith(f"{prefix}."):
|
|
46
|
+
return level
|
|
47
|
+
return "unknown" # Default to unknown for unlisted modules
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def _is_safe_callable(obj):
|
|
51
|
+
"""Check if a callable is safe to serialize."""
|
|
52
|
+
if not callable(obj) or isinstance(obj, type):
|
|
53
|
+
return True, "Not a callable function"
|
|
54
|
+
|
|
55
|
+
module = obj.__module__
|
|
56
|
+
func_name = (
|
|
57
|
+
f"{module}.{obj.__name__}"
|
|
58
|
+
if hasattr(obj, "__name__")
|
|
59
|
+
else "unknown"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Check against blocked functions
|
|
63
|
+
if func_name in SecureSerializer.BLOCKED_FUNCTIONS:
|
|
64
|
+
return False, f"Function {func_name} is explicitly blocked"
|
|
65
|
+
|
|
66
|
+
# Check module capability level
|
|
67
|
+
capability = SecureSerializer._get_module_capability(module)
|
|
68
|
+
if capability == "unknown":
|
|
69
|
+
return False, f"Module {module} has unknown security capability"
|
|
70
|
+
|
|
71
|
+
return True, capability
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def serialize(obj, allow_restricted=True, allow_high_risk=False):
|
|
75
|
+
"""Serialize an object with capability checks."""
|
|
76
|
+
if callable(obj) and not isinstance(obj, type):
|
|
77
|
+
is_safe, capability = SecureSerializer._is_safe_callable(obj)
|
|
78
|
+
|
|
79
|
+
if not is_safe:
|
|
80
|
+
raise ValueError(
|
|
81
|
+
f"Cannot serialize unsafe callable: {capability}"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if capability == "high_risk" and not allow_high_risk:
|
|
85
|
+
raise ValueError(
|
|
86
|
+
f"High risk callable {obj.__module__}.{obj.__name__} requires explicit permission"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
if capability == "restricted" and not allow_restricted:
|
|
90
|
+
raise ValueError(
|
|
91
|
+
f"Restricted callable {obj.__module__}.{obj.__name__} requires explicit permission"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Store metadata about the callable for verification during deserialization
|
|
95
|
+
metadata = {
|
|
96
|
+
"module": obj.__module__,
|
|
97
|
+
"name": getattr(obj, "__name__", "unknown"),
|
|
98
|
+
"capability": capability,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
"__serialized_callable__": True,
|
|
103
|
+
"data": cloudpickle.dumps(obj).hex(),
|
|
104
|
+
"metadata": metadata,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if isinstance(obj, list):
|
|
108
|
+
return [
|
|
109
|
+
SecureSerializer.serialize(
|
|
110
|
+
item, allow_restricted, allow_high_risk
|
|
111
|
+
)
|
|
112
|
+
for item in obj
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
if isinstance(obj, dict):
|
|
116
|
+
return {
|
|
117
|
+
k: SecureSerializer.serialize(
|
|
118
|
+
v, allow_restricted, allow_high_risk
|
|
119
|
+
)
|
|
120
|
+
for k, v in obj.items()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return obj
|
|
124
|
+
|
|
125
|
+
@staticmethod
|
|
126
|
+
def deserialize(obj, allow_restricted=True, allow_high_risk=False):
|
|
127
|
+
"""Deserialize an object with capability enforcement."""
|
|
128
|
+
if isinstance(obj, dict) and obj.get("__serialized_callable__") is True:
|
|
129
|
+
# Validate the capability level during deserialization
|
|
130
|
+
metadata = obj.get("metadata", {})
|
|
131
|
+
capability = metadata.get("capability", "unknown")
|
|
132
|
+
|
|
133
|
+
if capability == "high_risk" and not allow_high_risk:
|
|
134
|
+
raise ValueError(
|
|
135
|
+
f"Cannot deserialize high risk callable {metadata.get('module')}.{metadata.get('name')}"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
if capability == "restricted" and not allow_restricted:
|
|
139
|
+
raise ValueError(
|
|
140
|
+
f"Cannot deserialize restricted callable {metadata.get('module')}.{metadata.get('name')}"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
callable_obj = cloudpickle.loads(bytes.fromhex(obj["data"]))
|
|
145
|
+
|
|
146
|
+
# Additional verification that the deserialized object matches its metadata
|
|
147
|
+
if callable_obj.__module__ != metadata.get("module") or (
|
|
148
|
+
hasattr(callable_obj, "__name__")
|
|
149
|
+
and callable_obj.__name__ != metadata.get("name")
|
|
150
|
+
):
|
|
151
|
+
raise ValueError(
|
|
152
|
+
"Callable metadata mismatch - possible tampering detected"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
return callable_obj
|
|
156
|
+
except Exception as e:
|
|
157
|
+
raise ValueError(f"Failed to deserialize callable: {e!s}")
|
|
158
|
+
|
|
159
|
+
if isinstance(obj, list):
|
|
160
|
+
return [
|
|
161
|
+
SecureSerializer.deserialize(
|
|
162
|
+
item, allow_restricted, allow_high_risk
|
|
163
|
+
)
|
|
164
|
+
for item in obj
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
if isinstance(obj, dict) and "__serialized_callable__" not in obj:
|
|
168
|
+
return {
|
|
169
|
+
k: SecureSerializer.deserialize(
|
|
170
|
+
v, allow_restricted, allow_high_risk
|
|
171
|
+
)
|
|
172
|
+
for k, v in obj.items()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return obj
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flock-core
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: Declarative LLM Orchestration at Scale
|
|
5
5
|
Author-email: Andre Ratzenberger <andre.ratzenberger@whiteduck.de>
|
|
6
6
|
License-File: LICENSE
|
|
@@ -35,6 +35,7 @@ Requires-Dist: temporalio>=1.9.0
|
|
|
35
35
|
Requires-Dist: tiktoken>=0.8.0
|
|
36
36
|
Requires-Dist: toml>=0.10.2
|
|
37
37
|
Requires-Dist: tqdm>=4.67.1
|
|
38
|
+
Requires-Dist: uvicorn>=0.34.0
|
|
38
39
|
Requires-Dist: zep-python>=2.0.2
|
|
39
40
|
Provides-Extra: all-tools
|
|
40
41
|
Requires-Dist: docling>=2.18.0; extra == 'all-tools'
|
|
@@ -16,7 +16,7 @@ flock/core/flock_api.py,sha256=SKQVKgFCaNCqHtwvIcksnpqG6ajHodVhs3oaKUw-d8c,7192
|
|
|
16
16
|
flock/core/flock_evaluator.py,sha256=j7riJj_KsWoBnKmLiGp-U0CRhxDyJbgEdLGN26tfKm8,1588
|
|
17
17
|
flock/core/flock_factory.py,sha256=vyDq0eyFT4MyE_n2JyNU7YaFx2ljmjSDmZ07OIsmIOE,2694
|
|
18
18
|
flock/core/flock_module.py,sha256=VWFlBiY2RHZLTlGYfcchuT41M3m_JrZcmzw07u7KayM,2581
|
|
19
|
-
flock/core/context/context.py,sha256=
|
|
19
|
+
flock/core/context/context.py,sha256=AW0qKIAkgZucVroGsulrPVPc4WmWuqWIrVPHf2qaOLI,6380
|
|
20
20
|
flock/core/context/context_manager.py,sha256=qMySVny_dbTNLh21RHK_YT0mNKIOrqJDZpi9ZVdBsxU,1103
|
|
21
21
|
flock/core/context/context_vars.py,sha256=0Hn6fM2iNc0_jIIU0B7KX-K2o8qXqtZ5EYtwujETQ7U,272
|
|
22
22
|
flock/core/execution/local_executor.py,sha256=rnIQvaJOs6zZORUcR3vvyS6LPREDJTjaygl_Db0M8ao,952
|
|
@@ -37,12 +37,13 @@ flock/core/logging/telemetry_exporter/sqlite_exporter.py,sha256=CDsiMb9QcqeXelZ6
|
|
|
37
37
|
flock/core/mixin/dspy_integration.py,sha256=P5G4Y04nl5hFwFbJXCkQ-0TMR1L4skLL2IM_FlUjH_c,8364
|
|
38
38
|
flock/core/mixin/prompt_parser.py,sha256=eOqI-FK3y17gVqpc_y5GF-WmK1Jv8mFlkZxTcgweoxI,5121
|
|
39
39
|
flock/core/registry/agent_registry.py,sha256=TUClh9e3eA6YzZC1CMTlsTPvQeqb9jYHewi-zPpcWM8,4987
|
|
40
|
+
flock/core/serialization/secure_serializer.py,sha256=n5-zRvvXddgJv1FFHsaQ2wuYdL3WUSGPvG_LGaffEJo,6144
|
|
41
|
+
flock/core/serialization/serializable.py,sha256=SymJ0YrjBx48mOBItYSqoRpKuzIc4vKWRS6ScTzre7s,2573
|
|
40
42
|
flock/core/tools/basic_tools.py,sha256=fI9r81_ktRiRhNLwT-jSJ9rkjl28LC1ZfL-njnno2iw,4761
|
|
41
43
|
flock/core/tools/dev_tools/github.py,sha256=a2OTPXS7kWOVA4zrZHynQDcsmEi4Pac5MfSjQOLePzA,5308
|
|
42
44
|
flock/core/util/cli_helper.py,sha256=IOl9r4cz_MJv_Bp5R8dhHX8f-unAqA9vDS6-0E90Vzk,49813
|
|
43
45
|
flock/core/util/hydrator.py,sha256=6qNwOwCZB7r6y25BZ--0PGofrAlfMaXbDKFQeP5NLts,11196
|
|
44
46
|
flock/core/util/input_resolver.py,sha256=g9vDPdY4OH-G7qjas5ksGEHueokHGFPMoLOvC-ngeLo,5984
|
|
45
|
-
flock/core/util/serializable.py,sha256=SymJ0YrjBx48mOBItYSqoRpKuzIc4vKWRS6ScTzre7s,2573
|
|
46
47
|
flock/evaluators/declarative/declarative_evaluator.py,sha256=f8ldgZZp94zC4CoGzBufKvbvtckCGBe9EHTOoAZfZK0,1695
|
|
47
48
|
flock/evaluators/natural_language/natural_language_evaluator.py,sha256=6nVEeh8_uwv_h-d3FWlA0GbzDzRtdhvxCGKirHtyvOU,2012
|
|
48
49
|
flock/evaluators/zep/zep_evaluator.py,sha256=hEHQdgIwGsbC4ci9RvtdA2k7f4M0yznIok4v4XltNwg,1885
|
|
@@ -396,8 +397,8 @@ flock/workflow/activities.py,sha256=2zcYyDoCuYs9oQbnhLjCzBUdEi7d5IEIemKJ7TV_B8w,
|
|
|
396
397
|
flock/workflow/agent_activities.py,sha256=NhBZscflEf2IMfSRa_pBM_TRP7uVEF_O0ROvWZ33eDc,963
|
|
397
398
|
flock/workflow/temporal_setup.py,sha256=VWBgmBgfTBjwM5ruS_dVpA5AVxx6EZ7oFPGw4j3m0l0,1091
|
|
398
399
|
flock/workflow/workflow.py,sha256=I9MryXW_bqYVTHx-nl2epbTqeRy27CAWHHA7ZZA0nAk,1696
|
|
399
|
-
flock_core-0.3.
|
|
400
|
-
flock_core-0.3.
|
|
401
|
-
flock_core-0.3.
|
|
402
|
-
flock_core-0.3.
|
|
403
|
-
flock_core-0.3.
|
|
400
|
+
flock_core-0.3.6.dist-info/METADATA,sha256=-TPh-D8HF2et5uEyksY7uwhl9Fgc0b_RRf0PMe1B2hg,20494
|
|
401
|
+
flock_core-0.3.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
402
|
+
flock_core-0.3.6.dist-info/entry_points.txt,sha256=rWaS5KSpkTmWySURGFZk6PhbJ87TmvcFQDi2uzjlagQ,37
|
|
403
|
+
flock_core-0.3.6.dist-info/licenses/LICENSE,sha256=iYEqWy0wjULzM9GAERaybP4LBiPeu7Z1NEliLUdJKSc,1072
|
|
404
|
+
flock_core-0.3.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|